Skip to main content

vm/compiler/
codegen.rs

1use std::collections::{BTreeSet, HashMap, HashSet};
2
3use crate::assembler::Assembler;
4use crate::builtins::BuiltinFunction;
5use crate::{Program, TypeMap, Value, ValueType};
6
7use super::ir::{
8    ClosureExpr, Expr, FunctionDecl, FunctionImpl, LocalSlot, MatchPattern, MatchTypePattern, Stmt,
9    StructDecl, TypeSchema,
10};
11use super::{CompileError, TypingMode, typing};
12
13pub struct Compiler {
14    assembler: Assembler,
15    next_label_id: u32,
16    loop_stack: Vec<LoopContext>,
17    function_impls: HashMap<u16, FunctionImpl>,
18    function_decls: HashMap<u16, FunctionDecl>,
19    struct_schemas: HashMap<String, StructDecl>,
20    host_import_return_types: HashMap<u16, typing::BoundType>,
21    host_import_signatures: HashMap<u16, typing::HostCallableSignature>,
22    call_index_remap: HashMap<u16, u16>,
23    inline_call_stack: Vec<u16>,
24    callable_bindings: HashMap<LocalSlot, CallableBinding>,
25    enable_local_move_semantics: bool,
26    typing_mode: TypingMode,
27    type_state: typing::LocalTypeState,
28    type_map: TypeMap,
29}
30
31struct LoopContext {
32    continue_label: String,
33    break_label: String,
34}
35
36#[derive(Clone)]
37enum CallableBinding {
38    Closure(ClosureExpr),
39    Function(u16),
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
43enum CaptureBindingMode {
44    Copy,
45    Borrow,
46    BorrowMut,
47    Move,
48}
49
50impl Default for Compiler {
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56impl Compiler {
57    pub fn new() -> Self {
58        Self {
59            assembler: Assembler::new(),
60            next_label_id: 0,
61            loop_stack: Vec::new(),
62            function_impls: HashMap::new(),
63            function_decls: HashMap::new(),
64            struct_schemas: HashMap::new(),
65            host_import_return_types: HashMap::new(),
66            host_import_signatures: HashMap::new(),
67            call_index_remap: HashMap::new(),
68            inline_call_stack: Vec::new(),
69            callable_bindings: HashMap::new(),
70            enable_local_move_semantics: false,
71            typing_mode: TypingMode::DynamicHints,
72            type_state: typing::LocalTypeState::default(),
73            type_map: TypeMap::default(),
74        }
75    }
76
77    pub fn set_source(&mut self, source: String) {
78        self.assembler.set_source(source);
79    }
80
81    pub fn add_function_debug(&mut self, func: &FunctionDecl) {
82        self.assembler
83            .add_function(func.name.clone(), func.args.clone());
84    }
85
86    pub fn add_local_debug(
87        &mut self,
88        name: String,
89        index: LocalSlot,
90        declared_line: Option<u32>,
91        last_line: Option<u32>,
92    ) -> Result<(), CompileError> {
93        self.assembler.add_local_with_range(
94            name,
95            local_slot_operand(index)?,
96            declared_line,
97            last_line,
98        );
99        Ok(())
100    }
101
102    pub fn set_function_impls(&mut self, function_impls: HashMap<u16, FunctionImpl>) {
103        self.function_impls = function_impls;
104    }
105
106    pub fn set_function_decls(&mut self, function_decls: HashMap<u16, FunctionDecl>) {
107        self.function_decls = function_decls;
108    }
109
110    pub fn set_struct_schemas(&mut self, struct_schemas: HashMap<String, StructDecl>) {
111        self.struct_schemas = struct_schemas;
112    }
113
114    pub(crate) fn set_host_import_return_types(
115        &mut self,
116        host_import_return_types: HashMap<u16, typing::BoundType>,
117    ) {
118        self.host_import_return_types = host_import_return_types;
119    }
120
121    pub(crate) fn set_host_import_signatures(
122        &mut self,
123        host_import_signatures: HashMap<u16, typing::HostCallableSignature>,
124    ) {
125        self.host_import_signatures = host_import_signatures;
126    }
127
128    pub fn set_call_index_remap(&mut self, call_index_remap: HashMap<u16, u16>) {
129        self.call_index_remap = call_index_remap;
130    }
131
132    pub fn set_enable_local_move_semantics(&mut self, enable_local_move_semantics: bool) {
133        self.enable_local_move_semantics = enable_local_move_semantics;
134    }
135
136    pub(crate) fn set_typing_mode(&mut self, typing_mode: TypingMode) {
137        self.typing_mode = typing_mode;
138    }
139
140    pub(crate) fn set_type_inference(&mut self, type_info: typing::TypeInferenceResult) {
141        self.type_map.local_types = type_info.local_types;
142        self.type_map.local_schemas = type_info.local_schemas;
143        self.type_map.callable_slots = type_info.callable_slots;
144        self.type_map.optional_slots = type_info.optional_slots;
145    }
146
147    pub fn compile_program(mut self, stmts: &[Stmt]) -> Result<Program, CompileError> {
148        self.compile_stmts(stmts)?;
149        self.assembler.ret();
150        let mut program = self
151            .assembler
152            .finish_program()
153            .map_err(CompileError::Assembler)?;
154        self.type_map.strict_types = self.typing_mode.is_strict();
155        program.type_map = Some(self.type_map);
156        Ok(program)
157    }
158
159    fn compile_stmts(&mut self, stmts: &[Stmt]) -> Result<(), CompileError> {
160        for stmt in stmts {
161            self.compile_stmt(stmt)?;
162        }
163        Ok(())
164    }
165
166    fn compile_stmt(&mut self, stmt: &Stmt) -> Result<(), CompileError> {
167        match stmt {
168            Stmt::Noop { line } => {
169                self.assembler.mark_line(*line);
170            }
171            Stmt::Let {
172                index,
173                declared_schema,
174                expr,
175                line,
176            } => {
177                self.assembler.mark_line(*line);
178                self.assign_expr_to_slot(*index, declared_schema.as_ref(), expr)?;
179            }
180            Stmt::Assign {
181                index, expr, line, ..
182            } => {
183                self.assembler.mark_line(*line);
184                self.assign_expr_to_slot(*index, None, expr)?;
185            }
186            Stmt::ClosureLet { line, closure } => {
187                self.assembler.mark_line(*line);
188                self.bind_closure_captures(closure)?;
189            }
190            Stmt::FuncDecl {
191                index,
192                has_impl,
193                line,
194                ..
195            } => {
196                self.assembler.mark_line(*line);
197                if *has_impl {
198                    self.bind_function_decl_captures(*index)?;
199                }
200            }
201            Stmt::Expr { expr, line } => {
202                self.assembler.mark_line(*line);
203                self.compile_expr(expr)?;
204            }
205            Stmt::IfElse {
206                condition,
207                then_branch,
208                else_branch,
209                line,
210            } => {
211                self.assembler.mark_line(*line);
212                if matches!(condition, Expr::Bool(true)) {
213                    self.compile_stmts(then_branch)?;
214                    return Ok(());
215                }
216                if matches!(condition, Expr::Bool(false)) {
217                    self.compile_stmts(else_branch)?;
218                    return Ok(());
219                }
220                let callable_snapshot = self.callable_bindings.clone();
221                let type_state_snapshot = self.type_state.clone();
222                let then_refined_type_state =
223                    typing::refine_state_for_condition(&type_state_snapshot, condition, true);
224                let else_refined_type_state =
225                    typing::refine_state_for_condition(&type_state_snapshot, condition, false);
226                let else_label = self.fresh_label("else");
227                let end_label = self.fresh_label("endif");
228                self.compile_scalar_expr(condition)?;
229                self.assembler.brfalse_label(&else_label);
230                self.type_state = then_refined_type_state;
231                self.compile_stmts(then_branch)?;
232                let then_type_state = self.type_state.clone();
233                self.assembler.br_label(&end_label);
234                self.assembler
235                    .label(&else_label)
236                    .map_err(CompileError::Assembler)?;
237                self.callable_bindings = callable_snapshot.clone();
238                self.type_state = else_refined_type_state;
239                self.compile_stmts(else_branch)?;
240                let else_type_state = self.type_state.clone();
241                self.assembler
242                    .label(&end_label)
243                    .map_err(CompileError::Assembler)?;
244                self.callable_bindings = callable_snapshot;
245                self.type_state
246                    .merge_from_branches(&then_type_state, &else_type_state);
247            }
248            Stmt::For {
249                init,
250                condition,
251                post,
252                body,
253                line,
254            } => {
255                let callable_snapshot = self.callable_bindings.clone();
256                self.assembler.mark_line(*line);
257                self.compile_stmt(init)?;
258                let loop_entry_type_state = self.type_state.clone();
259                let stabilized_loop_type_state =
260                    self.stabilize_loop_type_state(&loop_entry_type_state, |iterated| {
261                        let _ = typing::infer_expr_type_with_function_impls_and_imports(
262                            condition,
263                            iterated,
264                            &self.function_impls,
265                            &self.function_decls,
266                            &self.struct_schemas,
267                            &self.host_import_return_types,
268                            &self.host_import_signatures,
269                        );
270                        typing::apply_stmts_with_function_impls_and_imports(
271                            body,
272                            iterated,
273                            &self.function_impls,
274                            &self.function_decls,
275                            &self.struct_schemas,
276                            &self.host_import_return_types,
277                            &self.host_import_signatures,
278                        );
279                        typing::apply_stmts_with_function_impls_and_imports(
280                            std::slice::from_ref(post),
281                            iterated,
282                            &self.function_impls,
283                            &self.function_decls,
284                            &self.struct_schemas,
285                            &self.host_import_return_types,
286                            &self.host_import_signatures,
287                        );
288                    });
289                self.type_state = stabilized_loop_type_state;
290                let start_label = self.fresh_label("for_start");
291                let continue_label = self.fresh_label("for_continue");
292                let end_label = self.fresh_label("for_end");
293                self.assembler
294                    .label(&start_label)
295                    .map_err(CompileError::Assembler)?;
296                self.compile_scalar_expr(condition)?;
297                self.assembler.brfalse_label(&end_label);
298                self.loop_stack.push(LoopContext {
299                    continue_label: continue_label.clone(),
300                    break_label: end_label.clone(),
301                });
302                self.compile_stmts(body)?;
303                self.loop_stack.pop();
304                self.assembler
305                    .label(&continue_label)
306                    .map_err(CompileError::Assembler)?;
307                self.compile_stmt(post)?;
308                self.assembler.br_label(&start_label);
309                self.assembler
310                    .label(&end_label)
311                    .map_err(CompileError::Assembler)?;
312                self.callable_bindings = callable_snapshot;
313                self.type_state = self
314                    .simulate_stmt_type_state(std::slice::from_ref(stmt), &loop_entry_type_state);
315            }
316            Stmt::While {
317                condition,
318                body,
319                line,
320            } => {
321                let callable_snapshot = self.callable_bindings.clone();
322                let loop_entry_type_state = self.type_state.clone();
323                let stabilized_loop_type_state =
324                    self.stabilize_loop_type_state(&loop_entry_type_state, |iterated| {
325                        let _ = typing::infer_expr_type_with_function_impls_and_imports(
326                            condition,
327                            iterated,
328                            &self.function_impls,
329                            &self.function_decls,
330                            &self.struct_schemas,
331                            &self.host_import_return_types,
332                            &self.host_import_signatures,
333                        );
334                        typing::apply_stmts_with_function_impls_and_imports(
335                            body,
336                            iterated,
337                            &self.function_impls,
338                            &self.function_decls,
339                            &self.struct_schemas,
340                            &self.host_import_return_types,
341                            &self.host_import_signatures,
342                        );
343                    });
344                self.assembler.mark_line(*line);
345                self.type_state = stabilized_loop_type_state;
346                let start_label = self.fresh_label("while_start");
347                let end_label = self.fresh_label("while_end");
348                self.assembler
349                    .label(&start_label)
350                    .map_err(CompileError::Assembler)?;
351                self.compile_scalar_expr(condition)?;
352                self.assembler.brfalse_label(&end_label);
353                self.loop_stack.push(LoopContext {
354                    continue_label: start_label.clone(),
355                    break_label: end_label.clone(),
356                });
357                self.compile_stmts(body)?;
358                self.loop_stack.pop();
359                self.assembler.br_label(&start_label);
360                self.assembler
361                    .label(&end_label)
362                    .map_err(CompileError::Assembler)?;
363                self.callable_bindings = callable_snapshot;
364                self.type_state = self
365                    .simulate_stmt_type_state(std::slice::from_ref(stmt), &loop_entry_type_state);
366            }
367            Stmt::Break { line } => {
368                self.assembler.mark_line(*line);
369                let loop_ctx = self
370                    .loop_stack
371                    .last()
372                    .ok_or(CompileError::BreakOutsideLoop)?;
373                self.assembler.br_label(&loop_ctx.break_label);
374            }
375            Stmt::Continue { line } => {
376                self.assembler.mark_line(*line);
377                let loop_ctx = self
378                    .loop_stack
379                    .last()
380                    .ok_or(CompileError::ContinueOutsideLoop)?;
381                self.assembler.br_label(&loop_ctx.continue_label);
382            }
383            Stmt::Drop { index, line } => {
384                self.assembler.mark_line(*line);
385                self.assign_expr_to_slot(*index, None, &Expr::Null)?;
386            }
387        }
388        Ok(())
389    }
390
391    fn compile_expr(&mut self, expr: &Expr) -> Result<(), CompileError> {
392        match expr {
393            Expr::Null => {
394                self.assembler.push_const(Value::Null);
395            }
396            Expr::Int(value) => {
397                self.assembler.push_const(Value::Int(*value));
398            }
399            Expr::Float(value) => {
400                self.assembler.push_const(Value::Float(*value));
401            }
402            Expr::Bool(value) => {
403                self.assembler.push_const(Value::Bool(*value));
404            }
405            Expr::String(value) => {
406                self.assembler.push_const(Value::string(value.clone()));
407            }
408            Expr::Bytes(value) => {
409                self.assembler.push_const(Value::bytes(value.clone()));
410            }
411            Expr::OptionalGet {
412                container,
413                key,
414                container_slot,
415                key_slot,
416            } => {
417                self.compile_optional_get_expr(container, key, *container_slot, *key_slot)?;
418            }
419            Expr::OptionUnwrapOr {
420                value,
421                value_slot,
422                fallback,
423            } => {
424                self.compile_option_unwrap_or_expr(value, *value_slot, fallback)?;
425            }
426            Expr::FunctionRef(_) => {
427                return Err(CompileError::CallableUsedAsValue);
428            }
429            Expr::Call(index, _, args) => {
430                self.compile_function_call(*index, args)?;
431            }
432            Expr::Closure(_) => {
433                return Err(CompileError::CallableUsedAsValue);
434            }
435            Expr::ClosureCall(closure, args) => {
436                self.compile_inline_closure_call(closure, args)?;
437            }
438            Expr::LocalCall(index, _, args) => {
439                let callable = self
440                    .callable_bindings
441                    .get(index)
442                    .cloned()
443                    .ok_or(CompileError::NonCallableLocal(*index))?;
444                self.compile_callable_call(callable, args)?;
445            }
446            Expr::Add(lhs, rhs) => {
447                let lhs_ty = self.value_type_of_expr(lhs);
448                let rhs_ty = self.value_type_of_expr(rhs);
449                if is_definitely_string_expr(lhs) {
450                    self.compile_scalar_expr(lhs)?;
451                    self.compile_string_concat_operand(rhs)?;
452                    self.record_operand_types(ValueType::String, ValueType::String);
453                    self.assembler.add();
454                    return Ok(());
455                }
456                if is_definitely_string_expr(rhs) {
457                    self.compile_string_concat_operand(lhs)?;
458                    self.compile_scalar_expr(rhs)?;
459                    self.record_operand_types(ValueType::String, ValueType::String);
460                    self.assembler.add();
461                    return Ok(());
462                }
463                self.compile_scalar_expr(lhs)?;
464                self.compile_scalar_expr(rhs)?;
465                self.record_operand_types(lhs_ty, rhs_ty);
466                self.assembler.add();
467            }
468            Expr::Sub(lhs, rhs) => {
469                let lhs_ty = self.value_type_of_expr(lhs);
470                let rhs_ty = self.value_type_of_expr(rhs);
471                self.compile_scalar_expr(lhs)?;
472                self.compile_scalar_expr(rhs)?;
473                self.record_operand_types(lhs_ty, rhs_ty);
474                self.assembler.sub();
475            }
476            Expr::Mul(lhs, rhs) => {
477                if let Expr::Int(value) = rhs.as_ref()
478                    && let Some(shift) = shift_amount_for_power_of_two(*value)
479                {
480                    self.compile_scalar_expr(lhs)?;
481                    self.assembler.push_const(Value::Int(shift as i64));
482                    self.assembler.shl();
483                } else if let Expr::Int(value) = lhs.as_ref()
484                    && let Some(shift) = shift_amount_for_power_of_two(*value)
485                {
486                    self.compile_scalar_expr(rhs)?;
487                    self.assembler.push_const(Value::Int(shift as i64));
488                    self.assembler.shl();
489                } else {
490                    let lhs_ty = self.value_type_of_expr(lhs);
491                    let rhs_ty = self.value_type_of_expr(rhs);
492                    self.compile_scalar_expr(lhs)?;
493                    self.compile_scalar_expr(rhs)?;
494                    self.record_operand_types(lhs_ty, rhs_ty);
495                    self.assembler.mul();
496                }
497            }
498            Expr::Div(lhs, rhs) => {
499                let lhs_ty = self.value_type_of_expr(lhs);
500                let rhs_ty = self.value_type_of_expr(rhs);
501                self.compile_scalar_expr(lhs)?;
502                self.compile_scalar_expr(rhs)?;
503                self.record_operand_types(lhs_ty, rhs_ty);
504                self.assembler.div();
505            }
506            Expr::Mod(lhs, rhs) => {
507                let lhs_ty = self.value_type_of_expr(lhs);
508                let rhs_ty = self.value_type_of_expr(rhs);
509                self.compile_scalar_expr(lhs)?;
510                self.compile_scalar_expr(rhs)?;
511                self.record_operand_types(lhs_ty, rhs_ty);
512                self.assembler.modulo();
513            }
514            Expr::Neg(inner) => {
515                let inner_ty = self.value_type_of_expr(inner);
516                self.compile_scalar_expr(inner)?;
517                self.record_unary_operand_type(inner_ty);
518                self.assembler.neg();
519            }
520            Expr::Not(inner) => {
521                self.compile_scalar_expr(inner)?;
522                self.assembler.not();
523            }
524            Expr::ToOwned(inner) => {
525                self.compile_scalar_expr(inner)?;
526            }
527            Expr::Borrow(inner) | Expr::BorrowMut(inner) => {
528                self.compile_scalar_expr(inner)?;
529            }
530            Expr::And(lhs, rhs) => {
531                self.compile_short_circuit_and(lhs, rhs)?;
532            }
533            Expr::Or(lhs, rhs) => {
534                self.compile_short_circuit_or(lhs, rhs)?;
535            }
536            Expr::Eq(lhs, rhs) => {
537                let lhs_ty = self.value_type_of_expr(lhs);
538                let rhs_ty = self.value_type_of_expr(rhs);
539                self.compile_scalar_expr(lhs)?;
540                self.compile_scalar_expr(rhs)?;
541                self.record_operand_types(lhs_ty, rhs_ty);
542                self.assembler.ceq();
543            }
544            Expr::Lt(lhs, rhs) => {
545                let lhs_ty = self.value_type_of_expr(lhs);
546                let rhs_ty = self.value_type_of_expr(rhs);
547                self.compile_scalar_expr(lhs)?;
548                self.compile_scalar_expr(rhs)?;
549                self.record_operand_types(lhs_ty, rhs_ty);
550                self.assembler.clt();
551            }
552            Expr::Gt(lhs, rhs) => {
553                let lhs_ty = self.value_type_of_expr(lhs);
554                let rhs_ty = self.value_type_of_expr(rhs);
555                self.compile_scalar_expr(lhs)?;
556                self.compile_scalar_expr(rhs)?;
557                self.record_operand_types(lhs_ty, rhs_ty);
558                self.assembler.cgt();
559            }
560            Expr::Var(index) => {
561                if self.callable_bindings.contains_key(index) {
562                    return Err(CompileError::CallableUsedAsValue);
563                }
564                self.emit_copy_ldloc(*index)?;
565            }
566            Expr::MoveVar(index) => {
567                if self.callable_bindings.contains_key(index) {
568                    return Err(CompileError::CallableUsedAsValue);
569                }
570                self.emit_move_ldloc(*index)?;
571                self.type_state.set(*index, typing::BoundType::Null);
572            }
573            Expr::MoveField { root, key } => {
574                self.emit_copy_ldloc(*root)?;
575                self.assembler.push_const(Value::string(key.clone()));
576                self.assembler.call(BuiltinFunction::Get.call_index(), 2);
577
578                self.emit_copy_ldloc(*root)?;
579                self.assembler.push_const(Value::string(key.clone()));
580                self.assembler.push_const(Value::Null);
581                self.assembler.call(BuiltinFunction::Set.call_index(), 3);
582                self.emit_stloc(*root)?;
583            }
584            Expr::MoveIndex { root, index } => {
585                self.emit_copy_ldloc(*root)?;
586                self.assembler.push_const(Value::Int(*index));
587                self.assembler.call(BuiltinFunction::Get.call_index(), 2);
588
589                self.emit_copy_ldloc(*root)?;
590                self.assembler.push_const(Value::Int(*index));
591                self.assembler.push_const(Value::Null);
592                self.assembler.call(BuiltinFunction::Set.call_index(), 3);
593                self.emit_stloc(*root)?;
594            }
595            Expr::IfElse {
596                condition,
597                then_expr,
598                else_expr,
599            } => {
600                let callable_snapshot = self.callable_bindings.clone();
601                let type_state_snapshot = self.type_state.clone();
602                self.compile_scalar_expr(condition)?;
603                let else_label = self.fresh_label("if_else");
604                let end_label = self.fresh_label("if_end");
605                self.assembler.brfalse_label(&else_label);
606                self.type_state =
607                    typing::refine_state_for_condition(&type_state_snapshot, condition, true);
608                self.compile_expr(then_expr)?;
609                let then_type_state = self.type_state.clone();
610                self.assembler.br_label(&end_label);
611                self.assembler
612                    .label(&else_label)
613                    .map_err(CompileError::Assembler)?;
614                self.callable_bindings = callable_snapshot.clone();
615                self.type_state =
616                    typing::refine_state_for_condition(&type_state_snapshot, condition, false);
617                self.compile_expr(else_expr)?;
618                let else_type_state = self.type_state.clone();
619                self.assembler
620                    .label(&end_label)
621                    .map_err(CompileError::Assembler)?;
622                self.callable_bindings = callable_snapshot;
623                self.type_state
624                    .merge_from_branches(&then_type_state, &else_type_state);
625            }
626            Expr::Match {
627                value_slot,
628                result_slot,
629                value,
630                arms,
631                default,
632            } => {
633                self.compile_scalar_expr(value)?;
634                self.emit_stloc(*value_slot)?;
635                let callable_snapshot = self.callable_bindings.clone();
636                let match_entry_type_state = self.type_state.clone();
637                let end_label = self.fresh_label("match_end");
638                let mut merged_type_state: Option<typing::LocalTypeState> = None;
639                for (pattern, arm_expr) in arms {
640                    let next_label = self.fresh_label("match_next");
641                    self.callable_bindings = callable_snapshot.clone();
642                    self.type_state = match_entry_type_state.clone();
643                    self.compile_match_pattern_condition(*value_slot, pattern)?;
644                    self.assembler.brfalse_label(&next_label);
645                    self.bind_match_pattern_slot(
646                        pattern,
647                        value,
648                        *value_slot,
649                        &match_entry_type_state,
650                    )?;
651                    self.compile_scalar_expr(arm_expr)?;
652                    self.emit_stloc(*result_slot)?;
653                    let arm_type_state = self.type_state.clone();
654                    merged_type_state = Some(match merged_type_state {
655                        Some(existing) => {
656                            let mut merged = typing::LocalTypeState::default();
657                            merged.merge_from_branches(&existing, &arm_type_state);
658                            merged
659                        }
660                        None => arm_type_state,
661                    });
662                    self.assembler.br_label(&end_label);
663                    self.assembler
664                        .label(&next_label)
665                        .map_err(CompileError::Assembler)?;
666                }
667                self.callable_bindings = callable_snapshot.clone();
668                self.type_state = match_entry_type_state.clone();
669                self.compile_scalar_expr(default)?;
670                self.emit_stloc(*result_slot)?;
671                let default_type_state = self.type_state.clone();
672                self.assembler
673                    .label(&end_label)
674                    .map_err(CompileError::Assembler)?;
675                self.callable_bindings = callable_snapshot;
676                self.type_state = if let Some(existing) = merged_type_state {
677                    let mut merged = typing::LocalTypeState::default();
678                    merged.merge_from_branches(&existing, &default_type_state);
679                    merged
680                } else {
681                    default_type_state
682                };
683                self.emit_copy_ldloc(*result_slot)?;
684            }
685            Expr::Block { stmts, expr } => {
686                self.compile_stmts(stmts)?;
687                self.compile_expr(expr)?;
688            }
689        }
690        Ok(())
691    }
692
693    fn compile_optional_get_expr(
694        &mut self,
695        container: &Expr,
696        key: &Expr,
697        container_slot: LocalSlot,
698        key_slot: LocalSlot,
699    ) -> Result<(), CompileError> {
700        self.compile_scalar_expr(container)?;
701        self.emit_stloc(container_slot)?;
702        self.compile_scalar_expr(key)?;
703        self.emit_stloc(key_slot)?;
704
705        let map_lookup = Expr::IfElse {
706            condition: Box::new(Expr::Call(
707                BuiltinFunction::Has.call_index(),
708                Vec::new(),
709                vec![Expr::Var(container_slot), Expr::Var(key_slot)],
710            )),
711            then_expr: Box::new(Expr::Call(
712                BuiltinFunction::Get.call_index(),
713                Vec::new(),
714                vec![Expr::Var(container_slot), Expr::Var(key_slot)],
715            )),
716            else_expr: Box::new(Expr::Null),
717        };
718        let index_lookup = Expr::IfElse {
719            condition: Box::new(Expr::Eq(
720                Box::new(Expr::Call(
721                    BuiltinFunction::TypeOf.call_index(),
722                    Vec::new(),
723                    vec![Expr::Var(key_slot)],
724                )),
725                Box::new(Expr::String("int".to_string())),
726            )),
727            then_expr: Box::new(Expr::IfElse {
728                condition: Box::new(Expr::Lt(
729                    Box::new(Expr::Var(key_slot)),
730                    Box::new(Expr::Int(0)),
731                )),
732                then_expr: Box::new(Expr::Null),
733                else_expr: Box::new(Expr::IfElse {
734                    condition: Box::new(Expr::Lt(
735                        Box::new(Expr::Var(key_slot)),
736                        Box::new(Expr::Call(
737                            BuiltinFunction::Len.call_index(),
738                            Vec::new(),
739                            vec![Expr::Var(container_slot)],
740                        )),
741                    )),
742                    then_expr: Box::new(Expr::Call(
743                        BuiltinFunction::Get.call_index(),
744                        Vec::new(),
745                        vec![Expr::Var(container_slot), Expr::Var(key_slot)],
746                    )),
747                    else_expr: Box::new(Expr::Null),
748                }),
749            }),
750            else_expr: Box::new(Expr::Null),
751        };
752        let lowered = Expr::IfElse {
753            condition: Box::new(Expr::Eq(
754                Box::new(Expr::Call(
755                    BuiltinFunction::TypeOf.call_index(),
756                    Vec::new(),
757                    vec![Expr::Var(container_slot)],
758                )),
759                Box::new(Expr::String("null".to_string())),
760            )),
761            then_expr: Box::new(Expr::Null),
762            else_expr: Box::new(Expr::IfElse {
763                condition: Box::new(Expr::Eq(
764                    Box::new(Expr::Call(
765                        BuiltinFunction::TypeOf.call_index(),
766                        Vec::new(),
767                        vec![Expr::Var(container_slot)],
768                    )),
769                    Box::new(Expr::String("map".to_string())),
770                )),
771                then_expr: Box::new(map_lookup),
772                else_expr: Box::new(Expr::IfElse {
773                    condition: Box::new(Expr::Eq(
774                        Box::new(Expr::Call(
775                            BuiltinFunction::TypeOf.call_index(),
776                            Vec::new(),
777                            vec![Expr::Var(container_slot)],
778                        )),
779                        Box::new(Expr::String("array".to_string())),
780                    )),
781                    then_expr: Box::new(index_lookup.clone()),
782                    else_expr: Box::new(Expr::IfElse {
783                        condition: Box::new(Expr::Eq(
784                            Box::new(Expr::Call(
785                                BuiltinFunction::TypeOf.call_index(),
786                                Vec::new(),
787                                vec![Expr::Var(container_slot)],
788                            )),
789                            Box::new(Expr::String("string".to_string())),
790                        )),
791                        then_expr: Box::new(index_lookup),
792                        else_expr: Box::new(Expr::Null),
793                    }),
794                }),
795            }),
796        };
797
798        self.compile_expr(&lowered)
799    }
800
801    fn compile_option_unwrap_or_expr(
802        &mut self,
803        value: &Expr,
804        value_slot: LocalSlot,
805        fallback: &Expr,
806    ) -> Result<(), CompileError> {
807        self.compile_scalar_expr(value)?;
808        self.emit_stloc(value_slot)?;
809        let lowered = Expr::IfElse {
810            condition: Box::new(Expr::Eq(
811                Box::new(Expr::Call(
812                    BuiltinFunction::TypeOf.call_index(),
813                    Vec::new(),
814                    vec![Expr::Var(value_slot)],
815                )),
816                Box::new(Expr::String("null".to_string())),
817            )),
818            then_expr: Box::new(fallback.clone()),
819            else_expr: Box::new(Expr::Var(value_slot)),
820        };
821        self.compile_expr(&lowered)
822    }
823
824    fn bind_closure_captures(&mut self, closure: &ClosureExpr) -> Result<(), CompileError> {
825        let mut seen = HashSet::new();
826        for (source_index, captured_slot) in &closure.capture_copies {
827            if !seen.insert((*source_index, *captured_slot)) {
828                continue;
829            }
830            let capture_mode = self.closure_capture_mode_for_slot(closure, *captured_slot);
831            self.bind_capture_copy(*source_index, *captured_slot, capture_mode)?;
832        }
833        Ok(())
834    }
835
836    fn bind_function_decl_captures(&mut self, index: u16) -> Result<(), CompileError> {
837        let Some(function_impl) = self.function_impls.get(&index).cloned() else {
838            return Ok(());
839        };
840        let mut seen = HashSet::new();
841        for (source_index, captured_slot) in &function_impl.capture_copies {
842            if !seen.insert((*source_index, *captured_slot)) {
843                continue;
844            }
845            let capture_mode = self.function_capture_mode_for_slot(&function_impl, *captured_slot);
846            self.bind_capture_copy(*source_index, *captured_slot, capture_mode)?;
847        }
848        Ok(())
849    }
850
851    fn bind_capture_copy(
852        &mut self,
853        source_index: LocalSlot,
854        captured_slot: LocalSlot,
855        capture_mode: CaptureBindingMode,
856    ) -> Result<(), CompileError> {
857        let captured_type = self.type_state.get(source_index);
858        if self.enable_local_move_semantics && capture_mode == CaptureBindingMode::Move {
859            self.emit_move_ldloc(source_index)?;
860            self.type_state.set(source_index, typing::BoundType::Null);
861        } else {
862            self.emit_copy_ldloc(source_index)?;
863        }
864        self.emit_stloc(captured_slot)?;
865        self.type_state.set(captured_slot, captured_type);
866        Ok(())
867    }
868
869    fn function_capture_mode_for_slot(
870        &self,
871        function_impl: &FunctionImpl,
872        captured_slot: LocalSlot,
873    ) -> CaptureBindingMode {
874        let mut mode = CaptureBindingMode::Copy;
875        let mut seen = false;
876        self.capture_mode_for_stmts(
877            &function_impl.body_stmts,
878            captured_slot,
879            CaptureBindingMode::Move,
880            &mut mode,
881            &mut seen,
882        );
883        self.capture_mode_for_expr(
884            &function_impl.body_expr,
885            captured_slot,
886            CaptureBindingMode::Move,
887            &mut mode,
888            &mut seen,
889        );
890        if seen { mode } else { CaptureBindingMode::Move }
891    }
892
893    fn closure_capture_mode_for_slot(
894        &self,
895        closure: &ClosureExpr,
896        captured_slot: LocalSlot,
897    ) -> CaptureBindingMode {
898        let mut mode = CaptureBindingMode::Copy;
899        let mut seen = false;
900        self.capture_mode_for_expr(
901            &closure.body,
902            captured_slot,
903            CaptureBindingMode::Move,
904            &mut mode,
905            &mut seen,
906        );
907        if seen { mode } else { CaptureBindingMode::Move }
908    }
909
910    fn capture_mode_for_stmts(
911        &self,
912        stmts: &[Stmt],
913        captured_slot: LocalSlot,
914        context: CaptureBindingMode,
915        mode: &mut CaptureBindingMode,
916        seen: &mut bool,
917    ) {
918        for stmt in stmts {
919            self.capture_mode_for_stmt(stmt, captured_slot, context, mode, seen);
920        }
921    }
922
923    fn capture_mode_for_stmt(
924        &self,
925        stmt: &Stmt,
926        captured_slot: LocalSlot,
927        context: CaptureBindingMode,
928        mode: &mut CaptureBindingMode,
929        seen: &mut bool,
930    ) {
931        match stmt {
932            Stmt::Noop { .. }
933            | Stmt::FuncDecl { .. }
934            | Stmt::Break { .. }
935            | Stmt::Continue { .. } => {}
936            Stmt::Drop { index, .. } => {
937                if *index == captured_slot {
938                    *seen = true;
939                    *mode = (*mode).max(context);
940                }
941            }
942            Stmt::Let { index, expr, .. } | Stmt::Assign { index, expr, .. } => {
943                if *index == captured_slot {
944                    *seen = true;
945                    *mode = (*mode).max(context);
946                }
947                self.capture_mode_for_expr(expr, captured_slot, context, mode, seen);
948            }
949            Stmt::ClosureLet { closure, .. } => {
950                for (nested_source_slot, nested_captured_slot) in &closure.capture_copies {
951                    if *nested_source_slot == captured_slot {
952                        self.capture_mode_for_expr(
953                            &closure.body,
954                            *nested_captured_slot,
955                            CaptureBindingMode::Move,
956                            mode,
957                            seen,
958                        );
959                    }
960                }
961                self.capture_mode_for_expr(&closure.body, captured_slot, context, mode, seen);
962            }
963            Stmt::Expr { expr, .. } => {
964                self.capture_mode_for_expr(expr, captured_slot, context, mode, seen);
965            }
966            Stmt::IfElse {
967                condition,
968                then_branch,
969                else_branch,
970                ..
971            } => {
972                self.capture_mode_for_expr(condition, captured_slot, context, mode, seen);
973                self.capture_mode_for_stmts(then_branch, captured_slot, context, mode, seen);
974                self.capture_mode_for_stmts(else_branch, captured_slot, context, mode, seen);
975            }
976            Stmt::For {
977                init,
978                condition,
979                post,
980                body,
981                ..
982            } => {
983                self.capture_mode_for_stmt(init, captured_slot, context, mode, seen);
984                self.capture_mode_for_expr(condition, captured_slot, context, mode, seen);
985                self.capture_mode_for_stmt(post, captured_slot, context, mode, seen);
986                self.capture_mode_for_stmts(body, captured_slot, context, mode, seen);
987            }
988            Stmt::While {
989                condition, body, ..
990            } => {
991                self.capture_mode_for_expr(condition, captured_slot, context, mode, seen);
992                self.capture_mode_for_stmts(body, captured_slot, context, mode, seen);
993            }
994        }
995    }
996
997    fn capture_mode_for_expr(
998        &self,
999        expr: &Expr,
1000        captured_slot: LocalSlot,
1001        context: CaptureBindingMode,
1002        mode: &mut CaptureBindingMode,
1003        seen: &mut bool,
1004    ) {
1005        match expr {
1006            Expr::Null
1007            | Expr::Int(_)
1008            | Expr::Float(_)
1009            | Expr::Bool(_)
1010            | Expr::Bytes(_)
1011            | Expr::String(_)
1012            | Expr::FunctionRef(_) => {}
1013            Expr::Var(index) => {
1014                if *index == captured_slot {
1015                    *seen = true;
1016                    *mode = (*mode).max(context);
1017                }
1018            }
1019            Expr::MoveVar(index) => {
1020                if *index == captured_slot {
1021                    *seen = true;
1022                    *mode = CaptureBindingMode::Move;
1023                }
1024            }
1025            Expr::MoveField { root, .. } | Expr::MoveIndex { root, .. } => {
1026                if *root == captured_slot {
1027                    *seen = true;
1028                    *mode = CaptureBindingMode::Move;
1029                }
1030            }
1031            Expr::OptionalGet {
1032                container,
1033                key,
1034                container_slot,
1035                key_slot,
1036            } => {
1037                if *container_slot == captured_slot || *key_slot == captured_slot {
1038                    *seen = true;
1039                    *mode = (*mode).max(context);
1040                }
1041                self.capture_mode_for_expr(container, captured_slot, context, mode, seen);
1042                self.capture_mode_for_expr(key, captured_slot, context, mode, seen);
1043            }
1044            Expr::OptionUnwrapOr {
1045                value,
1046                value_slot,
1047                fallback,
1048            } => {
1049                if *value_slot == captured_slot {
1050                    *seen = true;
1051                    *mode = (*mode).max(context);
1052                }
1053                self.capture_mode_for_expr(value, captured_slot, context, mode, seen);
1054                self.capture_mode_for_expr(fallback, captured_slot, context, mode, seen);
1055            }
1056            Expr::Call(_, _, args) | Expr::LocalCall(_, _, args) => {
1057                for arg in args {
1058                    self.capture_mode_for_expr(arg, captured_slot, context, mode, seen);
1059                }
1060            }
1061            Expr::Closure(closure) => {
1062                for (nested_source_slot, nested_captured_slot) in &closure.capture_copies {
1063                    if *nested_source_slot == captured_slot {
1064                        self.capture_mode_for_expr(
1065                            &closure.body,
1066                            *nested_captured_slot,
1067                            CaptureBindingMode::Move,
1068                            mode,
1069                            seen,
1070                        );
1071                    }
1072                }
1073                self.capture_mode_for_expr(&closure.body, captured_slot, context, mode, seen);
1074            }
1075            Expr::ClosureCall(closure, args) => {
1076                for arg in args {
1077                    self.capture_mode_for_expr(arg, captured_slot, context, mode, seen);
1078                }
1079                for (nested_source_slot, nested_captured_slot) in &closure.capture_copies {
1080                    if *nested_source_slot == captured_slot {
1081                        self.capture_mode_for_expr(
1082                            &closure.body,
1083                            *nested_captured_slot,
1084                            CaptureBindingMode::Move,
1085                            mode,
1086                            seen,
1087                        );
1088                    }
1089                }
1090                self.capture_mode_for_expr(&closure.body, captured_slot, context, mode, seen);
1091            }
1092            Expr::Add(lhs, rhs)
1093            | Expr::Sub(lhs, rhs)
1094            | Expr::Mul(lhs, rhs)
1095            | Expr::Div(lhs, rhs)
1096            | Expr::Mod(lhs, rhs)
1097            | Expr::And(lhs, rhs)
1098            | Expr::Or(lhs, rhs)
1099            | Expr::Eq(lhs, rhs)
1100            | Expr::Lt(lhs, rhs)
1101            | Expr::Gt(lhs, rhs) => {
1102                self.capture_mode_for_expr(lhs, captured_slot, context, mode, seen);
1103                self.capture_mode_for_expr(rhs, captured_slot, context, mode, seen);
1104            }
1105            Expr::Neg(inner) | Expr::Not(inner) => {
1106                self.capture_mode_for_expr(inner, captured_slot, context, mode, seen);
1107            }
1108            Expr::ToOwned(inner) => {
1109                self.capture_mode_for_expr(
1110                    inner,
1111                    captured_slot,
1112                    CaptureBindingMode::Copy,
1113                    mode,
1114                    seen,
1115                );
1116            }
1117            Expr::Borrow(inner) => {
1118                self.capture_mode_for_expr(
1119                    inner,
1120                    captured_slot,
1121                    CaptureBindingMode::Borrow,
1122                    mode,
1123                    seen,
1124                );
1125            }
1126            Expr::BorrowMut(inner) => {
1127                self.capture_mode_for_expr(
1128                    inner,
1129                    captured_slot,
1130                    CaptureBindingMode::BorrowMut,
1131                    mode,
1132                    seen,
1133                );
1134            }
1135            Expr::IfElse {
1136                condition,
1137                then_expr,
1138                else_expr,
1139            } => {
1140                self.capture_mode_for_expr(condition, captured_slot, context, mode, seen);
1141                self.capture_mode_for_expr(then_expr, captured_slot, context, mode, seen);
1142                self.capture_mode_for_expr(else_expr, captured_slot, context, mode, seen);
1143            }
1144            Expr::Match {
1145                value_slot,
1146                result_slot,
1147                value,
1148                arms,
1149                default,
1150            } => {
1151                if *value_slot == captured_slot || *result_slot == captured_slot {
1152                    *seen = true;
1153                    *mode = (*mode).max(context);
1154                }
1155                self.capture_mode_for_expr(value, captured_slot, context, mode, seen);
1156                for (_, arm_expr) in arms {
1157                    self.capture_mode_for_expr(arm_expr, captured_slot, context, mode, seen);
1158                }
1159                self.capture_mode_for_expr(default, captured_slot, context, mode, seen);
1160            }
1161            Expr::Block { stmts, expr } => {
1162                self.capture_mode_for_stmts(stmts, captured_slot, context, mode, seen);
1163                self.capture_mode_for_expr(expr, captured_slot, context, mode, seen);
1164            }
1165        }
1166    }
1167
1168    fn callable_binding_from_expr(
1169        &mut self,
1170        expr: &Expr,
1171    ) -> Result<Option<CallableBinding>, CompileError> {
1172        match expr {
1173            Expr::Closure(closure) => {
1174                self.bind_closure_captures(closure)?;
1175                Ok(Some(CallableBinding::Closure(closure.clone())))
1176            }
1177            Expr::FunctionRef(index) => Ok(Some(CallableBinding::Function(*index))),
1178            Expr::Var(index) => Ok(self.callable_bindings.get(index).cloned()),
1179            _ => Ok(None),
1180        }
1181    }
1182
1183    fn assign_expr_to_slot(
1184        &mut self,
1185        slot: LocalSlot,
1186        declared_schema: Option<&TypeSchema>,
1187        expr: &Expr,
1188    ) -> Result<(), CompileError> {
1189        if let Some(callable) = self.callable_binding_from_expr(expr)? {
1190            self.callable_bindings.insert(slot, callable.clone());
1191            match callable {
1192                CallableBinding::Closure(closure) => self.type_state.bind_closure(slot, &closure),
1193                CallableBinding::Function(index) => self.type_state.bind_function(slot, index),
1194            }
1195            return Ok(());
1196        }
1197        let declared_binding = declared_schema.map(TypeSchema::split_optional).or_else(|| {
1198            self.type_state
1199                .has_declared_schema(slot)
1200                .then(|| {
1201                    (
1202                        self.type_state.schema(slot).cloned(),
1203                        self.type_state.is_optional(slot),
1204                    )
1205                })
1206                .and_then(|(schema, optional)| schema.map(|schema| (schema, optional)))
1207        });
1208        let slot_declared_schema = declared_binding.as_ref().map(|(schema, _)| schema.clone());
1209        let declared_optional = declared_binding
1210            .as_ref()
1211            .map(|(_, optional)| *optional)
1212            .unwrap_or(false);
1213        let optional = typing::expr_is_optional_with_function_impls_and_imports(
1214            expr,
1215            &self.type_state,
1216            &self.function_impls,
1217            &self.function_decls,
1218            &self.struct_schemas,
1219            &self.host_import_return_types,
1220            &self.host_import_signatures,
1221        ) || declared_optional;
1222        let ty = if optional {
1223            typing::infer_optional_expr_inner_type_with_function_impls_and_imports(
1224                expr,
1225                &self.type_state,
1226                &self.function_impls,
1227                &self.function_decls,
1228                &self.struct_schemas,
1229                &self.host_import_return_types,
1230                &self.host_import_signatures,
1231            )
1232        } else {
1233            self.infer_bound_type(expr)
1234        };
1235        self.callable_bindings.remove(&slot);
1236        self.compile_scalar_expr(expr)?;
1237        self.emit_stloc(slot)?;
1238        let schema = slot_declared_schema.clone().or_else(|| {
1239            if optional {
1240                typing::infer_optional_expr_inner_schema_with_function_impls_and_imports(
1241                    expr,
1242                    &self.type_state,
1243                    &self.function_impls,
1244                    &self.function_decls,
1245                    &self.struct_schemas,
1246                    &self.host_import_return_types,
1247                    &self.host_import_signatures,
1248                )
1249            } else {
1250                typing::infer_expr_schema_with_function_impls_and_imports(
1251                    expr,
1252                    &self.type_state,
1253                    &self.function_impls,
1254                    &self.function_decls,
1255                    &self.struct_schemas,
1256                    &self.host_import_return_types,
1257                    &self.host_import_signatures,
1258                )
1259            }
1260        });
1261        let from_declared_schema =
1262            slot_declared_schema.is_some() || self.type_state.has_declared_schema(slot);
1263        let ty = slot_declared_schema
1264            .as_ref()
1265            .map(typing::bound_type_from_schema)
1266            .unwrap_or(ty);
1267        self.type_state.set_with_optional_schema_origin(
1268            slot,
1269            ty,
1270            schema,
1271            from_declared_schema,
1272            optional,
1273        );
1274        Ok(())
1275    }
1276
1277    fn compile_scalar_expr(&mut self, expr: &Expr) -> Result<(), CompileError> {
1278        self.compile_expr(expr)
1279    }
1280
1281    fn compile_short_circuit_and(&mut self, lhs: &Expr, rhs: &Expr) -> Result<(), CompileError> {
1282        let false_label = self.fresh_label("and_false");
1283        let end_label = self.fresh_label("and_end");
1284        self.compile_scalar_expr(lhs)?;
1285        self.assembler.brfalse_label(&false_label);
1286        self.compile_scalar_expr(rhs)?;
1287        self.assembler.br_label(&end_label);
1288        self.assembler
1289            .label(&false_label)
1290            .map_err(CompileError::Assembler)?;
1291        self.assembler.push_const(Value::Bool(false));
1292        self.assembler
1293            .label(&end_label)
1294            .map_err(CompileError::Assembler)?;
1295        Ok(())
1296    }
1297
1298    fn compile_short_circuit_or(&mut self, lhs: &Expr, rhs: &Expr) -> Result<(), CompileError> {
1299        let rhs_label = self.fresh_label("or_rhs");
1300        let end_label = self.fresh_label("or_end");
1301        self.compile_scalar_expr(lhs)?;
1302        self.assembler.brfalse_label(&rhs_label);
1303        self.assembler.push_const(Value::Bool(true));
1304        self.assembler.br_label(&end_label);
1305        self.assembler
1306            .label(&rhs_label)
1307            .map_err(CompileError::Assembler)?;
1308        self.compile_scalar_expr(rhs)?;
1309        self.assembler
1310            .label(&end_label)
1311            .map_err(CompileError::Assembler)?;
1312        Ok(())
1313    }
1314
1315    fn compile_function_call(&mut self, index: u16, args: &[Expr]) -> Result<(), CompileError> {
1316        if let Some(function_impl) = self.function_impls.get(&index).cloned() {
1317            return self.compile_inline_function_call(index, &function_impl, args);
1318        }
1319        self.compile_direct_call(index, args)
1320    }
1321
1322    fn compile_inline_function_call(
1323        &mut self,
1324        index: u16,
1325        function_impl: &FunctionImpl,
1326        args: &[Expr],
1327    ) -> Result<(), CompileError> {
1328        if function_impl.param_slots.len() != args.len() {
1329            return Err(CompileError::CallableArityMismatch {
1330                expected: function_impl.param_slots.len(),
1331                got: args.len(),
1332            });
1333        }
1334        let frame_slots = collect_function_frame_slots(function_impl);
1335        let callable_snapshot = self.callable_bindings.clone();
1336        for (arg, slot) in args.iter().zip(function_impl.param_slots.iter()) {
1337            self.assign_expr_to_slot(*slot, None, arg)?;
1338        }
1339        if self.inline_call_stack.contains(&index) {
1340            self.callable_bindings = callable_snapshot;
1341            return Err(CompileError::InlineFunctionRecursion(format!(
1342                "recursive RustScript function call detected for function index {}",
1343                index
1344            )));
1345        }
1346        self.inline_call_stack.push(index);
1347        let result = (|| -> Result<(), CompileError> {
1348            self.compile_stmts(&function_impl.body_stmts)?;
1349            self.compile_expr(&function_impl.body_expr)
1350        })();
1351        self.inline_call_stack.pop();
1352        self.callable_bindings = callable_snapshot;
1353        result?;
1354        self.emit_inline_frame_clears(&frame_slots)?;
1355        Ok(())
1356    }
1357
1358    fn compile_inline_closure_call(
1359        &mut self,
1360        closure: &ClosureExpr,
1361        args: &[Expr],
1362    ) -> Result<(), CompileError> {
1363        if closure.param_slots.len() != args.len() {
1364            return Err(CompileError::CallableArityMismatch {
1365                expected: closure.param_slots.len(),
1366                got: args.len(),
1367            });
1368        }
1369        let frame_slots = collect_closure_frame_slots(closure);
1370        let callable_snapshot = self.callable_bindings.clone();
1371        for (arg, slot) in args.iter().zip(closure.param_slots.iter()) {
1372            self.assign_expr_to_slot(*slot, None, arg)?;
1373        }
1374        let result = self.compile_expr(&closure.body);
1375        self.callable_bindings = callable_snapshot;
1376        result?;
1377        self.emit_inline_frame_clears(&frame_slots)?;
1378        Ok(())
1379    }
1380
1381    fn compile_direct_call(&mut self, index: u16, args: &[Expr]) -> Result<(), CompileError> {
1382        for arg in args {
1383            self.compile_scalar_expr(arg)?;
1384        }
1385        let argc = u8::try_from(args.len()).map_err(|_| CompileError::CallArityOverflow)?;
1386        if let Some(builtin) = BuiltinFunction::from_call_index(index) {
1387            debug_assert!(builtin.accepts_arity(argc));
1388            self.record_builtin_call_operand_types(args);
1389            self.assembler.call(index, argc);
1390            return Ok(());
1391        }
1392        let remapped_index = self.call_index_remap.get(&index).copied().unwrap_or(index);
1393        self.assembler.call(remapped_index, argc);
1394        Ok(())
1395    }
1396
1397    fn compile_callable_call(
1398        &mut self,
1399        callable: CallableBinding,
1400        args: &[Expr],
1401    ) -> Result<(), CompileError> {
1402        match callable {
1403            CallableBinding::Closure(closure) => self.compile_inline_closure_call(&closure, args),
1404            CallableBinding::Function(index) => self.compile_function_call(index, args),
1405        }
1406    }
1407
1408    fn compile_match_pattern_condition(
1409        &mut self,
1410        value_slot: LocalSlot,
1411        pattern: &MatchPattern,
1412    ) -> Result<(), CompileError> {
1413        match pattern {
1414            MatchPattern::Int(v) => {
1415                self.emit_copy_ldloc(value_slot)?;
1416                self.assembler.push_const(Value::Int(*v));
1417                self.assembler.ceq();
1418            }
1419            MatchPattern::String(v) => {
1420                self.emit_copy_ldloc(value_slot)?;
1421                self.assembler.push_const(Value::string(v.clone()));
1422                self.assembler.ceq();
1423            }
1424            MatchPattern::Bytes(v) => {
1425                self.emit_copy_ldloc(value_slot)?;
1426                self.assembler.push_const(Value::bytes(v.clone()));
1427                self.assembler.ceq();
1428            }
1429            MatchPattern::Null => {
1430                self.emit_copy_ldloc(value_slot)?;
1431                self.assembler.push_const(Value::Null);
1432                self.assembler.ceq();
1433            }
1434            MatchPattern::None => {
1435                self.emit_copy_ldloc(value_slot)?;
1436                self.assembler.push_const(Value::Null);
1437                self.assembler.ceq();
1438            }
1439            MatchPattern::SomeBinding(_) => {
1440                self.emit_copy_ldloc(value_slot)?;
1441                self.assembler.push_const(Value::Null);
1442                self.assembler.ceq();
1443                self.assembler.not();
1444            }
1445            MatchPattern::Type(type_pattern) => {
1446                self.compile_match_type_pattern_condition(value_slot, type_pattern)?;
1447            }
1448        }
1449        Ok(())
1450    }
1451
1452    fn compile_match_type_pattern_condition(
1453        &mut self,
1454        value_slot: LocalSlot,
1455        type_pattern: &MatchTypePattern,
1456    ) -> Result<(), CompileError> {
1457        match type_pattern {
1458            MatchTypePattern::Int => self.compile_type_name_equals(value_slot, "int")?,
1459            MatchTypePattern::Float => self.compile_type_name_equals(value_slot, "float")?,
1460            MatchTypePattern::Bool => self.compile_type_name_equals(value_slot, "bool")?,
1461            MatchTypePattern::String => self.compile_type_name_equals(value_slot, "string")?,
1462            MatchTypePattern::Bytes => self.compile_type_name_equals(value_slot, "bytes")?,
1463            MatchTypePattern::Array => self.compile_type_name_equals(value_slot, "array")?,
1464            MatchTypePattern::Map => self.compile_type_name_equals(value_slot, "map")?,
1465            MatchTypePattern::Number => {
1466                let number_fallback_label = self.fresh_label("match_type_number_fallback");
1467                let number_end_label = self.fresh_label("match_type_number_end");
1468
1469                self.compile_type_name_equals(value_slot, "int")?;
1470                self.assembler.brfalse_label(&number_fallback_label);
1471                self.assembler.push_const(Value::Bool(true));
1472                self.assembler.br_label(&number_end_label);
1473                self.assembler
1474                    .label(&number_fallback_label)
1475                    .map_err(CompileError::Assembler)?;
1476                self.compile_type_name_equals(value_slot, "float")?;
1477                self.assembler
1478                    .label(&number_end_label)
1479                    .map_err(CompileError::Assembler)?;
1480            }
1481        }
1482        Ok(())
1483    }
1484
1485    fn compile_type_name_equals(
1486        &mut self,
1487        value_slot: LocalSlot,
1488        expected: &str,
1489    ) -> Result<(), CompileError> {
1490        self.emit_copy_ldloc(value_slot)?;
1491        self.assembler.call(BuiltinFunction::TypeOf.call_index(), 1);
1492        self.assembler
1493            .push_const(Value::string(expected.to_string()));
1494        self.assembler.ceq();
1495        Ok(())
1496    }
1497
1498    fn bind_match_pattern_slot(
1499        &mut self,
1500        pattern: &MatchPattern,
1501        value: &Expr,
1502        value_slot: LocalSlot,
1503        match_entry_type_state: &typing::LocalTypeState,
1504    ) -> Result<(), CompileError> {
1505        let Some(binding_slot) = pattern.binding_slot() else {
1506            return Ok(());
1507        };
1508        let ty = typing::infer_optional_expr_inner_type_with_function_impls_and_imports(
1509            value,
1510            match_entry_type_state,
1511            &self.function_impls,
1512            &self.function_decls,
1513            &self.struct_schemas,
1514            &self.host_import_return_types,
1515            &self.host_import_signatures,
1516        );
1517        let schema = typing::infer_optional_expr_inner_schema_with_function_impls_and_imports(
1518            value,
1519            match_entry_type_state,
1520            &self.function_impls,
1521            &self.function_decls,
1522            &self.struct_schemas,
1523            &self.host_import_return_types,
1524            &self.host_import_signatures,
1525        );
1526        self.emit_copy_ldloc(value_slot)?;
1527        self.emit_stloc(binding_slot)?;
1528        self.type_state
1529            .set_with_optional_schema_origin(binding_slot, ty, schema, false, false);
1530        Ok(())
1531    }
1532
1533    fn infer_bound_type(&self, expr: &Expr) -> typing::BoundType {
1534        typing::infer_expr_type_with_function_impls_and_imports(
1535            expr,
1536            &self.type_state,
1537            &self.function_impls,
1538            &self.function_decls,
1539            &self.struct_schemas,
1540            &self.host_import_return_types,
1541            &self.host_import_signatures,
1542        )
1543    }
1544
1545    fn simulate_stmt_type_state(
1546        &self,
1547        stmts: &[Stmt],
1548        initial_state: &typing::LocalTypeState,
1549    ) -> typing::LocalTypeState {
1550        let mut state = initial_state.clone();
1551        typing::apply_stmts_with_function_impls_and_imports(
1552            stmts,
1553            &mut state,
1554            &self.function_impls,
1555            &self.function_decls,
1556            &self.struct_schemas,
1557            &self.host_import_return_types,
1558            &self.host_import_signatures,
1559        );
1560        state
1561    }
1562
1563    fn stabilize_loop_type_state<F>(
1564        &self,
1565        initial_state: &typing::LocalTypeState,
1566        mut run_iteration: F,
1567    ) -> typing::LocalTypeState
1568    where
1569        F: FnMut(&mut typing::LocalTypeState),
1570    {
1571        let zero_iteration = initial_state.clone();
1572        let mut first_iteration = initial_state.clone();
1573        run_iteration(&mut first_iteration);
1574        let mut second_iteration = first_iteration.clone();
1575        run_iteration(&mut second_iteration);
1576
1577        let mut stable_iteration = typing::LocalTypeState::default();
1578        stable_iteration.merge_from_branches(&first_iteration, &second_iteration);
1579
1580        let mut stabilized = zero_iteration.clone();
1581        stabilized.merge_from_branches(&zero_iteration, &stable_iteration);
1582        stabilized
1583    }
1584
1585    fn value_type_of_expr(&self, expr: &Expr) -> ValueType {
1586        ValueType::from(self.infer_bound_type(expr))
1587    }
1588
1589    fn record_operand_types(&mut self, lhs: ValueType, rhs: ValueType) {
1590        if lhs == ValueType::Unknown || rhs == ValueType::Unknown {
1591            return;
1592        }
1593        self.type_map
1594            .operand_types
1595            .insert(self.assembler.position() as usize, (lhs, rhs));
1596    }
1597
1598    fn record_unary_operand_type(&mut self, operand: ValueType) {
1599        if operand == ValueType::Unknown {
1600            return;
1601        }
1602        self.type_map.operand_types.insert(
1603            self.assembler.position() as usize,
1604            (operand, ValueType::Unknown),
1605        );
1606    }
1607
1608    fn record_builtin_call_operand_types(&mut self, args: &[Expr]) {
1609        if args.is_empty() {
1610            return;
1611        }
1612        let lhs = self.value_type_of_expr(&args[0]);
1613        let rhs = args
1614            .get(1)
1615            .map(|expr| self.value_type_of_expr(expr))
1616            .unwrap_or(ValueType::Unknown);
1617        if lhs == ValueType::Unknown && rhs == ValueType::Unknown {
1618            return;
1619        }
1620        self.type_map
1621            .operand_types
1622            .insert(self.assembler.position() as usize, (lhs, rhs));
1623    }
1624
1625    fn fresh_label(&mut self, prefix: &str) -> String {
1626        let label = format!("{prefix}_{}", self.next_label_id);
1627        self.next_label_id += 1;
1628        label
1629    }
1630
1631    fn emit_move_ldloc(&mut self, slot: LocalSlot) -> Result<(), CompileError> {
1632        let operand = local_slot_operand(slot)?;
1633        self.assembler.ldloc(operand);
1634        self.assembler.push_const(Value::Null);
1635        self.assembler.stloc(operand);
1636        Ok(())
1637    }
1638
1639    fn emit_copy_ldloc(&mut self, slot: LocalSlot) -> Result<(), CompileError> {
1640        self.assembler.ldloc(local_slot_operand(slot)?);
1641        Ok(())
1642    }
1643
1644    fn emit_stloc(&mut self, slot: LocalSlot) -> Result<(), CompileError> {
1645        self.assembler.stloc(local_slot_operand(slot)?);
1646        Ok(())
1647    }
1648
1649    fn emit_inline_frame_clears(&mut self, slots: &[LocalSlot]) -> Result<(), CompileError> {
1650        for slot in slots {
1651            self.assembler.push_const(Value::Null);
1652            self.emit_stloc(*slot)?;
1653            self.type_state.set(*slot, typing::BoundType::Null);
1654        }
1655        Ok(())
1656    }
1657
1658    fn compile_string_concat_operand(&mut self, expr: &Expr) -> Result<(), CompileError> {
1659        if let Some(value) = eval_const_int_expr(expr) {
1660            self.assembler.push_const(Value::string(value.to_string()));
1661            return Ok(());
1662        }
1663
1664        self.compile_scalar_expr(expr)?;
1665        self.lower_number_to_string_for_concat_top();
1666        Ok(())
1667    }
1668
1669    fn lower_number_to_string_for_concat_top(&mut self) {
1670        let not_int_label = self.fresh_label("concat_not_int");
1671        let not_float_label = self.fresh_label("concat_not_float");
1672        let done_label = self.fresh_label("concat_value_done");
1673
1674        self.assembler.dup();
1675        self.assembler.call(BuiltinFunction::TypeOf.call_index(), 1);
1676        self.assembler.push_const(Value::string("int"));
1677        self.assembler.ceq();
1678        self.assembler.brfalse_label(&not_int_label);
1679        self.assembler
1680            .call(BuiltinFunction::ToString.call_index(), 1);
1681        self.assembler.br_label(&done_label);
1682
1683        self.assembler
1684            .label(&not_int_label)
1685            .expect("compiler-generated label should be valid");
1686        self.assembler.dup();
1687        self.assembler.call(BuiltinFunction::TypeOf.call_index(), 1);
1688        self.assembler.push_const(Value::string("float"));
1689        self.assembler.ceq();
1690        self.assembler.brfalse_label(&not_float_label);
1691        self.assembler
1692            .call(BuiltinFunction::ToString.call_index(), 1);
1693        self.assembler.br_label(&done_label);
1694
1695        self.assembler
1696            .label(&not_float_label)
1697            .expect("compiler-generated label should be valid");
1698        self.assembler
1699            .label(&done_label)
1700            .expect("compiler-generated label should be valid");
1701    }
1702}
1703
1704fn local_slot_operand(index: LocalSlot) -> Result<u8, CompileError> {
1705    u8::try_from(index).map_err(|_| CompileError::LocalSlotOverflow(index))
1706}
1707
1708fn collect_function_frame_slots(function_impl: &FunctionImpl) -> Vec<LocalSlot> {
1709    let mut slots = BTreeSet::new();
1710    for slot in &function_impl.param_slots {
1711        slots.insert(*slot);
1712    }
1713    for stmt in &function_impl.body_stmts {
1714        collect_stmt_slot_footprint(stmt, &mut slots);
1715    }
1716    collect_expr_slot_footprint(&function_impl.body_expr, &mut slots);
1717    for (_, captured_slot) in &function_impl.capture_copies {
1718        slots.remove(captured_slot);
1719    }
1720    let mut out = slots.into_iter().collect::<Vec<_>>();
1721    out.sort_unstable_by(|lhs, rhs| rhs.cmp(lhs));
1722    out
1723}
1724
1725fn collect_closure_frame_slots(closure: &ClosureExpr) -> Vec<LocalSlot> {
1726    let mut slots = BTreeSet::new();
1727    for slot in &closure.param_slots {
1728        slots.insert(*slot);
1729    }
1730    collect_expr_slot_footprint(&closure.body, &mut slots);
1731    for (_, captured_slot) in &closure.capture_copies {
1732        slots.remove(captured_slot);
1733    }
1734    let mut out = slots.into_iter().collect::<Vec<_>>();
1735    out.sort_unstable_by(|lhs, rhs| rhs.cmp(lhs));
1736    out
1737}
1738
1739fn collect_stmt_slot_footprint(stmt: &Stmt, slots: &mut BTreeSet<LocalSlot>) {
1740    match stmt {
1741        Stmt::Noop { .. } | Stmt::FuncDecl { .. } | Stmt::Break { .. } | Stmt::Continue { .. } => {}
1742        Stmt::Drop { index, .. } => {
1743            slots.insert(*index);
1744        }
1745        Stmt::Let { index, expr, .. } | Stmt::Assign { index, expr, .. } => {
1746            slots.insert(*index);
1747            collect_expr_slot_footprint(expr, slots);
1748        }
1749        Stmt::ClosureLet { closure, .. } => {
1750            for slot in &closure.param_slots {
1751                slots.insert(*slot);
1752            }
1753            for (source_slot, captured_slot) in &closure.capture_copies {
1754                slots.insert(*source_slot);
1755                slots.insert(*captured_slot);
1756            }
1757            collect_expr_slot_footprint(&closure.body, slots);
1758        }
1759        Stmt::Expr { expr, .. } => collect_expr_slot_footprint(expr, slots),
1760        Stmt::IfElse {
1761            condition,
1762            then_branch,
1763            else_branch,
1764            ..
1765        } => {
1766            collect_expr_slot_footprint(condition, slots);
1767            for stmt in then_branch {
1768                collect_stmt_slot_footprint(stmt, slots);
1769            }
1770            for stmt in else_branch {
1771                collect_stmt_slot_footprint(stmt, slots);
1772            }
1773        }
1774        Stmt::For {
1775            init,
1776            condition,
1777            post,
1778            body,
1779            ..
1780        } => {
1781            collect_stmt_slot_footprint(init, slots);
1782            collect_expr_slot_footprint(condition, slots);
1783            collect_stmt_slot_footprint(post, slots);
1784            for stmt in body {
1785                collect_stmt_slot_footprint(stmt, slots);
1786            }
1787        }
1788        Stmt::While {
1789            condition, body, ..
1790        } => {
1791            collect_expr_slot_footprint(condition, slots);
1792            for stmt in body {
1793                collect_stmt_slot_footprint(stmt, slots);
1794            }
1795        }
1796    }
1797}
1798
1799fn collect_expr_slot_footprint(expr: &Expr, slots: &mut BTreeSet<LocalSlot>) {
1800    match expr {
1801        Expr::Null
1802        | Expr::Int(_)
1803        | Expr::Float(_)
1804        | Expr::Bool(_)
1805        | Expr::Bytes(_)
1806        | Expr::String(_)
1807        | Expr::FunctionRef(_) => {}
1808        Expr::Var(index) | Expr::MoveVar(index) => {
1809            slots.insert(*index);
1810        }
1811        Expr::MoveField { root, .. } | Expr::MoveIndex { root, .. } => {
1812            slots.insert(*root);
1813        }
1814        Expr::OptionalGet {
1815            container,
1816            key,
1817            container_slot,
1818            key_slot,
1819        } => {
1820            slots.insert(*container_slot);
1821            slots.insert(*key_slot);
1822            collect_expr_slot_footprint(container, slots);
1823            collect_expr_slot_footprint(key, slots);
1824        }
1825        Expr::OptionUnwrapOr {
1826            value,
1827            value_slot,
1828            fallback,
1829        } => {
1830            slots.insert(*value_slot);
1831            collect_expr_slot_footprint(value, slots);
1832            collect_expr_slot_footprint(fallback, slots);
1833        }
1834        Expr::Call(_, _, args) => {
1835            for arg in args {
1836                collect_expr_slot_footprint(arg, slots);
1837            }
1838        }
1839        Expr::LocalCall(index, _, args) => {
1840            slots.insert(*index);
1841            for arg in args {
1842                collect_expr_slot_footprint(arg, slots);
1843            }
1844        }
1845        Expr::Closure(closure) => {
1846            for slot in &closure.param_slots {
1847                slots.insert(*slot);
1848            }
1849            for (source_slot, captured_slot) in &closure.capture_copies {
1850                slots.insert(*source_slot);
1851                slots.insert(*captured_slot);
1852            }
1853            collect_expr_slot_footprint(&closure.body, slots);
1854        }
1855        Expr::ClosureCall(closure, args) => {
1856            for slot in &closure.param_slots {
1857                slots.insert(*slot);
1858            }
1859            for (source_slot, captured_slot) in &closure.capture_copies {
1860                slots.insert(*source_slot);
1861                slots.insert(*captured_slot);
1862            }
1863            for arg in args {
1864                collect_expr_slot_footprint(arg, slots);
1865            }
1866            collect_expr_slot_footprint(&closure.body, slots);
1867        }
1868        Expr::Add(lhs, rhs)
1869        | Expr::Sub(lhs, rhs)
1870        | Expr::Mul(lhs, rhs)
1871        | Expr::Div(lhs, rhs)
1872        | Expr::Mod(lhs, rhs)
1873        | Expr::And(lhs, rhs)
1874        | Expr::Or(lhs, rhs)
1875        | Expr::Eq(lhs, rhs)
1876        | Expr::Lt(lhs, rhs)
1877        | Expr::Gt(lhs, rhs) => {
1878            collect_expr_slot_footprint(lhs, slots);
1879            collect_expr_slot_footprint(rhs, slots);
1880        }
1881        Expr::Neg(inner)
1882        | Expr::Not(inner)
1883        | Expr::ToOwned(inner)
1884        | Expr::Borrow(inner)
1885        | Expr::BorrowMut(inner) => collect_expr_slot_footprint(inner, slots),
1886        Expr::IfElse {
1887            condition,
1888            then_expr,
1889            else_expr,
1890        } => {
1891            collect_expr_slot_footprint(condition, slots);
1892            collect_expr_slot_footprint(then_expr, slots);
1893            collect_expr_slot_footprint(else_expr, slots);
1894        }
1895        Expr::Match {
1896            value_slot,
1897            result_slot,
1898            value,
1899            arms,
1900            default,
1901        } => {
1902            slots.insert(*value_slot);
1903            slots.insert(*result_slot);
1904            collect_expr_slot_footprint(value, slots);
1905            for (_, arm_expr) in arms {
1906                collect_expr_slot_footprint(arm_expr, slots);
1907            }
1908            collect_expr_slot_footprint(default, slots);
1909        }
1910        Expr::Block { stmts, expr } => {
1911            for stmt in stmts {
1912                collect_stmt_slot_footprint(stmt, slots);
1913            }
1914            collect_expr_slot_footprint(expr, slots);
1915        }
1916    }
1917}
1918
1919fn shift_amount_for_power_of_two(value: i64) -> Option<u32> {
1920    if value <= 0 {
1921        return None;
1922    }
1923    let as_u64 = value as u64;
1924    if !as_u64.is_power_of_two() {
1925        return None;
1926    }
1927    Some(as_u64.trailing_zeros())
1928}
1929
1930fn is_definitely_string_expr(expr: &Expr) -> bool {
1931    match expr {
1932        Expr::String(_) => true,
1933        Expr::ToOwned(inner) | Expr::Borrow(inner) | Expr::BorrowMut(inner) => {
1934            is_definitely_string_expr(inner)
1935        }
1936        Expr::Add(lhs, rhs) => {
1937            (is_definitely_string_expr(lhs) && is_definitely_string_expr(rhs))
1938                || (is_definitely_string_expr(lhs) && eval_const_int_expr(rhs).is_some())
1939                || (eval_const_int_expr(lhs).is_some() && is_definitely_string_expr(rhs))
1940        }
1941        _ => false,
1942    }
1943}
1944
1945fn eval_const_int_expr(expr: &Expr) -> Option<i64> {
1946    match expr {
1947        Expr::Int(value) => Some(*value),
1948        Expr::ToOwned(inner) | Expr::Borrow(inner) | Expr::BorrowMut(inner) => {
1949            eval_const_int_expr(inner)
1950        }
1951        Expr::Neg(inner) => eval_const_int_expr(inner)?.checked_neg(),
1952        Expr::Add(lhs, rhs) => eval_const_int_expr(lhs)?.checked_add(eval_const_int_expr(rhs)?),
1953        Expr::Sub(lhs, rhs) => eval_const_int_expr(lhs)?.checked_sub(eval_const_int_expr(rhs)?),
1954        Expr::Mul(lhs, rhs) => eval_const_int_expr(lhs)?.checked_mul(eval_const_int_expr(rhs)?),
1955        Expr::Div(lhs, rhs) => {
1956            let rhs = eval_const_int_expr(rhs)?;
1957            if rhs == 0 {
1958                return None;
1959            }
1960            eval_const_int_expr(lhs)?.checked_div(rhs)
1961        }
1962        _ => None,
1963    }
1964}