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(¬_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(¬_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(¬_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(¬_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}