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        if !self.try_compile_same_local_collection_rebind(slot, expr)? {
1237            self.compile_scalar_expr(expr)?;
1238        }
1239        self.emit_stloc(slot)?;
1240        let schema = slot_declared_schema.clone().or_else(|| {
1241            if optional {
1242                typing::infer_optional_expr_inner_schema_with_function_impls_and_imports(
1243                    expr,
1244                    &self.type_state,
1245                    &self.function_impls,
1246                    &self.function_decls,
1247                    &self.struct_schemas,
1248                    &self.host_import_return_types,
1249                    &self.host_import_signatures,
1250                )
1251            } else {
1252                typing::infer_expr_schema_with_function_impls_and_imports(
1253                    expr,
1254                    &self.type_state,
1255                    &self.function_impls,
1256                    &self.function_decls,
1257                    &self.struct_schemas,
1258                    &self.host_import_return_types,
1259                    &self.host_import_signatures,
1260                )
1261            }
1262        });
1263        let from_declared_schema =
1264            slot_declared_schema.is_some() || self.type_state.has_declared_schema(slot);
1265        let ty = slot_declared_schema
1266            .as_ref()
1267            .map(typing::bound_type_from_schema)
1268            .unwrap_or(ty);
1269        self.type_state.set_with_optional_schema_origin(
1270            slot,
1271            ty,
1272            schema,
1273            from_declared_schema,
1274            optional,
1275        );
1276        Ok(())
1277    }
1278
1279    fn try_compile_same_local_collection_rebind(
1280        &mut self,
1281        target: LocalSlot,
1282        expr: &Expr,
1283    ) -> Result<bool, CompileError> {
1284        if !self.enable_local_move_semantics {
1285            return Ok(false);
1286        }
1287        let Expr::Call(index, _, args) = expr else {
1288            return Ok(false);
1289        };
1290        let Some(builtin) = BuiltinFunction::from_call_index(*index) else {
1291            return Ok(false);
1292        };
1293        let expected_arity = match builtin {
1294            BuiltinFunction::Set => 3,
1295            BuiltinFunction::ArrayPush => 2,
1296            _ => return Ok(false),
1297        };
1298        if args.len() != expected_arity
1299            || !matches!(args.first(), Some(Expr::Var(source)) if *source == target)
1300        {
1301            return Ok(false);
1302        }
1303
1304        for arg in args {
1305            self.compile_scalar_expr(arg)?;
1306        }
1307        self.assembler.push_const(Value::Null);
1308        self.emit_stloc(target)?;
1309        self.emit_direct_call(*index, args)?;
1310        Ok(true)
1311    }
1312
1313    fn compile_scalar_expr(&mut self, expr: &Expr) -> Result<(), CompileError> {
1314        self.compile_expr(expr)
1315    }
1316
1317    fn compile_short_circuit_and(&mut self, lhs: &Expr, rhs: &Expr) -> Result<(), CompileError> {
1318        let false_label = self.fresh_label("and_false");
1319        let end_label = self.fresh_label("and_end");
1320        self.compile_scalar_expr(lhs)?;
1321        self.assembler.brfalse_label(&false_label);
1322        self.compile_scalar_expr(rhs)?;
1323        self.assembler.br_label(&end_label);
1324        self.assembler
1325            .label(&false_label)
1326            .map_err(CompileError::Assembler)?;
1327        self.assembler.push_const(Value::Bool(false));
1328        self.assembler
1329            .label(&end_label)
1330            .map_err(CompileError::Assembler)?;
1331        Ok(())
1332    }
1333
1334    fn compile_short_circuit_or(&mut self, lhs: &Expr, rhs: &Expr) -> Result<(), CompileError> {
1335        let rhs_label = self.fresh_label("or_rhs");
1336        let end_label = self.fresh_label("or_end");
1337        self.compile_scalar_expr(lhs)?;
1338        self.assembler.brfalse_label(&rhs_label);
1339        self.assembler.push_const(Value::Bool(true));
1340        self.assembler.br_label(&end_label);
1341        self.assembler
1342            .label(&rhs_label)
1343            .map_err(CompileError::Assembler)?;
1344        self.compile_scalar_expr(rhs)?;
1345        self.assembler
1346            .label(&end_label)
1347            .map_err(CompileError::Assembler)?;
1348        Ok(())
1349    }
1350
1351    fn compile_function_call(&mut self, index: u16, args: &[Expr]) -> Result<(), CompileError> {
1352        if let Some(function_impl) = self.function_impls.get(&index).cloned() {
1353            return self.compile_inline_function_call(index, &function_impl, args);
1354        }
1355        self.compile_direct_call(index, args)
1356    }
1357
1358    fn compile_inline_function_call(
1359        &mut self,
1360        index: u16,
1361        function_impl: &FunctionImpl,
1362        args: &[Expr],
1363    ) -> Result<(), CompileError> {
1364        if function_impl.param_slots.len() != args.len() {
1365            return Err(CompileError::CallableArityMismatch {
1366                expected: function_impl.param_slots.len(),
1367                got: args.len(),
1368            });
1369        }
1370        let frame_slots = collect_function_frame_slots(function_impl);
1371        let callable_snapshot = self.callable_bindings.clone();
1372        for (arg, slot) in args.iter().zip(function_impl.param_slots.iter()) {
1373            self.assign_expr_to_slot(*slot, None, arg)?;
1374        }
1375        if self.inline_call_stack.contains(&index) {
1376            self.callable_bindings = callable_snapshot;
1377            return Err(CompileError::InlineFunctionRecursion(format!(
1378                "recursive RustScript function call detected for function index {}",
1379                index
1380            )));
1381        }
1382        self.inline_call_stack.push(index);
1383        let result = (|| -> Result<(), CompileError> {
1384            self.compile_stmts(&function_impl.body_stmts)?;
1385            self.compile_expr(&function_impl.body_expr)
1386        })();
1387        self.inline_call_stack.pop();
1388        self.callable_bindings = callable_snapshot;
1389        result?;
1390        self.emit_inline_frame_clears(&frame_slots)?;
1391        Ok(())
1392    }
1393
1394    fn compile_inline_closure_call(
1395        &mut self,
1396        closure: &ClosureExpr,
1397        args: &[Expr],
1398    ) -> Result<(), CompileError> {
1399        if closure.param_slots.len() != args.len() {
1400            return Err(CompileError::CallableArityMismatch {
1401                expected: closure.param_slots.len(),
1402                got: args.len(),
1403            });
1404        }
1405        let frame_slots = collect_closure_frame_slots(closure);
1406        let callable_snapshot = self.callable_bindings.clone();
1407        for (arg, slot) in args.iter().zip(closure.param_slots.iter()) {
1408            self.assign_expr_to_slot(*slot, None, arg)?;
1409        }
1410        let result = self.compile_expr(&closure.body);
1411        self.callable_bindings = callable_snapshot;
1412        result?;
1413        self.emit_inline_frame_clears(&frame_slots)?;
1414        Ok(())
1415    }
1416
1417    fn compile_direct_call(&mut self, index: u16, args: &[Expr]) -> Result<(), CompileError> {
1418        for arg in args {
1419            self.compile_scalar_expr(arg)?;
1420        }
1421        self.emit_direct_call(index, args)
1422    }
1423
1424    fn emit_direct_call(&mut self, index: u16, args: &[Expr]) -> Result<(), CompileError> {
1425        let argc = u8::try_from(args.len()).map_err(|_| CompileError::CallArityOverflow)?;
1426        if let Some(builtin) = BuiltinFunction::from_call_index(index) {
1427            debug_assert!(builtin.accepts_arity(argc));
1428            self.record_builtin_call_operand_types(args);
1429            self.assembler.call(index, argc);
1430            return Ok(());
1431        }
1432        let remapped_index = self.call_index_remap.get(&index).copied().unwrap_or(index);
1433        self.assembler.call(remapped_index, argc);
1434        Ok(())
1435    }
1436
1437    fn compile_callable_call(
1438        &mut self,
1439        callable: CallableBinding,
1440        args: &[Expr],
1441    ) -> Result<(), CompileError> {
1442        match callable {
1443            CallableBinding::Closure(closure) => self.compile_inline_closure_call(&closure, args),
1444            CallableBinding::Function(index) => self.compile_function_call(index, args),
1445        }
1446    }
1447
1448    fn compile_match_pattern_condition(
1449        &mut self,
1450        value_slot: LocalSlot,
1451        pattern: &MatchPattern,
1452    ) -> Result<(), CompileError> {
1453        match pattern {
1454            MatchPattern::Int(v) => {
1455                self.emit_copy_ldloc(value_slot)?;
1456                self.assembler.push_const(Value::Int(*v));
1457                self.assembler.ceq();
1458            }
1459            MatchPattern::String(v) => {
1460                self.emit_copy_ldloc(value_slot)?;
1461                self.assembler.push_const(Value::string(v.clone()));
1462                self.assembler.ceq();
1463            }
1464            MatchPattern::Bytes(v) => {
1465                self.emit_copy_ldloc(value_slot)?;
1466                self.assembler.push_const(Value::bytes(v.clone()));
1467                self.assembler.ceq();
1468            }
1469            MatchPattern::Null => {
1470                self.emit_copy_ldloc(value_slot)?;
1471                self.assembler.push_const(Value::Null);
1472                self.assembler.ceq();
1473            }
1474            MatchPattern::None => {
1475                self.emit_copy_ldloc(value_slot)?;
1476                self.assembler.push_const(Value::Null);
1477                self.assembler.ceq();
1478            }
1479            MatchPattern::SomeBinding(_) => {
1480                self.emit_copy_ldloc(value_slot)?;
1481                self.assembler.push_const(Value::Null);
1482                self.assembler.ceq();
1483                self.assembler.not();
1484            }
1485            MatchPattern::Type(type_pattern) => {
1486                self.compile_match_type_pattern_condition(value_slot, type_pattern)?;
1487            }
1488        }
1489        Ok(())
1490    }
1491
1492    fn compile_match_type_pattern_condition(
1493        &mut self,
1494        value_slot: LocalSlot,
1495        type_pattern: &MatchTypePattern,
1496    ) -> Result<(), CompileError> {
1497        match type_pattern {
1498            MatchTypePattern::Int => self.compile_type_name_equals(value_slot, "int")?,
1499            MatchTypePattern::Float => self.compile_type_name_equals(value_slot, "float")?,
1500            MatchTypePattern::Bool => self.compile_type_name_equals(value_slot, "bool")?,
1501            MatchTypePattern::String => self.compile_type_name_equals(value_slot, "string")?,
1502            MatchTypePattern::Bytes => self.compile_type_name_equals(value_slot, "bytes")?,
1503            MatchTypePattern::Array => self.compile_type_name_equals(value_slot, "array")?,
1504            MatchTypePattern::Map => self.compile_type_name_equals(value_slot, "map")?,
1505            MatchTypePattern::Number => {
1506                let number_fallback_label = self.fresh_label("match_type_number_fallback");
1507                let number_end_label = self.fresh_label("match_type_number_end");
1508
1509                self.compile_type_name_equals(value_slot, "int")?;
1510                self.assembler.brfalse_label(&number_fallback_label);
1511                self.assembler.push_const(Value::Bool(true));
1512                self.assembler.br_label(&number_end_label);
1513                self.assembler
1514                    .label(&number_fallback_label)
1515                    .map_err(CompileError::Assembler)?;
1516                self.compile_type_name_equals(value_slot, "float")?;
1517                self.assembler
1518                    .label(&number_end_label)
1519                    .map_err(CompileError::Assembler)?;
1520            }
1521        }
1522        Ok(())
1523    }
1524
1525    fn compile_type_name_equals(
1526        &mut self,
1527        value_slot: LocalSlot,
1528        expected: &str,
1529    ) -> Result<(), CompileError> {
1530        self.emit_copy_ldloc(value_slot)?;
1531        self.assembler.call(BuiltinFunction::TypeOf.call_index(), 1);
1532        self.assembler
1533            .push_const(Value::string(expected.to_string()));
1534        self.assembler.ceq();
1535        Ok(())
1536    }
1537
1538    fn bind_match_pattern_slot(
1539        &mut self,
1540        pattern: &MatchPattern,
1541        value: &Expr,
1542        value_slot: LocalSlot,
1543        match_entry_type_state: &typing::LocalTypeState,
1544    ) -> Result<(), CompileError> {
1545        let Some(binding_slot) = pattern.binding_slot() else {
1546            return Ok(());
1547        };
1548        let ty = typing::infer_optional_expr_inner_type_with_function_impls_and_imports(
1549            value,
1550            match_entry_type_state,
1551            &self.function_impls,
1552            &self.function_decls,
1553            &self.struct_schemas,
1554            &self.host_import_return_types,
1555            &self.host_import_signatures,
1556        );
1557        let schema = typing::infer_optional_expr_inner_schema_with_function_impls_and_imports(
1558            value,
1559            match_entry_type_state,
1560            &self.function_impls,
1561            &self.function_decls,
1562            &self.struct_schemas,
1563            &self.host_import_return_types,
1564            &self.host_import_signatures,
1565        );
1566        self.emit_copy_ldloc(value_slot)?;
1567        self.emit_stloc(binding_slot)?;
1568        self.type_state
1569            .set_with_optional_schema_origin(binding_slot, ty, schema, false, false);
1570        Ok(())
1571    }
1572
1573    fn infer_bound_type(&self, expr: &Expr) -> typing::BoundType {
1574        typing::infer_expr_type_with_function_impls_and_imports(
1575            expr,
1576            &self.type_state,
1577            &self.function_impls,
1578            &self.function_decls,
1579            &self.struct_schemas,
1580            &self.host_import_return_types,
1581            &self.host_import_signatures,
1582        )
1583    }
1584
1585    fn simulate_stmt_type_state(
1586        &self,
1587        stmts: &[Stmt],
1588        initial_state: &typing::LocalTypeState,
1589    ) -> typing::LocalTypeState {
1590        let mut state = initial_state.clone();
1591        typing::apply_stmts_with_function_impls_and_imports(
1592            stmts,
1593            &mut state,
1594            &self.function_impls,
1595            &self.function_decls,
1596            &self.struct_schemas,
1597            &self.host_import_return_types,
1598            &self.host_import_signatures,
1599        );
1600        state
1601    }
1602
1603    fn stabilize_loop_type_state<F>(
1604        &self,
1605        initial_state: &typing::LocalTypeState,
1606        mut run_iteration: F,
1607    ) -> typing::LocalTypeState
1608    where
1609        F: FnMut(&mut typing::LocalTypeState),
1610    {
1611        let zero_iteration = initial_state.clone();
1612        let mut first_iteration = initial_state.clone();
1613        run_iteration(&mut first_iteration);
1614        let mut second_iteration = first_iteration.clone();
1615        run_iteration(&mut second_iteration);
1616
1617        let mut stable_iteration = typing::LocalTypeState::default();
1618        stable_iteration.merge_from_branches(&first_iteration, &second_iteration);
1619
1620        let mut stabilized = zero_iteration.clone();
1621        stabilized.merge_from_branches(&zero_iteration, &stable_iteration);
1622        stabilized
1623    }
1624
1625    fn value_type_of_expr(&self, expr: &Expr) -> ValueType {
1626        ValueType::from(self.infer_bound_type(expr))
1627    }
1628
1629    fn record_operand_types(&mut self, lhs: ValueType, rhs: ValueType) {
1630        if lhs == ValueType::Unknown || rhs == ValueType::Unknown {
1631            return;
1632        }
1633        self.type_map
1634            .operand_types
1635            .insert(self.assembler.position() as usize, (lhs, rhs));
1636    }
1637
1638    fn record_unary_operand_type(&mut self, operand: ValueType) {
1639        if operand == ValueType::Unknown {
1640            return;
1641        }
1642        self.type_map.operand_types.insert(
1643            self.assembler.position() as usize,
1644            (operand, ValueType::Unknown),
1645        );
1646    }
1647
1648    fn record_builtin_call_operand_types(&mut self, args: &[Expr]) {
1649        if args.is_empty() {
1650            return;
1651        }
1652        let lhs = self.value_type_of_expr(&args[0]);
1653        let rhs = args
1654            .get(1)
1655            .map(|expr| self.value_type_of_expr(expr))
1656            .unwrap_or(ValueType::Unknown);
1657        if lhs == ValueType::Unknown && rhs == ValueType::Unknown {
1658            return;
1659        }
1660        self.type_map
1661            .operand_types
1662            .insert(self.assembler.position() as usize, (lhs, rhs));
1663    }
1664
1665    fn fresh_label(&mut self, prefix: &str) -> String {
1666        let label = format!("{prefix}_{}", self.next_label_id);
1667        self.next_label_id += 1;
1668        label
1669    }
1670
1671    fn emit_move_ldloc(&mut self, slot: LocalSlot) -> Result<(), CompileError> {
1672        let operand = local_slot_operand(slot)?;
1673        self.assembler.ldloc(operand);
1674        self.assembler.push_const(Value::Null);
1675        self.assembler.stloc(operand);
1676        Ok(())
1677    }
1678
1679    fn emit_copy_ldloc(&mut self, slot: LocalSlot) -> Result<(), CompileError> {
1680        self.assembler.ldloc(local_slot_operand(slot)?);
1681        Ok(())
1682    }
1683
1684    fn emit_stloc(&mut self, slot: LocalSlot) -> Result<(), CompileError> {
1685        self.assembler.stloc(local_slot_operand(slot)?);
1686        Ok(())
1687    }
1688
1689    fn emit_inline_frame_clears(&mut self, slots: &[LocalSlot]) -> Result<(), CompileError> {
1690        for slot in slots {
1691            self.assembler.push_const(Value::Null);
1692            self.emit_stloc(*slot)?;
1693            self.type_state.set(*slot, typing::BoundType::Null);
1694        }
1695        Ok(())
1696    }
1697
1698    fn compile_string_concat_operand(&mut self, expr: &Expr) -> Result<(), CompileError> {
1699        if let Some(value) = eval_const_int_expr(expr) {
1700            self.assembler.push_const(Value::string(value.to_string()));
1701            return Ok(());
1702        }
1703
1704        self.compile_scalar_expr(expr)?;
1705        self.lower_number_to_string_for_concat_top();
1706        Ok(())
1707    }
1708
1709    fn lower_number_to_string_for_concat_top(&mut self) {
1710        let not_int_label = self.fresh_label("concat_not_int");
1711        let not_float_label = self.fresh_label("concat_not_float");
1712        let done_label = self.fresh_label("concat_value_done");
1713
1714        self.assembler.dup();
1715        self.assembler.call(BuiltinFunction::TypeOf.call_index(), 1);
1716        self.assembler.push_const(Value::string("int"));
1717        self.assembler.ceq();
1718        self.assembler.brfalse_label(&not_int_label);
1719        self.assembler
1720            .call(BuiltinFunction::ToString.call_index(), 1);
1721        self.assembler.br_label(&done_label);
1722
1723        self.assembler
1724            .label(&not_int_label)
1725            .expect("compiler-generated label should be valid");
1726        self.assembler.dup();
1727        self.assembler.call(BuiltinFunction::TypeOf.call_index(), 1);
1728        self.assembler.push_const(Value::string("float"));
1729        self.assembler.ceq();
1730        self.assembler.brfalse_label(&not_float_label);
1731        self.assembler
1732            .call(BuiltinFunction::ToString.call_index(), 1);
1733        self.assembler.br_label(&done_label);
1734
1735        self.assembler
1736            .label(&not_float_label)
1737            .expect("compiler-generated label should be valid");
1738        self.assembler
1739            .label(&done_label)
1740            .expect("compiler-generated label should be valid");
1741    }
1742}
1743
1744fn local_slot_operand(index: LocalSlot) -> Result<u8, CompileError> {
1745    u8::try_from(index).map_err(|_| CompileError::LocalSlotOverflow(index))
1746}
1747
1748fn collect_function_frame_slots(function_impl: &FunctionImpl) -> Vec<LocalSlot> {
1749    let mut slots = BTreeSet::new();
1750    for slot in &function_impl.param_slots {
1751        slots.insert(*slot);
1752    }
1753    for stmt in &function_impl.body_stmts {
1754        collect_stmt_slot_footprint(stmt, &mut slots);
1755    }
1756    collect_expr_slot_footprint(&function_impl.body_expr, &mut slots);
1757    for (_, captured_slot) in &function_impl.capture_copies {
1758        slots.remove(captured_slot);
1759    }
1760    let mut out = slots.into_iter().collect::<Vec<_>>();
1761    out.sort_unstable_by(|lhs, rhs| rhs.cmp(lhs));
1762    out
1763}
1764
1765fn collect_closure_frame_slots(closure: &ClosureExpr) -> Vec<LocalSlot> {
1766    let mut slots = BTreeSet::new();
1767    for slot in &closure.param_slots {
1768        slots.insert(*slot);
1769    }
1770    collect_expr_slot_footprint(&closure.body, &mut slots);
1771    for (_, captured_slot) in &closure.capture_copies {
1772        slots.remove(captured_slot);
1773    }
1774    let mut out = slots.into_iter().collect::<Vec<_>>();
1775    out.sort_unstable_by(|lhs, rhs| rhs.cmp(lhs));
1776    out
1777}
1778
1779fn collect_stmt_slot_footprint(stmt: &Stmt, slots: &mut BTreeSet<LocalSlot>) {
1780    match stmt {
1781        Stmt::Noop { .. } | Stmt::FuncDecl { .. } | Stmt::Break { .. } | Stmt::Continue { .. } => {}
1782        Stmt::Drop { index, .. } => {
1783            slots.insert(*index);
1784        }
1785        Stmt::Let { index, expr, .. } | Stmt::Assign { index, expr, .. } => {
1786            slots.insert(*index);
1787            collect_expr_slot_footprint(expr, slots);
1788        }
1789        Stmt::ClosureLet { closure, .. } => {
1790            for slot in &closure.param_slots {
1791                slots.insert(*slot);
1792            }
1793            for (source_slot, captured_slot) in &closure.capture_copies {
1794                slots.insert(*source_slot);
1795                slots.insert(*captured_slot);
1796            }
1797            collect_expr_slot_footprint(&closure.body, slots);
1798        }
1799        Stmt::Expr { expr, .. } => collect_expr_slot_footprint(expr, slots),
1800        Stmt::IfElse {
1801            condition,
1802            then_branch,
1803            else_branch,
1804            ..
1805        } => {
1806            collect_expr_slot_footprint(condition, slots);
1807            for stmt in then_branch {
1808                collect_stmt_slot_footprint(stmt, slots);
1809            }
1810            for stmt in else_branch {
1811                collect_stmt_slot_footprint(stmt, slots);
1812            }
1813        }
1814        Stmt::For {
1815            init,
1816            condition,
1817            post,
1818            body,
1819            ..
1820        } => {
1821            collect_stmt_slot_footprint(init, slots);
1822            collect_expr_slot_footprint(condition, slots);
1823            collect_stmt_slot_footprint(post, slots);
1824            for stmt in body {
1825                collect_stmt_slot_footprint(stmt, slots);
1826            }
1827        }
1828        Stmt::While {
1829            condition, body, ..
1830        } => {
1831            collect_expr_slot_footprint(condition, slots);
1832            for stmt in body {
1833                collect_stmt_slot_footprint(stmt, slots);
1834            }
1835        }
1836    }
1837}
1838
1839fn collect_expr_slot_footprint(expr: &Expr, slots: &mut BTreeSet<LocalSlot>) {
1840    match expr {
1841        Expr::Null
1842        | Expr::Int(_)
1843        | Expr::Float(_)
1844        | Expr::Bool(_)
1845        | Expr::Bytes(_)
1846        | Expr::String(_)
1847        | Expr::FunctionRef(_) => {}
1848        Expr::Var(index) | Expr::MoveVar(index) => {
1849            slots.insert(*index);
1850        }
1851        Expr::MoveField { root, .. } | Expr::MoveIndex { root, .. } => {
1852            slots.insert(*root);
1853        }
1854        Expr::OptionalGet {
1855            container,
1856            key,
1857            container_slot,
1858            key_slot,
1859        } => {
1860            slots.insert(*container_slot);
1861            slots.insert(*key_slot);
1862            collect_expr_slot_footprint(container, slots);
1863            collect_expr_slot_footprint(key, slots);
1864        }
1865        Expr::OptionUnwrapOr {
1866            value,
1867            value_slot,
1868            fallback,
1869        } => {
1870            slots.insert(*value_slot);
1871            collect_expr_slot_footprint(value, slots);
1872            collect_expr_slot_footprint(fallback, slots);
1873        }
1874        Expr::Call(_, _, args) => {
1875            for arg in args {
1876                collect_expr_slot_footprint(arg, slots);
1877            }
1878        }
1879        Expr::LocalCall(index, _, args) => {
1880            slots.insert(*index);
1881            for arg in args {
1882                collect_expr_slot_footprint(arg, slots);
1883            }
1884        }
1885        Expr::Closure(closure) => {
1886            for slot in &closure.param_slots {
1887                slots.insert(*slot);
1888            }
1889            for (source_slot, captured_slot) in &closure.capture_copies {
1890                slots.insert(*source_slot);
1891                slots.insert(*captured_slot);
1892            }
1893            collect_expr_slot_footprint(&closure.body, slots);
1894        }
1895        Expr::ClosureCall(closure, args) => {
1896            for slot in &closure.param_slots {
1897                slots.insert(*slot);
1898            }
1899            for (source_slot, captured_slot) in &closure.capture_copies {
1900                slots.insert(*source_slot);
1901                slots.insert(*captured_slot);
1902            }
1903            for arg in args {
1904                collect_expr_slot_footprint(arg, slots);
1905            }
1906            collect_expr_slot_footprint(&closure.body, slots);
1907        }
1908        Expr::Add(lhs, rhs)
1909        | Expr::Sub(lhs, rhs)
1910        | Expr::Mul(lhs, rhs)
1911        | Expr::Div(lhs, rhs)
1912        | Expr::Mod(lhs, rhs)
1913        | Expr::And(lhs, rhs)
1914        | Expr::Or(lhs, rhs)
1915        | Expr::Eq(lhs, rhs)
1916        | Expr::Lt(lhs, rhs)
1917        | Expr::Gt(lhs, rhs) => {
1918            collect_expr_slot_footprint(lhs, slots);
1919            collect_expr_slot_footprint(rhs, slots);
1920        }
1921        Expr::Neg(inner)
1922        | Expr::Not(inner)
1923        | Expr::ToOwned(inner)
1924        | Expr::Borrow(inner)
1925        | Expr::BorrowMut(inner) => collect_expr_slot_footprint(inner, slots),
1926        Expr::IfElse {
1927            condition,
1928            then_expr,
1929            else_expr,
1930        } => {
1931            collect_expr_slot_footprint(condition, slots);
1932            collect_expr_slot_footprint(then_expr, slots);
1933            collect_expr_slot_footprint(else_expr, slots);
1934        }
1935        Expr::Match {
1936            value_slot,
1937            result_slot,
1938            value,
1939            arms,
1940            default,
1941        } => {
1942            slots.insert(*value_slot);
1943            slots.insert(*result_slot);
1944            collect_expr_slot_footprint(value, slots);
1945            for (_, arm_expr) in arms {
1946                collect_expr_slot_footprint(arm_expr, slots);
1947            }
1948            collect_expr_slot_footprint(default, slots);
1949        }
1950        Expr::Block { stmts, expr } => {
1951            for stmt in stmts {
1952                collect_stmt_slot_footprint(stmt, slots);
1953            }
1954            collect_expr_slot_footprint(expr, slots);
1955        }
1956    }
1957}
1958
1959fn shift_amount_for_power_of_two(value: i64) -> Option<u32> {
1960    if value <= 0 {
1961        return None;
1962    }
1963    let as_u64 = value as u64;
1964    if !as_u64.is_power_of_two() {
1965        return None;
1966    }
1967    Some(as_u64.trailing_zeros())
1968}
1969
1970fn is_definitely_string_expr(expr: &Expr) -> bool {
1971    match expr {
1972        Expr::String(_) => true,
1973        Expr::ToOwned(inner) | Expr::Borrow(inner) | Expr::BorrowMut(inner) => {
1974            is_definitely_string_expr(inner)
1975        }
1976        Expr::Add(lhs, rhs) => {
1977            (is_definitely_string_expr(lhs) && is_definitely_string_expr(rhs))
1978                || (is_definitely_string_expr(lhs) && eval_const_int_expr(rhs).is_some())
1979                || (eval_const_int_expr(lhs).is_some() && is_definitely_string_expr(rhs))
1980        }
1981        _ => false,
1982    }
1983}
1984
1985fn eval_const_int_expr(expr: &Expr) -> Option<i64> {
1986    match expr {
1987        Expr::Int(value) => Some(*value),
1988        Expr::ToOwned(inner) | Expr::Borrow(inner) | Expr::BorrowMut(inner) => {
1989            eval_const_int_expr(inner)
1990        }
1991        Expr::Neg(inner) => eval_const_int_expr(inner)?.checked_neg(),
1992        Expr::Add(lhs, rhs) => eval_const_int_expr(lhs)?.checked_add(eval_const_int_expr(rhs)?),
1993        Expr::Sub(lhs, rhs) => eval_const_int_expr(lhs)?.checked_sub(eval_const_int_expr(rhs)?),
1994        Expr::Mul(lhs, rhs) => eval_const_int_expr(lhs)?.checked_mul(eval_const_int_expr(rhs)?),
1995        Expr::Div(lhs, rhs) => {
1996            let rhs = eval_const_int_expr(rhs)?;
1997            if rhs == 0 {
1998                return None;
1999            }
2000            eval_const_int_expr(lhs)?.checked_div(rhs)
2001        }
2002        _ => None,
2003    }
2004}