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