1use crate::cold_path;
5use crate::compiler::compiler_data::InstrSrc;
6use crate::compiler::compiler_data::Source;
7use crate::compiler::compiler_data::TypeNames;
8use crate::compiler::compiler_errors::error_cannot_find_dynlib_symbol;
9use crate::compiler::compiler_errors::error_cannot_load_dynlib;
10use crate::compiler::compiler_errors::error_cannot_push_type_to_array;
11use crate::compiler::compiler_errors::error_cannot_read_file;
12use crate::compiler::compiler_errors::error_conditional_expression_without_else;
13use crate::compiler::compiler_errors::error_division_by_zero;
14use crate::compiler::compiler_errors::error_duplicate_map_key;
15use crate::compiler::compiler_errors::error_invalid_index_type;
16use crate::compiler::compiler_errors::error_invalid_type;
17use crate::compiler::compiler_errors::error_map_diff_types;
18use crate::compiler::compiler_errors::error_non_bool_condition;
19use crate::compiler::compiler_errors::error_not_literal_map_key;
20use crate::compiler::compiler_errors::error_range_invalid_type;
21use crate::compiler::compiler_errors::error_shift_count_out_of_range;
22use crate::compiler::compiler_errors::error_type_arg_count;
23use crate::compiler::compiler_errors::error_type_has_no_c_representation;
24use crate::compiler::compiler_errors::error_type_not_indexable;
25use crate::compiler::compiler_errors::error_unknown_namespace;
26use crate::compiler::imports::ImportResolver;
27use crate::data::NULL;
28use crate::instr::LibFunc;
29use crate::parser;
30use crate::rt::FnValue;
31use crate::rt::LibraryOrigin;
32use crate::rt::TargetOs;
33use crate::rt::resolve_library_filename;
34use crate::vm::CandelaMap;
35use crate::vm::Pool;
36use crate::vm::StringPool;
37use crate::vm::map_insert;
38use crate::vm::shift_count_in_range;
39use crate::{data::Data, instr::Instr};
40use compiler_data::Ctx;
41use compiler_data::DynamicLibFn;
42use compiler_data::Dynamiclib;
43use compiler_data::EnumType;
44use compiler_data::EnumVariant;
45use compiler_data::FnGenerics;
46use compiler_data::FnSignature;
47use compiler_data::Function;
48use compiler_data::HostFnSig;
49use compiler_data::IndirectRegisters;
50use compiler_data::Pools;
51use compiler_data::ReturnCheck;
52use compiler_data::State;
53use compiler_data::Struct;
54use compiler_data::Variable;
55use expr::Expr;
56use expr::ImportBinding;
57use expr::LetAnnotation;
58use expr::METHOD_SEP;
59use expr::Span;
60use expr::StructRest;
61use expr::UNARY_MINUS_METHOD;
62use expr::code_captures_variable;
63use expr::code_modifies_variable;
64use expr::mangle_method;
65use functions::handle_functions;
66use functions::handle_value_call;
67use functions::user_functions::body_being_compiled;
68use functions::user_functions::check_declared_fn;
69use functions::user_functions::declared_holds_fn_signature;
70use functions::user_functions::ensure_indirect_impl;
71use functions::user_functions::handle_user_function;
72use methods::compile_operator_method;
73use methods::handle_method_calls;
74use registers::int_immediate;
75use registers::move_reg_to_reg;
76use registers::move_to_id;
77pub(crate) use registers::use_immediates;
78use rustc_hash::FxHashMap;
79use rustc_hash::FxHashSet;
80use smol_strc::SmolStr;
81use smol_strc::ToSmolStr;
82use std::cell::RefCell;
83use std::hash::BuildHasherDefault;
84use std::hint::unreachable_unchecked;
85use std::path::{Path, PathBuf};
86use std::rc::Rc;
87use type_system::DataType;
88use type_system::FnTypeExpr;
89use type_system::Generics;
90use type_system::ReturnAnnotation;
91use type_system::STRUCT_DEFAULT_METHOD;
92use type_system::TypeCtx;
93use type_system::TypeExpr;
94use type_system::TypeParams;
95use type_system::bare_fn_value_type;
96use type_system::check_if_returns_void;
97use type_system::check_operator_method;
98use type_system::check_struct_default_fn;
99use type_system::collect_direct_fn_calls;
100use type_system::infer_user_fn_return_type;
101use type_system::let_type_fits;
102use type_system::literal_struct_id;
103use type_system::param_type_matches;
104use type_system::pin_inserted_entry;
105use type_system::qualify_duplicate_type_names;
106use type_system::resolve_generic_variant;
107use type_system::struct_field_type_matches;
108use type_system::struct_of_default_fn;
109use type_system::variant_constructor_at;
110
111#[cfg(not(target_arch = "wasm32"))]
112use libloading::Library;
113
114#[cfg(target_arch = "wasm32")]
115use crate::errors::wasm_error;
116
117#[cfg(not(target_arch = "wasm32"))]
121type LibHandle = Option<Rc<Library>>;
122#[cfg(target_arch = "wasm32")]
123type LibHandle = ();
124
125thread_local! {
126 static CHECK_ONLY: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
128}
129
130struct CheckOnlyGuard(bool);
133
134impl Drop for CheckOnlyGuard {
135 fn drop(&mut self) {
136 CHECK_ONLY.set(self.0);
137 }
138}
139
140pub fn check_only_scope<R>(f: impl FnOnce() -> R) -> R {
147 let _guard = CheckOnlyGuard(CHECK_ONLY.replace(true));
148 f()
149}
150
151#[cfg(not(target_arch = "wasm32"))]
153fn is_check_only() -> bool {
154 CHECK_ONLY.get()
155}
156
157type PendingDylib = (
162 u16,
163 u16,
164 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
165 LibHandle,
166 (SmolStr, LibraryOrigin),
167 Span,
168);
169pub mod compiler_data;
170mod compiler_errors;
171pub mod imports;
172mod std_index;
173pub mod type_system;
174
175pub mod expr;
176
177#[path = "functions/functions.rs"]
178mod functions;
179#[path = "functions/methods.rs"]
180mod methods;
181
182mod copies;
183pub(crate) mod flow;
184mod fold;
185pub(crate) mod function_table;
186mod registers;
187mod scalar;
188pub(crate) mod type_test;
189
190pub trait UnwrapId {
191 fn unwrap_id(self) -> u16;
192}
193
194impl UnwrapId for Option<u16> {
195 #[inline(always)]
196 fn unwrap_id(self) -> u16 {
197 debug_assert!(self.is_some());
198 unsafe { self.unwrap_unchecked() }
199 }
200}
201
202fn add_cmp_false(condition_id: u16, len: &mut u16, output: &mut Vec<Instr>, jmp_backwards: bool) {
204 if output.is_empty() {
205 return output.push(Instr::IsFalseJmp(condition_id, *len));
206 }
207 *output.last_mut().unwrap() = match *output.last().unwrap() {
208 Instr::InfFloat(o1, o2, o3) if o3 == condition_id => Instr::SupEqFloatJmp(o1, o2, *len),
209 Instr::InfInt(o1, o2, o3) if o3 == condition_id => Instr::SupEqIntJmp(o1, o2, *len),
210 Instr::InfEqFloat(o1, o2, o3) if o3 == condition_id => Instr::SupFloatJmp(o1, o2, *len),
211 Instr::InfEqInt(o1, o2, o3) if o3 == condition_id => Instr::SupIntJmp(o1, o2, *len),
212 Instr::SupFloat(o1, o2, o3) if o3 == condition_id => Instr::InfEqFloatJmp(o1, o2, *len),
213 Instr::SupInt(o1, o2, o3) if o3 == condition_id => Instr::InfEqIntJmp(o1, o2, *len),
214 Instr::SupEqFloat(o1, o2, o3) if o3 == condition_id => Instr::InfFloatJmp(o1, o2, *len),
215 Instr::SupEqInt(o1, o2, o3) if o3 == condition_id => Instr::InfIntJmp(o1, o2, *len),
216 Instr::Eq(o1, o2, o3) if o3 == condition_id => Instr::NotEqJmp(o1, o2, *len),
217 Instr::ObjEq(o1, o2, o3) if o3 == condition_id => Instr::ObjNotEqJmp(o1, o2, *len),
218 Instr::StrEq(o1, o2, o3) if o3 == condition_id => Instr::StrNotEqJmp(o1, o2, *len),
219 Instr::NotEq(o1, o2, o3) if o3 == condition_id => Instr::EqJmp(o1, o2, *len),
220 Instr::ObjNotEq(o1, o2, o3) if o3 == condition_id => Instr::ObjEqJmp(o1, o2, *len),
221 Instr::StrNotEq(o1, o2, o3) if o3 == condition_id => Instr::StrEqJmp(o1, o2, *len),
222 _ => {
223 output.push(Instr::IsFalseJmp(condition_id, *len));
224 return;
225 }
226 };
227 if jmp_backwards {
228 *len -= 1;
229 }
230}
231
232#[inline(always)]
234fn add_cmp_true(condition_id: u16, output: &mut Vec<Instr>) {
235 if output.is_empty() {
236 return output.push(Instr::IsTrueJmp(condition_id, 0));
237 }
238 let new_instr = match *output.last().unwrap() {
239 Instr::InfFloat(o1, o2, o3) if o3 == condition_id => Instr::InfFloatJmp(o1, o2, 0),
240 Instr::InfInt(o1, o2, o3) if o3 == condition_id => Instr::InfIntJmp(o1, o2, 0),
241 Instr::InfEqFloat(o1, o2, o3) if o3 == condition_id => Instr::InfEqFloatJmp(o1, o2, 0),
242 Instr::InfEqInt(o1, o2, o3) if o3 == condition_id => Instr::InfEqIntJmp(o1, o2, 0),
243 Instr::SupFloat(o1, o2, o3) if o3 == condition_id => Instr::SupFloatJmp(o1, o2, 0),
244 Instr::SupInt(o1, o2, o3) if o3 == condition_id => Instr::SupIntJmp(o1, o2, 0),
245 Instr::SupEqFloat(o1, o2, o3) if o3 == condition_id => Instr::SupEqFloatJmp(o1, o2, 0),
246 Instr::SupEqInt(o1, o2, o3) if o3 == condition_id => Instr::SupEqIntJmp(o1, o2, 0),
247 Instr::Eq(o1, o2, o3) if o3 == condition_id => Instr::EqJmp(o1, o2, 0),
248 Instr::ObjEq(o1, o2, o3) if o3 == condition_id => Instr::ObjEqJmp(o1, o2, 0),
249 Instr::StrEq(o1, o2, o3) if o3 == condition_id => Instr::StrEqJmp(o1, o2, 0),
250 Instr::NotEq(o1, o2, o3) if o3 == condition_id => Instr::NotEqJmp(o1, o2, 0),
251 Instr::ObjNotEq(o1, o2, o3) if o3 == condition_id => Instr::ObjNotEqJmp(o1, o2, 0),
252 Instr::StrNotEq(o1, o2, o3) if o3 == condition_id => Instr::StrNotEqJmp(o1, o2, 0),
253 _ => {
254 output.push(Instr::IsTrueJmp(condition_id, 0));
255 return;
256 }
257 };
258 *output.last_mut().unwrap() = new_instr;
259}
260
261#[inline(always)]
263const fn set_jmp_size(instr: &mut Instr, size: u16) {
264 match instr {
265 Instr::IsFalseJmp(_, jump_size)
266 | Instr::IsTrueJmp(_, jump_size)
267 | Instr::Jmp(jump_size)
268 | Instr::SupEqFloatJmp(_, _, jump_size)
269 | Instr::SupEqIntJmp(_, _, jump_size)
270 | Instr::SupFloatJmp(_, _, jump_size)
271 | Instr::SupIntJmp(_, _, jump_size)
272 | Instr::InfEqFloatJmp(_, _, jump_size)
273 | Instr::InfEqIntJmp(_, _, jump_size)
274 | Instr::InfFloatJmp(_, _, jump_size)
275 | Instr::InfIntJmp(_, _, jump_size)
276 | Instr::InfIntJmpBack(_, _, jump_size)
277 | Instr::StepIntJmpBack(_, _, jump_size)
278 | Instr::NotEqJmp(_, _, jump_size)
279 | Instr::EqJmp(_, _, jump_size)
280 | Instr::ObjNotEqJmp(_, _, jump_size)
281 | Instr::ObjEqJmp(_, _, jump_size)
282 | Instr::StrNotEqJmp(_, _, jump_size)
283 | Instr::StrEqJmp(_, _, jump_size) => *jump_size = size,
284 _ => unsafe { unreachable_unchecked() },
285 }
286}
287
288fn compile_condition_operand(
296 expr: &Expr,
297 condition_span: Span,
298 v: &mut Vec<Variable>,
299 ctx: Ctx,
300 state: &mut State<'_>,
301 output: &mut Vec<Instr>,
302) -> u16 {
303 let condition_type = expr.infer_type(v, ctx, state);
304 match condition_type {
305 DataType::Bool => expr
306 .compile(v, ctx, state, output, None, false, true)
307 .unwrap_id(),
308 DataType::Unknown => {
309 let cond_id = expr
310 .compile(v, ctx, state, output, None, false, true)
311 .unwrap_id();
312 state.free_reg(cond_id, v);
313 let checked_id = state.alloc_reg();
314 output.push(Instr::CallLibFunc(LibFunc::AsBoolVal, cond_id, checked_id));
315 checked_id
316 }
317 other => error_non_bool_condition(
318 condition_span,
319 &other,
320 ctx.file_idx,
321 state.sources,
322 state.type_names(),
323 ),
324 }
325}
326
327#[allow(clippy::too_many_arguments)]
332fn compile_short_circuit_condition(
333 expr: &Expr,
334 condition_span: Span,
335 v: &mut Vec<Variable>,
336 ctx: Ctx,
337 state: &mut State<'_>,
338 output: &mut Vec<Instr>,
339 bool_or_mode: bool,
340) -> (Vec<usize>, Vec<usize>) {
341 let v_len = v.len();
346 match expr {
347 Expr::BoolOr(left, right, left_span, right_span) => {
348 let (mut true_jumps, left_false) =
350 compile_short_circuit_condition(left, *left_span, v, ctx, state, output, true);
351 v.truncate(v_len);
352 let right_start = output.len();
355 for j in left_false {
356 set_jmp_size(&mut output[j], (right_start - j) as u16);
357 }
358 let (right_true, right_false) = compile_short_circuit_condition(
359 right,
360 *right_span,
361 v,
362 ctx,
363 state,
364 output,
365 bool_or_mode,
366 );
367 true_jumps.extend(right_true);
368 v.truncate(v_len);
369 (true_jumps, right_false)
370 }
371 Expr::BoolAnd(left, right, left_span, right_span) => {
372 if bool_or_mode {
373 let (_, left_false) =
379 compile_short_circuit_condition(left, *left_span, v, ctx, state, output, false);
380 let (right_true, _) = compile_short_circuit_condition(
381 right,
382 *right_span,
383 v,
384 ctx,
385 state,
386 output,
387 true,
388 );
389 let fallthrough = output.len();
390 for j in left_false {
391 set_jmp_size(&mut output[j], (fallthrough - j) as u16);
392 }
393 v.truncate(v_len);
394 (right_true, Vec::new())
395 } else {
396 let (left_true, mut false_jumps) =
398 compile_short_circuit_condition(left, *left_span, v, ctx, state, output, false);
399 let right_start = output.len();
403 for j in left_true {
404 set_jmp_size(&mut output[j], (right_start - j) as u16);
405 }
406 let (right_true, right_false) = compile_short_circuit_condition(
407 right,
408 *right_span,
409 v,
410 ctx,
411 state,
412 output,
413 false,
414 );
415 false_jumps.extend(right_false);
416 (right_true, false_jumps)
417 }
418 }
419 Expr::Is(operand, target, operand_span, target_span) if !bool_or_mode => {
422 let (cond_id, facts) = type_test::compile_is(
423 operand,
424 target,
425 *operand_span,
426 *target_span,
427 None,
428 true,
429 v,
430 ctx,
431 state,
432 output,
433 );
434 add_cmp_false(cond_id, &mut 0, output, false);
435 state.free_reg(cond_id, v);
436 let false_jump = output.len() - 1;
437 facts.apply(v, state, output);
438 (Vec::new(), vec![false_jump])
439 }
440 expr => {
441 let cond_id = compile_condition_operand(expr, condition_span, v, ctx, state, output);
442 if bool_or_mode {
443 add_cmp_true(cond_id, output);
444 state.free_reg(cond_id, v);
445 (vec![output.len() - 1], Vec::new())
446 } else {
447 add_cmp_false(cond_id, &mut 0, output, false);
448 state.free_reg(cond_id, v);
449 (Vec::new(), vec![output.len() - 1])
450 }
451 }
452 }
453}
454
455fn parse_loop_flow_control(
456 loop_code: &mut [Instr],
457 loop_id: u16,
458 code_length: u16,
459 for_loop: bool,
460 indefinite: bool,
461) {
462 loop_code.iter_mut().enumerate().for_each(|(i, x)| {
463 if let Instr::NotEqJmp(break_id, 0, 0) = x
464 && *break_id == loop_id
465 {
466 if for_loop && !indefinite {
467 *x = Instr::Jmp(code_length - i as u16 - 1);
468 } else {
469 *x = Instr::Jmp(code_length - i as u16);
470 }
471 } else if let Instr::EqJmp(continue_id, 0, 0) = x
472 && *continue_id == loop_id
473 {
474 if for_loop {
478 *x = Instr::Jmp(code_length - i as u16 - 2);
479 } else {
480 *x = Instr::Jmp(code_length - i as u16 - 1);
481 }
482 }
483 });
484}
485
486pub(crate) fn compile_fn_value(
493 expr: &Expr,
494 v: &mut Vec<Variable>,
495 ctx: Ctx,
496 state: &mut State<'_>,
497 output: &mut Vec<Instr>,
498 tgt_id: Option<u16>,
499) -> u16 {
500 if let DataType::Fn(fn_id) = expr.infer_type(v, ctx, state)
501 && state.fns[fn_id as usize].captures.is_empty()
502 {
503 return compile_fn_entry_value(fn_id as usize, v, ctx, state, output, tgt_id);
504 }
505 expr.compile(v, ctx, state, output, tgt_id, false, true)
506 .unwrap_id()
507}
508
509pub(crate) fn compile_fn_entry_value(
518 fn_id: usize,
519 v: &mut Vec<Variable>,
520 ctx: Ctx,
521 state: &mut State<'_>,
522 output: &mut Vec<Instr>,
523 tgt_id: Option<u16>,
524) -> u16 {
525 if let Some(params) = state.fns[fn_id].declared_params()
526 && !state.fns[fn_id]
527 .impls
528 .iter()
529 .any(|fn_impl| fn_impl.indirect)
530 {
531 let span = state.fns[fn_id].name_span;
532 ensure_indirect_impl(fn_id, ¶ms, span, output, v, ctx, state);
533 }
534 let entry_id = state.fn_entry_register(fn_id);
535 let value_id = state.alloc_reg_tgt(tgt_id);
536 output.push(Instr::EmptyFnValue(value_id));
537 output.push(Instr::Push(value_id, entry_id));
538 value_id
539}
540
541fn holds_functions(types: &[DataType]) -> bool {
546 types
547 .iter()
548 .any(|t| matches!(t, DataType::Fn(_) | DataType::FnValue(_)))
549}
550
551fn compile_element(
554 elem: &Expr,
555 as_fn_value: bool,
556 v: &mut Vec<Variable>,
557 ctx: Ctx,
558 state: &mut State<'_>,
559 output: &mut Vec<Instr>,
560) -> u16 {
561 if as_fn_value {
562 compile_fn_value(elem, v, ctx, state, output, None)
563 } else {
564 elem.compile(v, ctx, state, output, None, false, true)
565 .unwrap_id()
566 }
567}
568
569#[inline(always)]
570fn compile_array_literal(
571 array_items: &[Expr],
572 spans: &[Span],
573 v: &mut Vec<Variable>,
574 ctx: Ctx,
575 state: &mut State<'_>,
576 output: &mut Vec<Instr>,
577) -> u16 {
578 if let Some(first) = array_items.first() {
579 let first_type = first.infer_type(v, ctx, state);
580 if let Some(failing_elem_idx) = array_items
581 .iter()
582 .skip(1)
583 .position(|x| x.infer_type(v, ctx, state) != first_type)
584 {
585 let failing_elem_type = array_items[failing_elem_idx + 1].infer_type(v, ctx, state);
586 let failing_elem_span = spans[failing_elem_idx + 2];
587 compiler_errors::error_array_diff_types(
588 ctx.file_idx,
589 state.sources,
590 spans[1],
591 &first_type,
592 failing_elem_span,
593 &failing_elem_type,
594 state.type_names(),
595 )
596 }
597 }
598 let elem_types = array_items
602 .iter()
603 .map(|elem| elem.infer_type(v, ctx, state))
604 .collect::<Vec<DataType>>();
605 let as_fn_values = holds_functions(&elem_types);
606 let array_id = {
607 state.pools.objs.push(Vec::with_capacity(array_items.len()));
608 state.pools.objs.len() - 1
609 };
610 if array_items.is_empty() && !ctx.single_run {
611 let array_reg = {
612 state.registers.push(Data::array(array_id as u32));
613 state.registers.len() - 1
614 } as u16;
615 output.push(Instr::EmptyArray(array_reg));
616 return array_reg;
617 }
618 if ctx.single_run {
619 for elem in array_items {
620 let id = compile_element(elem, as_fn_values, v, ctx, state, output);
621 if elem.is_constant_literal() {
622 state
623 .pools
624 .objs
625 .get_mut(array_id)
626 .push(state.registers[id as usize]);
627 } else {
628 output.push(Instr::ObjElemMov(
629 id,
630 array_id as u16,
631 state.pools.objs[array_id].len() as u16,
632 ));
633 state.pools.objs.get_mut(array_id).push(NULL);
634 }
635 }
636 state.registers.push(Data::array(array_id as u32));
637 (state.registers.len() - 1) as u16
638 } else {
639 let mut constant_array = true;
641 let mut elem_ids: Vec<u16> = Vec::with_capacity(array_items.len());
642 for elem in array_items {
643 let id = compile_element(elem, as_fn_values, v, ctx, state, output);
644 if elem.is_constant_literal() {
645 state
646 .pools
647 .objs
648 .get_mut(array_id)
649 .push(state.registers[id as usize]);
650 } else {
651 constant_array = false;
652 state.pools.objs.get_mut(array_id).push(NULL);
653 }
654 elem_ids.push(id);
655 }
656
657 if constant_array {
658 let template_reg = state.template_register(Data::array(array_id as u32));
660 let dest_reg = {
670 state.registers.push(Data::array(array_id as u32));
671 (state.registers.len() - 1) as u16
672 };
673 output.push(Instr::CloneArray(
674 template_reg,
675 dest_reg,
676 state.pools.objs[array_id].len() as u16,
677 ));
678 dest_reg
679 } else {
680 let dest_reg = {
687 state.registers.push(Data::array(array_id as u32));
688 (state.registers.len() - 1) as u16
689 };
690 output.push(Instr::EmptyArray(dest_reg));
691 for elem_reg in elem_ids {
692 output.push(Instr::Push(dest_reg, elem_reg));
693 }
694 dest_reg
695 }
696 }
697}
698
699#[allow(clippy::too_many_arguments)]
705fn compile_struct_field_type(
706 name: &SmolStr,
707 struct_idx: usize,
708 field_idx: usize,
709 field_expr: &Expr,
710 field_value_span: Span,
711 v: &mut Vec<Variable>,
712 ctx: Ctx,
713 state: &mut State<'_>,
714 output: &mut Vec<Instr>,
715) -> bool {
716 let field_type = field_expr.infer_type(v, ctx, state);
717 let declared = state.structs[struct_idx].fields[field_idx].1.clone();
718 let as_fn_value = declared_holds_fn_signature(&declared);
719 if as_fn_value {
720 check_declared_fn(
721 field_expr,
722 &declared,
723 field_value_span,
724 output,
725 v,
726 ctx,
727 state,
728 );
729 }
730 let as_fn_value = as_fn_value && matches!(declared, DataType::FnValue(_));
731 let field_type = if as_fn_value && matches!(field_type, DataType::Fn(_) | DataType::FnValue(_))
734 {
735 declared
736 } else {
737 field_type
738 };
739 let field = &state.structs[struct_idx].fields[field_idx];
740 if !struct_field_type_matches(&field.1, &field_type, state.generics) {
741 compiler_errors::error_struct_field_invalid_type(
742 ctx.file_idx,
743 name,
744 field.2,
745 &field.0,
746 &field.1,
747 field_value_span,
748 &field_type,
749 state.sources,
750 state.type_names(),
751 );
752 }
753 as_fn_value
754}
755
756#[derive(Clone, Copy)]
758enum StructFill {
759 Nothing,
761 Base(u16),
763 Defaults,
766}
767
768#[allow(clippy::too_many_arguments)]
769fn compile_struct_literal(
770 namespace: &[SmolStr],
771 fields: &[(SmolStr, Expr, Span, Span)],
772 type_args: &[TypeExpr],
773 base: Option<&(StructRest, Span)>,
774 span: Span,
775 v: &mut Vec<Variable>,
776 ctx: Ctx,
777 state: &mut State<'_>,
778 output: &mut Vec<Instr>,
779) -> u16 {
780 let expected_struct_idx = literal_struct_id(
781 namespace,
782 fields,
783 type_args,
784 base.and_then(|(b, _)| b.base()),
785 span,
786 v,
787 ctx,
788 state,
789 ) as usize;
790 let name = state.structs[expected_struct_idx].name.clone();
791 let expected_fields_len = state.structs[expected_struct_idx].fields.len();
792 if base.is_some() {
793 if let Some(unexpected_field) = fields.iter().find(|(f, _, _, _)| {
796 !state.structs[expected_struct_idx]
797 .fields
798 .iter()
799 .any(|(declared, _, _)| declared == f)
800 }) {
801 compiler_errors::error_struct_no_such_field(
802 ctx.file_idx,
803 &name,
804 state.structs[expected_struct_idx].name_span,
805 unexpected_field.2,
806 &unexpected_field.0,
807 state.sources,
808 )
809 }
810 } else if expected_fields_len < fields.len() {
811 let unexpected_field = &fields[expected_fields_len];
812 compiler_errors::error_struct_no_such_field(
813 ctx.file_idx,
814 &name,
815 state.structs[expected_struct_idx].name_span,
816 unexpected_field.2,
817 &unexpected_field.0,
818 state.sources,
819 )
820 }
821 let fill = match base {
822 None => StructFill::Nothing,
823 Some((StructRest::Default, _)) => {
824 match user_struct_default(expected_struct_idx as u16, state) {
825 Some(fn_id) => StructFill::Base(call_user_struct_default(
826 fn_id,
827 expected_struct_idx as u16,
828 span,
829 v,
830 ctx,
831 state,
832 output,
833 )),
834 None => StructFill::Defaults,
835 }
836 }
837 Some((StructRest::Base(base), base_span)) => {
838 let base_type = base.infer_type(v, ctx, state);
839 if base_type != DataType::Struct(state.structs[expected_struct_idx].id) {
840 compiler_errors::error_struct_base_type(
841 ctx.file_idx,
842 &name,
843 *base_span,
844 &base_type,
845 state.sources,
846 state.type_names(),
847 );
848 }
849 StructFill::Base(
850 base.compile(v, ctx, state, output, None, false, true)
851 .unwrap_id(),
852 )
853 }
854 };
855 let dest = build_struct(
856 expected_struct_idx,
857 fields,
858 fill,
859 span,
860 v,
861 ctx,
862 state,
863 output,
864 );
865 if let StructFill::Base(base_reg) = fill {
866 state.free_reg(base_reg, v);
867 }
868 dest
869}
870
871fn user_struct_default(struct_idx: u16, state: &State<'_>) -> Option<usize> {
877 let name = mangle_method(
878 &state.structs[struct_idx as usize].name,
879 STRUCT_DEFAULT_METHOD,
880 );
881 let fn_id = state.fns.iter().position(|f| f.name == name)?;
882 (body_being_compiled() != Some(fn_id)).then_some(fn_id)
883}
884
885fn call_user_struct_default(
889 fn_id: usize,
890 struct_idx: u16,
891 span: Span,
892 v: &mut Vec<Variable>,
893 ctx: Ctx,
894 state: &mut State<'_>,
895 output: &mut Vec<Instr>,
896) -> u16 {
897 let name = state.fns[fn_id].name.clone();
898 check_struct_default_fn(
899 &state.fns[fn_id],
900 struct_idx,
901 None,
902 state.sources,
903 state.type_names(),
904 );
905 let (returned, returned_span) = match state.fns[fn_id].return_type.clone() {
906 Some(declared) => declared,
907 None => (
908 infer_user_fn_return_type(fn_id, &[], &[], &name, v, ctx, state),
909 state.fns[fn_id].name_span,
910 ),
911 };
912 check_struct_default_fn(
913 &state.fns[fn_id],
914 struct_idx,
915 Some((&returned, returned_span)),
916 state.sources,
917 state.type_names(),
918 );
919 handle_user_function(
920 &name,
921 fn_id,
922 output,
923 v,
924 ctx,
925 state,
926 None,
927 &[],
928 span,
929 &[],
930 &[],
931 None,
932 )
933 .unwrap_id()
934}
935
936pub(crate) fn compile_struct_default(
940 struct_idx: u16,
941 span: Span,
942 v: &mut Vec<Variable>,
943 ctx: Ctx,
944 state: &mut State<'_>,
945 output: &mut Vec<Instr>,
946) -> u16 {
947 thread_local! {
948 static DEFAULTING: RefCell<Vec<u16>> = const { RefCell::new(Vec::new()) };
951 }
952 struct Leave;
954 impl Drop for Leave {
955 fn drop(&mut self) {
956 DEFAULTING.with(|d| d.borrow_mut().pop());
957 }
958 }
959 if let Some(fn_id) = user_struct_default(struct_idx, state) {
960 return call_user_struct_default(fn_id, struct_idx, span, v, ctx, state, output);
961 }
962 if DEFAULTING.with(|d| d.borrow().contains(&struct_idx)) {
963 let s = &state.structs[struct_idx as usize];
964 let (field_name, field_type, _) = s
965 .fields
966 .iter()
967 .find(|(_, t, _)| matches!(t, DataType::Struct(_)))
968 .cloned()
969 .unwrap_or_else(|| (s.name.clone(), DataType::Struct(struct_idx), span));
970 compiler_errors::error_struct_field_no_default(
971 ctx.file_idx,
972 span,
973 &s.name,
974 &field_name,
975 &field_type,
976 state.sources,
977 state.type_names(),
978 );
979 }
980 DEFAULTING.with(|d| d.borrow_mut().push(struct_idx));
981 let _leave = Leave;
982 build_struct(
983 struct_idx as usize,
984 &[],
985 StructFill::Defaults,
986 span,
987 v,
988 ctx,
989 state,
990 output,
991 )
992}
993
994fn zero_value(field_type: &DataType, span: Span) -> Option<Expr> {
998 Some(match field_type {
999 DataType::Int => Expr::Int(0),
1000 DataType::Float => Expr::Float(0.0),
1001 DataType::Bool => Expr::Bool(false),
1002 DataType::String => Expr::String(SmolStr::default()),
1003 DataType::Null | DataType::Unknown => Expr::Null,
1004 DataType::Union(members) if members.contains(&DataType::Null) => Expr::Null,
1005 DataType::Array(_) => Expr::Array(Box::from([]), Box::from([span])),
1006 DataType::Map(_) => Expr::Map(Box::from([]), span),
1007 DataType::Struct(id) => Expr::StructDefault(*id, span),
1008 DataType::Union(_) | DataType::Enum(_) | DataType::Fn(_) | DataType::FnValue(_) => {
1009 return None;
1010 }
1011 })
1012}
1013
1014#[allow(clippy::too_many_arguments)]
1017fn build_struct(
1018 struct_idx: usize,
1019 fields: &[(SmolStr, Expr, Span, Span)],
1020 fill: StructFill,
1021 span: Span,
1022 v: &mut Vec<Variable>,
1023 ctx: Ctx,
1024 state: &mut State<'_>,
1025 output: &mut Vec<Instr>,
1026) -> u16 {
1027 let name = state.structs[struct_idx].name.clone();
1028 let type_id = state.structs[struct_idx].id;
1029 let expected_fields_len = state.structs[struct_idx].fields.len();
1030 let pool_idx = {
1031 state
1032 .pools
1033 .objs
1034 .push(Vec::with_capacity(expected_fields_len));
1035 state.pools.objs.len() - 1
1036 };
1037 let mut dynamic: Vec<(u16, u16)> = Vec::with_capacity(expected_fields_len);
1041 let mut scratch: Vec<u16> = Vec::new();
1042 for field_idx in 0..expected_fields_len {
1043 let field_name = state.structs[struct_idx].fields[field_idx].0.clone();
1044 let written = fields.iter().find(|(f, _, _, _)| *f == field_name);
1045 let (id, constant) = match (written, fill) {
1046 (Some((_, field_expr, _, field_value_span)), _) => {
1047 let as_fn_value = compile_struct_field_type(
1048 &name,
1049 struct_idx,
1050 field_idx,
1051 field_expr,
1052 *field_value_span,
1053 v,
1054 ctx,
1055 state,
1056 output,
1057 );
1058 let id = compile_element(field_expr, as_fn_value, v, ctx, state, output);
1059 (id, field_expr.is_constant_literal())
1060 }
1061 (None, StructFill::Base(base_reg)) => {
1062 let id = state.alloc_reg();
1063 output.push(Instr::GetFieldStruct(base_reg, field_idx as u16, id));
1064 scratch.push(id);
1065 (id, false)
1066 }
1067 (None, StructFill::Defaults) => {
1068 let field_type = state.structs[struct_idx].fields[field_idx].1.clone();
1069 let (file_idx, value, value_span) = match state
1073 .generics
1074 .struct_default(type_id, field_idx)
1075 {
1076 Some((file_idx, value, value_span)) => (file_idx, value.clone(), value_span),
1077 None => (
1078 ctx.file_idx,
1079 zero_value(&field_type, span).unwrap_or_else(|| {
1080 compiler_errors::error_struct_field_no_default(
1081 ctx.file_idx,
1082 span,
1083 &name,
1084 &field_name,
1085 &field_type,
1086 state.sources,
1087 state.type_names(),
1088 )
1089 }),
1090 span,
1091 ),
1092 };
1093 let default_ctx = Ctx { file_idx, ..ctx };
1094 let mut scope: Vec<Variable> = Vec::new();
1095 let as_fn_value = compile_struct_field_type(
1096 &name,
1097 struct_idx,
1098 field_idx,
1099 &value,
1100 value_span,
1101 &mut scope,
1102 default_ctx,
1103 state,
1104 output,
1105 );
1106 let id =
1107 compile_element(&value, as_fn_value, &mut scope, default_ctx, state, output);
1108 (id, value.is_constant_literal())
1109 }
1110 (None, StructFill::Nothing) => {
1111 let missing_elems = state.structs[struct_idx]
1112 .fields
1113 .iter()
1114 .map(|(f, _, _)| f)
1115 .filter(|f| !fields.iter().any(|(written, _, _, _)| written == *f))
1116 .collect::<Vec<&SmolStr>>();
1117 compiler_errors::error_struct_missing_fields(
1118 ctx.file_idx,
1119 state.structs[struct_idx].name_span,
1120 span,
1121 state.sources,
1122 &missing_elems,
1123 )
1124 }
1125 };
1126 if constant {
1127 let value = state.registers[id as usize];
1128 state.pools.objs.get_mut(pool_idx).push(value);
1129 } else if ctx.single_run {
1130 output.push(Instr::ObjElemMov(
1131 id,
1132 pool_idx as u16,
1133 state.pools.objs[pool_idx].len() as u16,
1134 ));
1135 state.pools.objs.get_mut(pool_idx).push(NULL);
1136 } else {
1137 state.pools.objs.get_mut(pool_idx).push(NULL);
1138 dynamic.push((id, field_idx as u16));
1139 }
1140 }
1141
1142 let dest_reg = if ctx.single_run {
1143 state
1144 .registers
1145 .push(Data::struct_instance(type_id, pool_idx as u32));
1146 (state.registers.len() - 1) as u16
1147 } else {
1148 let template_reg = state.template_register(Data::struct_instance(type_id, pool_idx as u32));
1149 let dest_reg = {
1155 state
1156 .registers
1157 .push(Data::struct_instance(type_id, pool_idx as u32));
1158 (state.registers.len() - 1) as u16
1159 };
1160 output.push(Instr::CloneStruct(template_reg, dest_reg));
1161 for (val_reg, slot) in dynamic {
1162 output.push(Instr::SetFieldStruct(dest_reg, val_reg, slot));
1163 }
1164 dest_reg
1165 };
1166 for id in scratch {
1167 state.free_reg(id, v);
1168 }
1169 dest_reg
1170}
1171
1172pub(crate) fn resolve_enum_variant(
1179 path: &[SmolStr],
1180 file_idx: u16,
1181 state: &State<'_>,
1182) -> Option<(u16, u16)> {
1183 if path.len() >= 2 {
1184 let variant = &path[path.len() - 1];
1185 let enum_name = &path[path.len() - 2];
1186 let module = &path[..path.len() - 2];
1187 let eid = state.scope(file_idx).find_enum(module, enum_name)?;
1188 let vidx = state.enums[eid]
1189 .variants
1190 .iter()
1191 .position(|vt| &vt.name == variant)?;
1192 Some((eid as u16, vidx as u16))
1193 } else if let Some(name) = path.first() {
1194 for e in state.enums.iter() {
1195 if let Some(vidx) = e.variants.iter().position(|vt| &vt.name == name) {
1196 return Some((e.id, vidx as u16));
1197 }
1198 }
1199 None
1200 } else {
1201 None
1202 }
1203}
1204
1205pub(crate) fn variant_constructor(
1211 path: &[SmolStr],
1212 args: &[Expr],
1213 span: Span,
1214 v: &mut Vec<Variable>,
1215 ctx: Ctx,
1216 state: &mut State<'_>,
1217) -> Option<(u16, u16)> {
1218 let arg_types: Vec<DataType> = args.iter().map(|a| a.infer_type(v, ctx, state)).collect();
1219 variant_constructor_at(path, &arg_types, span, ctx, state)
1220}
1221
1222#[allow(clippy::too_many_arguments)]
1228pub(crate) fn compile_enum_construction(
1229 enum_id: u16,
1230 variant_idx: u16,
1231 args: &[Expr],
1232 span: Span,
1233 args_indexes: &[Span],
1234 v: &mut Vec<Variable>,
1235 ctx: Ctx,
1236 state: &mut State<'_>,
1237 output: &mut Vec<Instr>,
1238) -> u16 {
1239 let variant = &state.enums[enum_id as usize].variants[variant_idx as usize];
1240 let variant_name = variant.name.clone();
1241 let payload_types = variant.payload.clone();
1242 let arity = payload_types.len();
1243
1244 compiler_errors::check_args(
1245 args,
1246 arity,
1247 &variant_name,
1248 span,
1249 state.sources,
1250 ctx.file_idx,
1251 );
1252 for (i, expected) in payload_types.iter().enumerate() {
1257 let received = args[i].infer_type(v, ctx, state);
1258 if !param_type_matches(expected, &received, state.generics) {
1259 compiler_errors::error_function_arg_invalid_type(
1260 &received,
1261 expected,
1262 args_indexes[i],
1263 &variant_name,
1264 None,
1265 ctx.file_idx,
1266 state.sources,
1267 state.type_names(),
1268 );
1269 }
1270 }
1271
1272 let pool_idx = {
1273 state.pools.objs.push(Vec::with_capacity(arity + 1));
1274 state.pools.objs.len() - 1
1275 };
1276 state
1277 .pools
1278 .objs
1279 .get_mut(pool_idx)
1280 .push(Data::int(i64::from(variant_idx)));
1281
1282 let mut dynamic: Vec<(u16, u16)> = Vec::with_capacity(arity);
1283 for (i, arg) in args.iter().enumerate() {
1284 let id = arg
1285 .compile(v, ctx, state, output, None, false, true)
1286 .unwrap_id();
1287 if arg.is_constant_literal() {
1288 let d = state.registers[id as usize];
1289 state.pools.objs.get_mut(pool_idx).push(d);
1290 } else {
1291 state.pools.objs.get_mut(pool_idx).push(NULL);
1292 dynamic.push((id, (i + 1) as u16));
1293 }
1294 }
1295
1296 let template_reg = state.template_register(Data::enum_instance(enum_id, pool_idx as u32));
1297 let dest_reg = {
1307 state
1308 .registers
1309 .push(Data::enum_instance(enum_id, pool_idx as u32));
1310 (state.registers.len() - 1) as u16
1311 };
1312 output.push(Instr::CloneEnum(template_reg, dest_reg));
1313 for (val_reg, slot) in dynamic {
1314 output.push(Instr::SetFieldStruct(dest_reg, val_reg, slot));
1315 }
1316 dest_reg
1317}
1318
1319fn compile_enum_definition(
1323 name: &SmolStr,
1324 variants: &[(SmolStr, Box<[TypeExpr]>, Span)],
1325 span: Span,
1326 type_params: &TypeParams,
1327 ctx: Ctx,
1328 state: &mut State<'_>,
1329) {
1330 if !type_params.is_empty() {
1331 let template = state.generics.add_enum_template(
1332 name.clone(),
1333 type_params.clone(),
1334 ctx.file_idx,
1335 Box::from(variants),
1336 );
1337 state
1338 .scope_mut(ctx.file_idx)
1339 .symbols
1340 .push((name.clone(), SymbolKind::Template(template)));
1341 return;
1342 }
1343 let enum_id = state.enums.len() as u16;
1344 state.enums.push(EnumType {
1345 name: name.clone(),
1346 variants: Box::from([]),
1347 id: enum_id,
1348 name_span: span,
1349 });
1350 state
1351 .scope_mut(ctx.file_idx)
1352 .symbols
1353 .push((name.clone(), SymbolKind::Enum(enum_id)));
1354 let resolved = variants
1355 .iter()
1356 .map(|(vn, payload, vspan)| EnumVariant {
1357 name: vn.clone(),
1358 payload: payload
1359 .iter()
1360 .map(|t| t.to_datatype(&mut state.type_ctx(ctx.file_idx)))
1361 .collect(),
1362 name_span: *vspan,
1363 })
1364 .collect();
1365 state.enums[enum_id as usize].variants = resolved;
1366}
1367
1368pub(crate) fn resolve_variant_pattern(
1381 enum_id: u16,
1382 pattern: &Expr,
1383 fallback_span: Span,
1384 ctx: Ctx,
1385 state: &mut State<'_>,
1386) -> (u16, Vec<SmolStr>) {
1387 let (path, type_args, binders, span): (&[SmolStr], &[TypeExpr], Vec<SmolStr>, Span) =
1388 match pattern {
1389 Expr::Var(name, span) => (std::slice::from_ref(name), &[], Vec::new(), *span),
1390 Expr::NamespacedRef(path, span, type_args) => (path, type_args, Vec::new(), *span),
1391 Expr::FunctionCall(args, namespace, span, _, type_args) => {
1392 let mut binders = Vec::with_capacity(args.len());
1393 for arg in args {
1394 if let Expr::Var(binder, _) = arg {
1395 binders.push(binder.clone());
1396 } else {
1397 compiler_errors::error_enum(
1398 "Invalid match pattern",
1399 "Enum variant patterns may only bind identifiers, e.g. Circle(r)",
1400 *span,
1401 ctx.file_idx,
1402 state.sources,
1403 );
1404 }
1405 }
1406 (&**namespace, type_args, binders, *span)
1407 }
1408 _ => compiler_errors::error_enum(
1409 "Invalid match pattern",
1410 "A match on an enum expects variant patterns, e.g. Circle(r) or Unit",
1411 fallback_span,
1412 ctx.file_idx,
1413 state.sources,
1414 ),
1415 };
1416 let variant_name = path[path.len() - 1].clone();
1417 let variant_idx = if path.len() >= 2 {
1418 let (pattern_enum, variant_idx) = if type_args.is_empty() {
1419 resolve_qualified_pattern_enum(path, enum_id, span, ctx, state)
1420 } else {
1421 resolve_generic_variant(path, type_args, span, ctx, state)
1422 };
1423 if pattern_enum != enum_id {
1424 compiler_errors::error_pattern_enum_mismatch(
1425 &path.join("::"),
1426 &state.enums[pattern_enum as usize].name,
1427 &state.enums[enum_id as usize].name,
1428 span,
1429 ctx.file_idx,
1430 state.sources,
1431 );
1432 }
1433 variant_idx
1434 } else {
1435 let e = &state.enums[enum_id as usize];
1436 let Some(variant_idx) = e.variants.iter().position(|vt| vt.name == variant_name) else {
1437 compiler_errors::error_enum(
1438 "Unknown enum variant",
1439 &format!("{} is not a variant of enum {}", variant_name, e.name),
1440 span,
1441 ctx.file_idx,
1442 state.sources,
1443 );
1444 };
1445 variant_idx as u16
1446 };
1447 let e = &state.enums[enum_id as usize];
1448 let expected_arity = e.variants[variant_idx as usize].payload.len();
1449 if binders.len() != expected_arity {
1450 compiler_errors::error_enum(
1451 "Wrong variant payload arity",
1452 &format!(
1453 "Variant {} binds {} value(s) but the pattern has {}",
1454 variant_name,
1455 expected_arity,
1456 binders.len()
1457 ),
1458 span,
1459 ctx.file_idx,
1460 state.sources,
1461 );
1462 }
1463 (variant_idx, binders)
1464}
1465
1466fn resolve_qualified_pattern_enum(
1472 path: &[SmolStr],
1473 scrut_enum: u16,
1474 span: Span,
1475 ctx: Ctx,
1476 state: &State<'_>,
1477) -> (u16, u16) {
1478 let variant = &path[path.len() - 1];
1479 let enum_name = &path[path.len() - 2];
1480 let module = &path[..path.len() - 2];
1481 let Some(pattern_enum) = state.scope(ctx.file_idx).find_enum(module, enum_name) else {
1482 compiler_errors::error_enum(
1483 "Unknown enum",
1484 &format!(
1485 "{} does not name an enum in scope, and this match is on enum {}",
1486 path[..path.len() - 1].join("::"),
1487 state.enums[scrut_enum as usize].name
1488 ),
1489 span,
1490 ctx.file_idx,
1491 state.sources,
1492 );
1493 };
1494 let e = &state.enums[pattern_enum];
1495 let Some(variant_idx) = e.variants.iter().position(|vt| &vt.name == variant) else {
1496 compiler_errors::error_enum(
1497 "Unknown enum variant",
1498 &format!("{} is not a variant of enum {}", variant, e.name),
1499 span,
1500 ctx.file_idx,
1501 state.sources,
1502 );
1503 };
1504 (pattern_enum as u16, variant_idx as u16)
1505}
1506
1507const fn pattern_span(pattern: &Expr, fallback: Span) -> Span {
1510 match pattern {
1511 Expr::Var(_, span)
1512 | Expr::NamespacedRef(_, span, _)
1513 | Expr::FunctionCall(_, _, span, _, _) => *span,
1514 _ => fallback,
1515 }
1516}
1517
1518pub(crate) fn alternative_bindings(
1521 enum_id: u16,
1522 (variant_idx, binders): &(u16, Vec<SmolStr>),
1523 state: &State<'_>,
1524) -> Vec<(SmolStr, DataType)> {
1525 let payload = &state.enums[enum_id as usize].variants[*variant_idx as usize].payload;
1526 let mut bound: Vec<(SmolStr, DataType)> = binders
1527 .iter()
1528 .zip(payload.iter())
1529 .filter(|(binder, _)| binder.as_str() != "_")
1530 .map(|(binder, ty)| (binder.clone(), ty.clone()))
1531 .collect();
1532 bound.sort_by(|a, b| a.0.cmp(&b.0));
1533 bound
1534}
1535
1536pub(crate) fn resolve_arm_patterns(
1543 enum_id: u16,
1544 patterns: &[Expr],
1545 span: Span,
1546 ctx: Ctx,
1547 state: &mut State<'_>,
1548) -> Vec<(u16, Vec<SmolStr>)> {
1549 let alternatives: Vec<(u16, Vec<SmolStr>)> = patterns
1550 .iter()
1551 .map(|pattern| resolve_variant_pattern(enum_id, pattern, span, ctx, state))
1552 .collect();
1553 if let [first, rest @ ..] = alternatives.as_slice() {
1554 let expected = alternative_bindings(enum_id, first, state);
1555 for (k, alternative) in rest.iter().enumerate() {
1556 let found = alternative_bindings(enum_id, alternative, state);
1557 if found != expected {
1558 compiler_errors::error_pattern_bindings_differ(
1559 &expected,
1560 &found,
1561 pattern_span(&patterns[k + 1], span),
1562 ctx.file_idx,
1563 state.sources,
1564 state.type_names(),
1565 );
1566 }
1567 }
1568 }
1569 alternatives
1570}
1571
1572pub(crate) fn declare_arm_bindings(
1576 enum_id: u16,
1577 patterns: &[Expr],
1578 span: Span,
1579 v: &mut Vec<Variable>,
1580 ctx: Ctx,
1581 state: &mut State<'_>,
1582) {
1583 let alternatives = resolve_arm_patterns(enum_id, patterns, span, ctx, state);
1584 for (name, var_type) in alternative_bindings(enum_id, &alternatives[0], state) {
1585 v.push(Variable {
1586 declared: None,
1587 name,
1588 register_id: 0,
1589 cell: false,
1590 var_type,
1591 });
1592 }
1593}
1594
1595fn compile_arm_body(
1598 body: &[Expr],
1599 result: Option<(u16, bool)>,
1600 v: &mut Vec<Variable>,
1601 ctx: Ctx,
1602 state: &mut State<'_>,
1603 output: &mut Vec<Instr>,
1604) {
1605 if let Some((result, as_fn_value)) = result {
1606 compile_inline_condition_branch(body, v, ctx, state, output, result, as_fn_value);
1607 } else {
1608 let code = compile_expr(body, v, ctx.advance_offset(output.len() as u16), state);
1609 output.extend(code);
1610 }
1611}
1612
1613#[allow(clippy::too_many_arguments)]
1621fn compile_enum_match(
1622 enum_id: u16,
1623 scrutinee: &Expr,
1624 arms: &[(Box<[Expr]>, Box<[Expr]>)],
1625 wildcard: Option<&[Expr]>,
1626 span: Span,
1627 v: &mut Vec<Variable>,
1628 ctx: Ctx,
1629 state: &mut State<'_>,
1630 output: &mut Vec<Instr>,
1631 result: Option<(u16, bool)>,
1632) {
1633 let scrut_reg = scrutinee
1634 .compile(v, ctx, state, output, None, false, true)
1635 .unwrap_id();
1636 let v_base = v.len();
1639 v.push(Variable {
1640 declared: None,
1641 name: SmolStr::new_static("[MATCH SCRUT]"),
1642 register_id: scrut_reg,
1643 cell: false,
1644 var_type: DataType::Enum(enum_id),
1645 });
1646
1647 let tag_reg = state.alloc_reg();
1648 output.push(Instr::GetFieldStruct(scrut_reg, 0, tag_reg));
1649
1650 let variant_count = state.enums[enum_id as usize].variants.len();
1651 let mut covered = vec![false; variant_count];
1652 let mut false_jmps: Vec<usize> = Vec::with_capacity(arms.len());
1655 let mut alternative_starts: Vec<usize> = Vec::with_capacity(arms.len());
1656 let mut end_jmps: Vec<usize> = Vec::with_capacity(arms.len());
1657
1658 for (patterns, body) in arms {
1659 let alternatives = resolve_arm_patterns(enum_id, patterns, span, ctx, state);
1660 let mut slots: Vec<(SmolStr, u16, u16, DataType, bool)> = Vec::new();
1662 let mut to_body: Vec<usize> = Vec::new();
1663 for (k, (variant_idx, binders)) in alternatives.iter().enumerate() {
1664 covered[*variant_idx as usize] = true;
1665 alternative_starts.push(output.len());
1666 let idx_reg = state.alloc_reg();
1667 output.push(Instr::SetInt(idx_reg, i32::from(*variant_idx)));
1668 false_jmps.push(output.len());
1669 output.push(Instr::NotEqJmp(tag_reg, idx_reg, 0));
1670 state.free_reg(idx_reg, v);
1671
1672 for (i, binder) in binders.iter().enumerate() {
1675 if binder.as_str() == "_" {
1676 continue;
1677 }
1678 let field = (i + 1) as u16;
1679 if let Some((_, read_reg, value_reg, _, captured)) =
1680 slots.iter().find(|slot| slot.0 == *binder)
1681 {
1682 if *captured {
1683 output.push(Instr::GetFieldStruct(scrut_reg, field, *value_reg));
1684 output.push(Instr::NewCell(*value_reg, *read_reg));
1685 } else {
1686 output.push(Instr::GetFieldStruct(scrut_reg, field, *read_reg));
1687 }
1688 continue;
1689 }
1690 let value_reg = state.alloc_reg();
1691 output.push(Instr::GetFieldStruct(scrut_reg, field, value_reg));
1692 let payload_type = state.enums[enum_id as usize].variants[*variant_idx as usize]
1693 .payload[i]
1694 .clone();
1695 let captured = code_captures_variable(binder, body);
1696 let read_reg = if captured {
1697 let cell_id = state.alloc_reg();
1698 output.push(Instr::NewCell(value_reg, cell_id));
1699 cell_id
1700 } else {
1701 value_reg
1702 };
1703 slots.push((binder.clone(), read_reg, value_reg, payload_type, captured));
1704 }
1705 if k + 1 < alternatives.len() {
1706 to_body.push(output.len());
1707 output.push(Instr::Jmp(0));
1708 }
1709 }
1710 let body_start = output.len();
1711 for at in to_body {
1712 set_jmp_size(&mut output[at], (body_start - at) as u16);
1713 }
1714
1715 let v_arm = v.len();
1716 for (name, read_reg, _, var_type, captured) in slots {
1717 v.push(Variable {
1718 declared: None,
1719 name,
1720 register_id: read_reg,
1721 cell: captured,
1722 var_type,
1723 });
1724 }
1725 compile_arm_body(body, result, v, ctx, state, output);
1726 v.truncate(v_arm);
1727
1728 end_jmps.push(output.len());
1729 output.push(Instr::Jmp(0));
1730 }
1731
1732 let after_arms = output.len();
1734 if let Some(w) = wildcard {
1735 compile_arm_body(w, result, v, ctx, state, output);
1736 }
1737 let end = output.len();
1738
1739 for (k, &j) in false_jmps.iter().enumerate() {
1740 let target = if k + 1 < alternative_starts.len() {
1741 alternative_starts[k + 1]
1742 } else {
1743 after_arms
1744 };
1745 set_jmp_size(&mut output[j], (target - j) as u16);
1746 }
1747 for &j in &end_jmps {
1748 set_jmp_size(&mut output[j], (end - j) as u16);
1749 }
1750
1751 v.truncate(v_base);
1752 state.free_reg(tag_reg, v);
1753 state.free_reg(scrut_reg, v);
1754
1755 if wildcard.is_none() && !covered.iter().all(|&c| c) {
1756 let missing: Vec<&str> = state.enums[enum_id as usize]
1757 .variants
1758 .iter()
1759 .enumerate()
1760 .filter(|(i, _)| !covered[*i])
1761 .map(|(_, vt)| vt.name.as_str())
1762 .collect();
1763 compiler_errors::error_enum(
1764 "Non-exhaustive match",
1765 &format!(
1766 "match on enum {} does not cover: {}. Add the missing arm(s) or a `_` wildcard",
1767 state.enums[enum_id as usize].name,
1768 missing.join(", ")
1769 ),
1770 span,
1771 ctx.file_idx,
1772 state.sources,
1773 );
1774 }
1775}
1776
1777fn pattern_variant_enum(
1782 pattern: &Expr,
1783 v: &[Variable],
1784 ctx: Ctx,
1785 state: &State<'_>,
1786) -> Option<u16> {
1787 let path: &[SmolStr] = match pattern {
1788 Expr::Var(name, _) if v.iter().all(|var| &var.name != name) => std::slice::from_ref(name),
1789 Expr::NamespacedRef(path, _, _) => path,
1790 Expr::FunctionCall(_, namespace, _, _, _)
1791 if namespace.len() >= 2
1792 || namespace.first().is_some_and(|name| {
1793 state
1794 .scope(ctx.file_idx)
1795 .find_function(&[], name.as_str())
1796 .is_none()
1797 }) =>
1798 {
1799 namespace
1800 }
1801 _ => return None,
1802 };
1803 resolve_enum_variant(path, ctx.file_idx, state).map(|(enum_id, _)| enum_id)
1804}
1805
1806pub(crate) fn check_match_scrutinee_is_enum(
1814 scrut_type: &DataType,
1815 arms: &[(Box<[Expr]>, Box<[Expr]>)],
1816 span: Span,
1817 v: &[Variable],
1818 ctx: Ctx,
1819 state: &State<'_>,
1820) {
1821 if let Some(enum_id) = arms
1822 .iter()
1823 .flat_map(|(patterns, _)| patterns.iter())
1824 .find_map(|pattern| pattern_variant_enum(pattern, v, ctx, state))
1825 {
1826 compiler_errors::error_match_not_enum(
1827 &state.enums[enum_id as usize].name,
1828 scrut_type,
1829 span,
1830 ctx.file_idx,
1831 state.sources,
1832 state.type_names(),
1833 );
1834 }
1835}
1836
1837fn equality_arm_test(scrutinee: &SmolStr, patterns: &[Expr], span: Span) -> Expr {
1840 let equals = |pattern: &Expr| {
1841 Expr::Eq(
1842 Box::new(Expr::Var(scrutinee.clone(), span)),
1843 Box::new(pattern.clone()),
1844 span,
1845 span,
1846 )
1847 };
1848 let mut test = equals(&patterns[0]);
1849 for pattern in &patterns[1..] {
1850 test = Expr::BoolOr(Box::new(test), Box::new(equals(pattern)), span, span);
1851 }
1852 test
1853}
1854
1855fn equality_match_chain(
1860 scrutinee: &SmolStr,
1861 arms: &[(Box<[Expr]>, Box<[Expr]>)],
1862 wildcard: Option<&[Expr]>,
1863 span: Span,
1864) -> (Expr, Box<[Expr]>) {
1865 let (first_patterns, first_body) = &arms[0];
1866 let mut code: Vec<Expr> = Vec::with_capacity(arms.len() + first_body.len());
1867 code.extend(first_body.iter().cloned());
1868 for (patterns, body) in &arms[1..] {
1869 code.push(Expr::ElseIfBlock(
1870 Box::new(equality_arm_test(scrutinee, patterns, span)),
1871 body.clone(),
1872 span,
1873 ));
1874 }
1875 if let Some(w) = wildcard {
1876 code.push(Expr::ElseBlock(Box::from(w)));
1877 }
1878 (
1879 equality_arm_test(scrutinee, first_patterns, span),
1880 Box::from(code),
1881 )
1882}
1883
1884fn compile_match(
1888 scrutinee: &Expr,
1889 arms: &[(Box<[Expr]>, Box<[Expr]>)],
1890 wildcard: Option<&[Expr]>,
1891 span: Span,
1892 v: &mut Vec<Variable>,
1893 ctx: Ctx,
1894 state: &mut State<'_>,
1895 output: &mut Vec<Instr>,
1896) {
1897 let scrut_type = scrutinee.infer_type(v, ctx, state);
1898 if let DataType::Enum(enum_id) = scrut_type {
1899 compile_enum_match(
1900 enum_id, scrutinee, arms, wildcard, span, v, ctx, state, output, None,
1901 );
1902 } else {
1903 check_match_scrutinee_is_enum(&scrut_type, arms, span, v, ctx, state);
1904 let obj_var = SmolStr::new_static("[MATCH TEMP]");
1905 let (test, code) = equality_match_chain(&obj_var, arms, wildcard, span);
1906 let desugared = Expr::EvalBlock(Box::from([
1907 Expr::VarDeclare(obj_var, Box::new(scrutinee.clone()), None),
1908 Expr::Condition(Box::new(test), code, span, span),
1909 ]));
1910 desugared.compile(v, ctx, state, output, None, false, false);
1911 }
1912}
1913
1914fn compile_match_value(
1921 scrutinee: &Expr,
1922 arms: &[(Box<[Expr]>, Box<[Expr]>)],
1923 wildcard: Option<&[Expr]>,
1924 span: Span,
1925 v: &mut Vec<Variable>,
1926 ctx: Ctx,
1927 state: &mut State<'_>,
1928 output: &mut Vec<Instr>,
1929 tgt_id: Option<u16>,
1930 as_fn_value: bool,
1931) -> u16 {
1932 let scrut_type = scrutinee.infer_type(v, ctx, state);
1933 if let DataType::Enum(enum_id) = scrut_type {
1934 let result = state.alloc_reg_tgt(tgt_id);
1935 compile_enum_match(
1936 enum_id,
1937 scrutinee,
1938 arms,
1939 wildcard,
1940 span,
1941 v,
1942 ctx,
1943 state,
1944 output,
1945 Some((result, as_fn_value)),
1946 );
1947 return result;
1948 }
1949 check_match_scrutinee_is_enum(&scrut_type, arms, span, v, ctx, state);
1950 if wildcard.is_none() {
1951 compiler_errors::error_match_value_without_wildcard(span, ctx.file_idx, state.sources);
1952 }
1953 let scrut_reg = scrutinee
1954 .compile(v, ctx, state, output, None, false, true)
1955 .unwrap_id();
1956 let obj_var = SmolStr::new_static("[MATCH TEMP]");
1957 let v_base = v.len();
1958 v.push(Variable {
1959 declared: None,
1960 name: obj_var.clone(),
1961 register_id: scrut_reg,
1962 cell: false,
1963 var_type: scrut_type,
1964 });
1965 let (test, code) = equality_match_chain(&obj_var, arms, wildcard, span);
1966 let result = compile_inline_condition(
1967 &test,
1968 &code,
1969 span,
1970 span,
1971 v,
1972 ctx,
1973 state,
1974 output,
1975 tgt_id,
1976 as_fn_value,
1977 );
1978 v.truncate(v_base);
1979 state.free_reg(scrut_reg, v);
1980 result
1981}
1982
1983fn compile_map_literal(
1984 kv_pairs: &[(Expr, Span, Expr, Span)],
1985 map_span: Span,
1986 v: &mut Vec<Variable>,
1987 ctx: Ctx,
1988 state: &mut State<'_>,
1989 output: &mut Vec<Instr>,
1990) -> u16 {
1991 let mut global_key_type: DataType = DataType::Unknown;
1992 let mut global_val_type: DataType = DataType::Unknown;
1993 let value_types = kv_pairs
1997 .iter()
1998 .map(|(_, _, val, _)| val.infer_type(v, ctx, state))
1999 .collect::<Vec<DataType>>();
2000 let as_fn_values = holds_functions(&value_types);
2001 let map_id = state.pools.maps.len();
2002 state.pools.maps.push(CandelaMap::with_capacity_and_hasher(
2005 kv_pairs.len(),
2006 BuildHasherDefault::default(),
2007 ));
2008 if ctx.single_run {
2009 for (i, (key, key_span, val, val_span)) in kv_pairs.iter().enumerate() {
2010 if let Some((_, repeat_key_span, _, _)) =
2011 kv_pairs.iter().skip(i + 1).find(|(k, _, _, _)| k == key)
2012 {
2013 error_duplicate_map_key(
2014 *key_span,
2015 *repeat_key_span,
2016 map_span,
2017 ctx.file_idx,
2018 state.sources,
2019 );
2020 }
2021 let key_t = key.infer_type(v, ctx, state);
2022 let val_t = val.infer_type(v, ctx, state);
2023 if i == 0 {
2024 global_key_type = key_t;
2025 global_val_type = val_t;
2026 } else {
2027 if key_t != global_key_type {
2028 error_map_diff_types(
2029 ctx.file_idx,
2030 state.sources,
2031 map_span,
2032 &global_key_type,
2033 *key_span,
2034 &key_t,
2035 state.type_names(),
2036 )
2037 }
2038 if val_t != global_val_type {
2039 error_map_diff_types(
2040 ctx.file_idx,
2041 state.sources,
2042 map_span,
2043 &global_val_type,
2044 *val_span,
2045 &val_t,
2046 state.type_names(),
2047 )
2048 }
2049 }
2050 let output_len = output.len();
2051 let key_val_id = key
2052 .compile(v, ctx, state, output, None, false, true)
2053 .unwrap_id();
2054 if !(key.is_constant_literal()
2055 || matches!(key, Expr::Array(_, _)) && output_len == output.len())
2056 {
2057 error_not_literal_map_key(*key_span, map_span, ctx.file_idx, state.sources);
2058 }
2059 let key_val = state.registers[key_val_id as usize];
2060 let id = compile_element(val, as_fn_values, v, ctx, state, output);
2061 if val.is_constant_literal() {
2062 map_insert(
2063 &mut state.pools.maps[map_id],
2064 key_val,
2065 state.registers[id as usize],
2066 &state.pools.strings,
2067 );
2068 } else {
2069 map_insert(
2070 &mut state.pools.maps[map_id],
2071 key_val,
2072 NULL,
2073 &state.pools.strings,
2074 );
2075 output.push(Instr::MapInsert(
2076 map_id as u16,
2077 state.registers.len() as u16,
2078 id,
2079 ));
2080 state.registers.push(key_val);
2081 }
2082 }
2083 let dest_id = state.registers.len();
2084 state.registers.push(Data::map(map_id as u32));
2085 dest_id as u16
2086 } else {
2087 let mut dynamic: Vec<(Data, u16)> = Vec::with_capacity(kv_pairs.len());
2088 for (i, (key, key_span, val, val_span)) in kv_pairs.iter().enumerate() {
2089 if let Some((_, repeat_key_span, _, _)) =
2090 kv_pairs.iter().skip(i + 1).find(|(k, _, _, _)| k == key)
2091 {
2092 error_duplicate_map_key(
2093 *key_span,
2094 *repeat_key_span,
2095 map_span,
2096 ctx.file_idx,
2097 state.sources,
2098 );
2099 }
2100 let key_t = key.infer_type(v, ctx, state);
2101 let val_t = val.infer_type(v, ctx, state);
2102 if i == 0 {
2103 global_key_type = key_t;
2104 global_val_type = val_t;
2105 } else {
2106 if key_t != global_key_type {
2107 error_map_diff_types(
2108 ctx.file_idx,
2109 state.sources,
2110 map_span,
2111 &global_key_type,
2112 *key_span,
2113 &key_t,
2114 state.type_names(),
2115 )
2116 }
2117 if val_t != global_val_type {
2118 error_map_diff_types(
2119 ctx.file_idx,
2120 state.sources,
2121 map_span,
2122 &global_val_type,
2123 *val_span,
2124 &val_t,
2125 state.type_names(),
2126 )
2127 }
2128 }
2129 let output_len = output.len();
2130 let key_val_id = key
2131 .compile(v, ctx, state, output, None, false, true)
2132 .unwrap_id();
2133 if !(key.is_constant_literal()
2134 || matches!(key, Expr::Array(_, _)) && output_len == output.len())
2135 {
2136 error_not_literal_map_key(*key_span, map_span, ctx.file_idx, state.sources);
2137 }
2138 let key_val = state.registers[key_val_id as usize];
2139 let val_id = compile_element(val, as_fn_values, v, ctx, state, output);
2140 if val.is_constant_literal() {
2141 map_insert(
2142 &mut state.pools.maps[map_id],
2143 key_val,
2144 state.registers[val_id as usize],
2145 &state.pools.strings,
2146 );
2147 } else {
2148 map_insert(
2149 &mut state.pools.maps[map_id],
2150 key_val,
2151 NULL,
2152 &state.pools.strings,
2153 );
2154 dynamic.push((key_val, val_id));
2155 }
2156 }
2157
2158 let template_reg = state.template_register(Data::map(map_id as u32));
2159 let dest_reg = {
2164 state.registers.push(Data::map(map_id as u32));
2165 (state.registers.len() - 1) as u16
2166 };
2167 output.push(Instr::CloneMap(template_reg, dest_reg));
2168 for (key_val, val_id) in dynamic {
2169 let key_reg = if let Some(&id) = state.const_registers.get(&key_val) {
2170 id
2171 } else {
2172 let id = state.registers.len() as u16;
2173 state.const_registers.insert(key_val, id);
2174 state.registers.push(key_val);
2175 id
2176 };
2177 output.push(Instr::MapInsertReg(dest_reg, key_reg, val_id));
2178 }
2179 dest_reg
2180 }
2181}
2182
2183fn compile_struct_field_access(
2184 struct_expr: &Expr,
2185 field: &SmolStr,
2186 struct_span: Span,
2187 field_span: Span,
2188 v: &mut Vec<Variable>,
2189 ctx: Ctx,
2190 state: &mut State<'_>,
2191 output: &mut Vec<Instr>,
2192) -> u16 {
2193 let t = struct_expr.infer_type(v, ctx, state);
2194 if let DataType::Struct(s_id) = t {
2195 let s = &state.structs[s_id as usize];
2196 let idx = s
2197 .fields
2198 .iter()
2199 .position(|f| &f.0 == field)
2200 .unwrap_or_else(|| {
2201 compiler_errors::error_struct_unknown_field(
2202 ctx.file_idx,
2203 field_span,
2204 field,
2205 &s.name,
2206 &s.fields,
2207 state.sources,
2208 );
2209 });
2210 let id = struct_expr
2211 .compile(v, ctx, state, output, None, false, true)
2212 .unwrap_id();
2213 let dest_reg_id = state.alloc_reg();
2214 output.push(Instr::GetFieldStruct(id, idx as u16, dest_reg_id));
2215 dest_reg_id
2216 } else {
2217 error_invalid_type(
2218 &DataType::Struct(0),
2219 &t,
2220 struct_span,
2221 None,
2222 None,
2223 ctx.file_idx,
2224 state.sources,
2225 state.type_names(),
2226 );
2227 }
2228}
2229
2230fn compile_propagate(
2236 operand: &Expr,
2237 span: Span,
2238 tgt_id: Option<u16>,
2239 v: &mut Vec<Variable>,
2240 ctx: Ctx,
2241 state: &mut State<'_>,
2242 output: &mut Vec<Instr>,
2243) -> u16 {
2244 let operand_type = operand.infer_type(v, ctx, state);
2245 let Some((_, failure_idx)) = type_system::propagation_sides(&operand_type, state) else {
2246 compiler_errors::error_propagate_operand(
2247 &operand_type,
2248 span,
2249 ctx.file_idx,
2250 state.sources,
2251 state.type_names(),
2252 );
2253 };
2254 let function = state.fn_returns.last().cloned();
2255 let fits = ctx.in_function
2256 && function
2257 .as_ref()
2258 .is_some_and(|(_, returns)| propagation_fits(&operand_type, returns, state));
2259 if !fits {
2260 compiler_errors::error_propagate_return(
2261 function
2262 .as_ref()
2263 .filter(|_| ctx.in_function)
2264 .map(|(name, returns)| (name.as_str(), returns)),
2265 &operand_type,
2266 span,
2267 ctx.file_idx,
2268 state.sources,
2269 state.type_names(),
2270 );
2271 }
2272
2273 let operand_reg = operand
2274 .compile(v, ctx, state, output, None, false, true)
2275 .unwrap_id();
2276 let v_base = v.len();
2278 v.push(Variable {
2279 declared: None,
2280 name: SmolStr::new_static("[PROPAGATE]"),
2281 register_id: operand_reg,
2282 cell: false,
2283 var_type: operand_type,
2284 });
2285 let tag_reg = state.alloc_reg();
2286 output.push(Instr::GetFieldStruct(operand_reg, 0, tag_reg));
2287 let failure_reg = state.alloc_reg();
2288 output.push(Instr::SetInt(failure_reg, i32::from(failure_idx)));
2289 let to_success = output.len();
2290 output.push(Instr::NotEqJmp(tag_reg, failure_reg, 0));
2291 state.free_reg(failure_reg, v);
2292 state.free_reg(tag_reg, v);
2293
2294 compile_return(
2295 Some(&Expr::Var(SmolStr::new_static("[PROPAGATE]"), span)),
2296 v,
2297 ctx,
2298 state,
2299 output,
2300 );
2301
2302 let success = output.len();
2303 set_jmp_size(&mut output[to_success], (success - to_success) as u16);
2304 let dest = state.alloc_reg_tgt(tgt_id);
2305 output.push(Instr::GetFieldStruct(operand_reg, 1, dest));
2306 v.truncate(v_base);
2307 state.free_reg(operand_reg, v);
2308 dest
2309}
2310
2311fn propagation_fits(operand: &DataType, returns: &DataType, state: &State<'_>) -> bool {
2315 let (Some((template, operand_args)), Some((returns_template, returns_args))) = (
2316 state.generics.instantiation_of(operand),
2317 state.generics.instantiation_of(returns),
2318 ) else {
2319 return false;
2320 };
2321 template == returns_template
2322 && operand_args
2323 .iter()
2324 .zip(returns_args)
2325 .skip(1)
2326 .all(|(a, b)| param_type_matches(b, a, state.generics))
2327}
2328
2329fn check_map_key(
2332 map_type: &DataType,
2333 key: &Expr,
2334 span: Span,
2335 v: &mut Vec<Variable>,
2336 ctx: Ctx,
2337 state: &mut State<'_>,
2338) {
2339 let DataType::Map(entry) = map_type else {
2340 return;
2341 };
2342 let Some(expected) = &entry.0 else {
2343 return;
2344 };
2345 let found = key.infer_type(v, ctx, state);
2346 if !param_type_matches(expected, &found, state.generics) {
2347 error_invalid_index_type(
2348 &found,
2349 span,
2350 ctx.file_idx,
2351 state.sources,
2352 state.type_names(),
2353 );
2354 }
2355}
2356
2357fn compile_map_indexing(
2360 map: &Expr,
2361 map_type: &DataType,
2362 key: &Expr,
2363 span: Span,
2364 v: &mut Vec<Variable>,
2365 ctx: Ctx,
2366 state: &mut State<'_>,
2367 output: &mut Vec<Instr>,
2368) -> u16 {
2369 check_map_key(map_type, key, span, v, ctx, state);
2370 let map_id = map
2371 .compile(v, ctx, state, output, None, false, true)
2372 .unwrap_id();
2373 let key_id = key
2374 .compile(v, ctx, state, output, None, false, true)
2375 .unwrap_id();
2376 state.free_reg(key_id, v);
2377 let dest_reg_id = state.alloc_reg();
2378 output.push(Instr::MapGet(map_id, key_id, dest_reg_id));
2379 state.add_to_src(ctx, output, span);
2380 dest_reg_id
2381}
2382
2383fn compile_array_indexing(
2384 array: &Expr,
2385 index: &Expr,
2386 span: Span,
2387 v: &mut Vec<Variable>,
2388 ctx: Ctx,
2389 state: &mut State<'_>,
2390 output: &mut Vec<Instr>,
2391) -> u16 {
2392 let inferred = array.infer_type(v, ctx, state);
2393 if matches!(inferred, DataType::Map(_)) {
2394 return compile_map_indexing(array, &inferred, index, span, v, ctx, state, output);
2395 }
2396 if !inferred.is_indexable() {
2397 error_type_not_indexable(
2398 &inferred,
2399 span,
2400 false,
2401 ctx.file_idx,
2402 state.sources,
2403 state.type_names(),
2404 );
2405 }
2406
2407 let id = array
2408 .compile(v, ctx, state, output, None, false, true)
2409 .unwrap_id();
2410
2411 let index_inferred = index.infer_type(v, ctx, state);
2412 if index_inferred != DataType::Int {
2413 error_invalid_index_type(
2414 &index_inferred,
2415 span,
2416 ctx.file_idx,
2417 state.sources,
2418 state.type_names(),
2419 );
2420 }
2421 let index_id = index
2422 .compile(v, ctx, state, output, None, false, true)
2423 .unwrap_id();
2424 state.free_reg(index_id, v);
2425 let dest_reg_id = state.alloc_reg();
2426
2427 let to_push = if inferred == DataType::String {
2428 Instr::GetIndexString(id, index_id, dest_reg_id)
2429 } else {
2430 Instr::GetIndexArray(id, index_id, dest_reg_id)
2431 };
2432 output.push(to_push);
2433 state.add_to_src(ctx, output, span);
2434 dest_reg_id
2435}
2436
2437fn compile_array_slice(
2438 array: &Expr,
2439 idx_start: &Expr,
2440 idx_end: Option<&Expr>,
2441 span: Span,
2442 v: &mut Vec<Variable>,
2443 ctx: Ctx,
2444 state: &mut State<'_>,
2445 output: &mut Vec<Instr>,
2446) -> u16 {
2447 let inferred = array.infer_type(v, ctx, state);
2448 if !inferred.is_indexable() {
2449 error_type_not_indexable(
2450 &inferred,
2451 span,
2452 false,
2453 ctx.file_idx,
2454 state.sources,
2455 state.type_names(),
2456 );
2457 }
2458 let id = array
2459 .compile(v, ctx, state, output, None, false, true)
2460 .unwrap_id();
2461 let idx_start_inferred = idx_start.infer_type(v, ctx, state);
2462 if idx_start_inferred != DataType::Int {
2463 error_invalid_index_type(
2464 &idx_start_inferred,
2465 span,
2466 ctx.file_idx,
2467 state.sources,
2468 state.type_names(),
2469 );
2470 }
2471 let idx_start_id = idx_start
2472 .compile(v, ctx, state, output, None, false, true)
2473 .unwrap_id();
2474 let idx_end_id = if let Some(idx_end) = idx_end {
2475 let idx_end_inferred = idx_end.infer_type(v, ctx, state);
2476 if idx_end_inferred != DataType::Int {
2477 error_invalid_index_type(
2478 &idx_end_inferred,
2479 span,
2480 ctx.file_idx,
2481 state.sources,
2482 state.type_names(),
2483 );
2484 }
2485 idx_end
2486 .compile(v, ctx, state, output, None, false, true)
2487 .unwrap_id()
2488 } else {
2489 let len_id = state.alloc_reg();
2492 output.push(Instr::CallLibFunc(LibFunc::Len, id, len_id));
2493 len_id
2494 };
2495 output.push(Instr::StoreFuncArg(idx_end_id));
2496 state.free_reg(idx_start_id, v);
2497 state.free_reg(idx_end_id, v);
2498 let dest_reg_id = state.alloc_reg();
2499 let to_push = if inferred == DataType::String {
2500 Instr::GetSliceString(id, idx_start_id, dest_reg_id)
2501 } else {
2502 Instr::GetSliceArray(id, idx_start_id, dest_reg_id)
2503 };
2504 output.push(to_push);
2505 state.add_to_src(ctx, output, span);
2506 dest_reg_id
2507}
2508
2509#[inline]
2510fn uniform_op2(
2511 instr: fn(u16, u16, u16) -> Instr,
2512 t_1: &'static DataType,
2513 instr2: fn(u16, u16, u16) -> Instr,
2514 t_2: &'static DataType,
2515 symbol: &'static str,
2516 l: &Expr,
2517 r: &Expr,
2518 span_l: Span,
2519 span_r: Span,
2520 tgt_id: Option<u16>,
2521 v: &mut Vec<Variable>,
2522 ctx: Ctx,
2523 state: &mut State<'_>,
2524 output: &mut Vec<Instr>,
2525) -> u16 {
2526 let (t_l, t_r) = (l.infer_type(v, ctx, state), r.infer_type(v, ctx, state));
2527 if !((&t_l == t_1 && &t_r == t_1) || (&t_l == t_2 && &t_r == t_2)) {
2528 if let Some(id) = compile_operator_method(
2529 symbol,
2530 l,
2531 Some(r),
2532 &t_l,
2533 &t_r,
2534 span_l,
2535 span_r,
2536 tgt_id,
2537 v,
2538 ctx,
2539 state,
2540 output,
2541 ) {
2542 return id;
2543 }
2544 compiler_errors::error_op(
2545 &t_l,
2546 &t_r,
2547 symbol,
2548 span_l,
2549 span_r,
2550 ctx.file_idx,
2551 state.sources,
2552 state.type_names(),
2553 );
2554 }
2555 let id_l = l
2556 .compile(v, ctx, state, output, None, false, true)
2557 .unwrap_id();
2558 let id_r = r
2559 .compile(v, ctx, state, output, None, false, true)
2560 .unwrap_id();
2561 state.free_reg(id_l, v);
2562 state.free_reg(id_r, v);
2563 let id = state.alloc_reg_tgt(tgt_id);
2564 output.push(if &t_l == t_1 {
2565 instr(id_l, id_r, id)
2566 } else {
2567 instr2(id_l, id_r, id)
2568 });
2569 id
2570}
2571
2572fn compile_cmp_op(
2578 float_instr: fn(u16, u16, u16) -> Instr,
2579 int_instr: fn(u16, u16, u16) -> Instr,
2580 str_instr: fn(u16, u16, u16) -> Instr,
2581 symbol: &'static str,
2582 l: &Expr,
2583 r: &Expr,
2584 span_l: Span,
2585 span_r: Span,
2586 tgt_id: Option<u16>,
2587 v: &mut Vec<Variable>,
2588 ctx: Ctx,
2589 state: &mut State<'_>,
2590 output: &mut Vec<Instr>,
2591) -> u16 {
2592 let (t_l, t_r) = (l.infer_type(v, ctx, state), r.infer_type(v, ctx, state));
2593 if t_l != t_r || !matches!(t_l, DataType::Float | DataType::Int | DataType::String) {
2594 if let Some(id) = compile_operator_method(
2595 symbol,
2596 l,
2597 Some(r),
2598 &t_l,
2599 &t_r,
2600 span_l,
2601 span_r,
2602 tgt_id,
2603 v,
2604 ctx,
2605 state,
2606 output,
2607 ) {
2608 return id;
2609 }
2610 compiler_errors::error_op(
2611 &t_l,
2612 &t_r,
2613 symbol,
2614 span_l,
2615 span_r,
2616 ctx.file_idx,
2617 state.sources,
2618 state.type_names(),
2619 );
2620 }
2621 let id_l = l
2622 .compile(v, ctx, state, output, None, false, true)
2623 .unwrap_id();
2624 let id_r = r
2625 .compile(v, ctx, state, output, None, false, true)
2626 .unwrap_id();
2627 state.free_reg(id_l, v);
2628 state.free_reg(id_r, v);
2629 let id = state.alloc_reg_tgt(tgt_id);
2630 output.push(if t_l == DataType::String {
2631 str_instr(id_l, id_r, id)
2632 } else if t_l == DataType::Float {
2633 float_instr(id_l, id_r, id)
2634 } else {
2635 int_instr(id_l, id_r, id)
2636 });
2637 id
2638}
2639
2640fn compile_div_op(
2641 l: &Expr,
2642 r: &Expr,
2643 span_l: Span,
2644 span_r: Span,
2645 tgt_id: Option<u16>,
2646 v: &mut Vec<Variable>,
2647 ctx: Ctx,
2648 state: &mut State<'_>,
2649 output: &mut Vec<Instr>,
2650) -> u16 {
2651 if let Expr::Int(n) = r
2655 && *n == 0
2656 && let t_l = l.infer_type(v, ctx, state)
2657 && t_l != DataType::Float
2658 && !matches!(t_l, DataType::Struct(_) | DataType::Enum(_))
2659 {
2660 error_division_by_zero(false, span_l.extend(span_r), ctx.file_idx, state.sources);
2661 }
2662 let id = uniform_op2(
2663 Instr::DivFloat,
2664 &DataType::Float,
2665 Instr::DivInt,
2666 &DataType::Int,
2667 "/",
2668 l,
2669 r,
2670 span_l,
2671 span_r,
2672 tgt_id,
2673 v,
2674 ctx,
2675 state,
2676 output,
2677 );
2678 if matches!(output.last(), Some(Instr::DivInt(..))) {
2679 state.add_to_src(ctx, output, span_l.extend(span_r));
2680 }
2681 id
2682}
2683
2684fn compile_add_op(
2685 l: &Expr,
2686 r: &Expr,
2687 span_l: Span,
2688 span_r: Span,
2689 tgt_id: Option<u16>,
2690 v: &mut Vec<Variable>,
2691 ctx: Ctx,
2692 state: &mut State<'_>,
2693 output: &mut Vec<Instr>,
2694) -> u16 {
2695 let t_l = l.infer_type(v, ctx, state);
2696 let t_r = r.infer_type(v, ctx, state);
2697 if t_l != t_r
2698 || !matches!(
2699 t_l,
2700 DataType::String | DataType::Array(_) | DataType::Float | DataType::Int
2701 )
2702 {
2703 if let Some(id) = compile_operator_method(
2704 "+",
2705 l,
2706 Some(r),
2707 &t_l,
2708 &t_r,
2709 span_l,
2710 span_r,
2711 tgt_id,
2712 v,
2713 ctx,
2714 state,
2715 output,
2716 ) {
2717 return id;
2718 }
2719 compiler_errors::error_op(
2720 &t_l,
2721 &t_r,
2722 "+",
2723 span_l,
2724 span_r,
2725 ctx.file_idx,
2726 state.sources,
2727 state.type_names(),
2728 );
2729 }
2730 if t_l == DataType::Int
2732 && let Some(Expr::Var(src_name, _)) = {
2733 if matches!(r, Expr::Int(1)) {
2734 Some(l)
2735 } else if matches!(l, Expr::Int(1)) {
2736 Some(r)
2737 } else {
2738 None
2739 }
2740 }
2741 && let Some(src_var) = v.iter().rfind(|x| x.name == *src_name)
2742 && !src_var.cell
2743 {
2744 let src_id = src_var.register_id;
2745 let id = tgt_id.unwrap_or_else(|| state.alloc_reg());
2746 output.push(if src_id == id {
2747 Instr::IncInt(id)
2748 } else {
2749 Instr::IncIntTo(src_id, id)
2750 });
2751 return id;
2752 }
2753 let id_l = l
2754 .compile(v, ctx, state, output, None, false, true)
2755 .unwrap_id();
2756 let id_r = r
2757 .compile(v, ctx, state, output, None, false, true)
2758 .unwrap_id();
2759 state.free_reg(id_l, v);
2760 state.free_reg(id_r, v);
2761 let id = state.alloc_reg_tgt(tgt_id);
2762 if matches!(t_l, DataType::Array(_)) {
2763 output.push(Instr::AddArray(id_l, id_r, id));
2764 } else if t_l == DataType::String {
2765 output.push(Instr::AddStr(id_l, id_r, id));
2766 } else if t_l == DataType::Float {
2767 output.push(Instr::AddFloat(id_l, id_r, id));
2768 } else {
2769 output.push(Instr::AddInt(id_l, id_r, id));
2770 }
2771 id
2772}
2773
2774fn compile_sub_op(
2775 l: &Expr,
2776 r: &Expr,
2777 span_l: Span,
2778 span_r: Span,
2779 tgt_id: Option<u16>,
2780 v: &mut Vec<Variable>,
2781 ctx: Ctx,
2782 state: &mut State<'_>,
2783 output: &mut Vec<Instr>,
2784) -> u16 {
2785 let t_l = l.infer_type(v, ctx, state);
2786 let t_r = r.infer_type(v, ctx, state);
2787 if !((t_l == DataType::Float && t_r == DataType::Float)
2788 || (t_l == DataType::Int && t_r == DataType::Int))
2789 {
2790 if let Some(id) = compile_operator_method(
2791 "-",
2792 l,
2793 Some(r),
2794 &t_l,
2795 &t_r,
2796 span_l,
2797 span_r,
2798 tgt_id,
2799 v,
2800 ctx,
2801 state,
2802 output,
2803 ) {
2804 return id;
2805 }
2806 compiler_errors::error_op(
2807 &t_l,
2808 &t_r,
2809 "-",
2810 span_l,
2811 span_r,
2812 ctx.file_idx,
2813 state.sources,
2814 state.type_names(),
2815 );
2816 }
2817 if t_l == DataType::Int
2819 && matches!(r, Expr::Int(1))
2820 && let Expr::Var(src_name, _) = l
2821 && let Some(src_var) = v.iter().rfind(|x| x.name == *src_name)
2822 && !src_var.cell
2823 {
2824 let src_id = src_var.register_id;
2825 let id = tgt_id.unwrap_or_else(|| state.alloc_reg());
2826 output.push(if src_id == id {
2827 Instr::DecInt(id)
2828 } else {
2829 Instr::DecIntTo(src_id, id)
2830 });
2831 return id;
2832 }
2833 let id_l = l
2834 .compile(v, ctx, state, output, None, false, true)
2835 .unwrap_id();
2836 let id_r = r
2837 .compile(v, ctx, state, output, None, false, true)
2838 .unwrap_id();
2839 state.free_reg(id_l, v);
2840 state.free_reg(id_r, v);
2841 let id = state.alloc_reg_tgt(tgt_id);
2842 output.push(if t_l == DataType::Float {
2843 Instr::SubFloat(id_l, id_r, id)
2844 } else {
2845 Instr::SubInt(id_l, id_r, id)
2846 });
2847 id
2848}
2849
2850fn compile_mod_op(
2851 l: &Expr,
2852 r: &Expr,
2853 span_l: Span,
2854 span_r: Span,
2855 tgt_id: Option<u16>,
2856 v: &mut Vec<Variable>,
2857 ctx: Ctx,
2858 state: &mut State<'_>,
2859 output: &mut Vec<Instr>,
2860) -> u16 {
2861 if let Expr::Int(n) = r
2864 && *n == 0
2865 && let t_l = l.infer_type(v, ctx, state)
2866 && t_l != DataType::Float
2867 && !matches!(t_l, DataType::Struct(_) | DataType::Enum(_))
2868 {
2869 error_division_by_zero(true, span_l.extend(span_r), ctx.file_idx, state.sources);
2870 }
2871 let id = uniform_op2(
2872 Instr::ModFloat,
2873 &DataType::Float,
2874 Instr::ModInt,
2875 &DataType::Int,
2876 "%",
2877 l,
2878 r,
2879 span_l,
2880 span_r,
2881 tgt_id,
2882 v,
2883 ctx,
2884 state,
2885 output,
2886 );
2887 if matches!(output.last(), Some(Instr::ModInt(..))) {
2888 state.add_to_src(ctx, output, span_l.extend(span_r));
2889 }
2890 id
2891}
2892
2893fn compile_short_circuit_value(
2905 l: &Expr,
2906 r: &Expr,
2907 span_l: Span,
2908 span_r: Span,
2909 symbol: &'static str,
2910 tgt_id: Option<u16>,
2911 v: &mut Vec<Variable>,
2912 ctx: Ctx,
2913 state: &mut State<'_>,
2914 output: &mut Vec<Instr>,
2915 keep_facts: bool,
2916) -> u16 {
2917 let and = symbol == "&&";
2918 let v_len = v.len();
2919 let t_l = l.infer_type(v, ctx, state);
2920 if and {
2921 type_test::declare_condition_facts(l, v, ctx, state);
2922 }
2923 let t_r = r.infer_type(v, ctx, state);
2924 v.truncate(v_len);
2925 if t_l != DataType::Bool || t_r != DataType::Bool {
2926 cold_path();
2927 compiler_errors::error_op(
2928 &t_l,
2929 &t_r,
2930 symbol,
2931 span_l,
2932 span_r,
2933 ctx.file_idx,
2934 state.sources,
2935 state.type_names(),
2936 );
2937 }
2938
2939 let id = state.alloc_reg_tgt(tgt_id);
2940 let (left_id, facts) = match l {
2944 Expr::Is(operand, target, operand_span, target_span) if and => {
2945 let outer = std::mem::replace(
2946 &mut state.captured_binders,
2947 type_test::captured_binders(l, std::slice::from_ref(r)),
2948 );
2949 let compiled = type_test::compile_is(
2950 operand,
2951 target,
2952 *operand_span,
2953 *target_span,
2954 Some(id),
2955 true,
2956 v,
2957 ctx,
2958 state,
2959 output,
2960 );
2961 state.captured_binders = outer;
2962 (compiled.0, Some(compiled.1))
2963 }
2964 Expr::BoolAnd(ll, lr, span_ll, span_lr) if and => (
2967 compile_short_circuit_value(
2968 ll,
2969 lr,
2970 *span_ll,
2971 *span_lr,
2972 "&&",
2973 Some(id),
2974 v,
2975 ctx,
2976 state,
2977 output,
2978 true,
2979 ),
2980 None,
2981 ),
2982 _ => (
2983 l.compile(v, ctx, state, output, Some(id), false, true)
2984 .unwrap_id(),
2985 None,
2986 ),
2987 };
2988 if left_id != id {
2989 output.push(Instr::Mov(left_id, id));
2990 }
2991
2992 let skip_idx = output.len();
2993 output.push(if and {
2996 Instr::IsFalseJmp(id, 0)
2997 } else {
2998 Instr::IsTrueJmp(id, 0)
2999 });
3000 if let Some(facts) = facts {
3001 facts.apply(v, state, output);
3002 }
3003
3004 let right_id = r
3005 .compile(v, ctx, state, output, None, false, true)
3006 .unwrap_id();
3007 if !keep_facts {
3008 v.truncate(v_len);
3009 }
3010 state.free_reg(right_id, v);
3011 output.push(Instr::Mov(right_id, id));
3012 let skip_size = (output.len() - skip_idx) as u16;
3013 set_jmp_size(&mut output[skip_idx], skip_size);
3014 id
3015}
3016
3017const fn compares_by_contents(l_type: &DataType, r_type: &DataType) -> bool {
3023 const fn is_object(t: &DataType) -> bool {
3024 matches!(
3025 t,
3026 DataType::Array(_) | DataType::Map(_) | DataType::Struct(_) | DataType::Enum(_)
3027 )
3028 }
3029 is_object(l_type) && is_object(r_type)
3030}
3031
3032fn compile_eq_op(
3033 l: &Expr,
3034 r: &Expr,
3035 span_l: Span,
3036 span_r: Span,
3037 tgt_id: Option<u16>,
3038 v: &mut Vec<Variable>,
3039 ctx: Ctx,
3040 state: &mut State<'_>,
3041 output: &mut Vec<Instr>,
3042) -> u16 {
3043 let l_type = l.infer_type(v, ctx, state);
3044 let r_type = r.infer_type(v, ctx, state);
3045 if let Some(id) = compile_operator_method(
3048 "==",
3049 l,
3050 Some(r),
3051 &l_type,
3052 &r_type,
3053 span_l,
3054 span_r,
3055 tgt_id,
3056 v,
3057 ctx,
3058 state,
3059 output,
3060 ) {
3061 return id;
3062 }
3063 let by_contents = compares_by_contents(&l_type, &r_type);
3064 let is_string = l_type == DataType::String || r_type == DataType::String;
3065 let id_l = l
3066 .compile(v, ctx, state, output, None, false, true)
3067 .unwrap_id();
3068 let id_r = r
3069 .compile(v, ctx, state, output, None, false, true)
3070 .unwrap_id();
3071 state.free_reg(id_l, v);
3072 state.free_reg(id_r, v);
3073 let id = state.alloc_reg_tgt(tgt_id);
3074 output.push(if by_contents {
3075 Instr::ObjEq(id_l, id_r, id)
3076 } else if is_string {
3077 Instr::StrEq(id_l, id_r, id)
3078 } else {
3079 Instr::Eq(id_l, id_r, id)
3080 });
3081 id
3082}
3083
3084fn compile_neq_op(
3085 l: &Expr,
3086 r: &Expr,
3087 span_l: Span,
3088 span_r: Span,
3089 tgt_id: Option<u16>,
3090 v: &mut Vec<Variable>,
3091 ctx: Ctx,
3092 state: &mut State<'_>,
3093 output: &mut Vec<Instr>,
3094) -> u16 {
3095 let l_type = l.infer_type(v, ctx, state);
3096 let r_type = r.infer_type(v, ctx, state);
3097 if let Some(id) = compile_operator_method(
3100 "!=",
3101 l,
3102 Some(r),
3103 &l_type,
3104 &r_type,
3105 span_l,
3106 span_r,
3107 tgt_id,
3108 v,
3109 ctx,
3110 state,
3111 output,
3112 ) {
3113 return id;
3114 }
3115 let by_contents = compares_by_contents(&l_type, &r_type);
3116 let is_string = l_type == DataType::String || r_type == DataType::String;
3117 let id_l = l
3118 .compile(v, ctx, state, output, None, false, true)
3119 .unwrap_id();
3120 let id_r = r
3121 .compile(v, ctx, state, output, None, false, true)
3122 .unwrap_id();
3123 state.free_reg(id_l, v);
3124 state.free_reg(id_r, v);
3125 let id = state.alloc_reg_tgt(tgt_id);
3126 if by_contents {
3127 output.push(Instr::ObjNotEq(id_l, id_r, id));
3128 } else if is_string {
3129 output.push(Instr::StrNotEq(id_l, id_r, id));
3130 } else {
3131 output.push(Instr::NotEq(id_l, id_r, id));
3132 }
3133 id
3134}
3135
3136fn compile_neg_op(
3137 l: &Expr,
3138 span_l: Span,
3139 span_r: Span,
3140 tgt_id: Option<u16>,
3141 v: &mut Vec<Variable>,
3142 ctx: Ctx,
3143 state: &mut State<'_>,
3144 output: &mut Vec<Instr>,
3145) -> u16 {
3146 let operand_type = l.infer_type(v, ctx, state);
3147 if let Some(id) = compile_operator_method(
3148 UNARY_MINUS_METHOD,
3149 l,
3150 None,
3151 &operand_type,
3152 &operand_type,
3153 span_l,
3154 span_r,
3155 tgt_id,
3156 v,
3157 ctx,
3158 state,
3159 output,
3160 ) {
3161 return id;
3162 }
3163 let id_l = l
3164 .compile(v, ctx, state, output, None, false, true)
3165 .unwrap_id();
3166 state.free_reg(id_l, v);
3167 let id = state.alloc_reg_tgt(tgt_id);
3168 if operand_type == DataType::Float {
3169 output.push(Instr::NegFloat(id_l, id));
3170 } else if operand_type == DataType::Int {
3171 output.push(Instr::NegInt(id_l, id));
3172 } else {
3173 compiler_errors::error_op(
3174 &DataType::Null,
3175 &operand_type,
3176 "-",
3177 span_l,
3178 span_r,
3179 ctx.file_idx,
3180 state.sources,
3181 state.type_names(),
3182 );
3183 }
3184 id
3185}
3186
3187#[allow(clippy::too_many_arguments)]
3194fn int_op2(
3195 instr: fn(u16, u16, u16) -> Instr,
3196 symbol: &'static str,
3197 l: &Expr,
3198 r: &Expr,
3199 span_l: Span,
3200 span_r: Span,
3201 tgt_id: Option<u16>,
3202 v: &mut Vec<Variable>,
3203 ctx: Ctx,
3204 state: &mut State<'_>,
3205 output: &mut Vec<Instr>,
3206) -> u16 {
3207 let (t_l, t_r) = (l.infer_type(v, ctx, state), r.infer_type(v, ctx, state));
3208 if t_l != DataType::Int || t_r != DataType::Int {
3209 if let Some(id) = compile_operator_method(
3210 symbol,
3211 l,
3212 Some(r),
3213 &t_l,
3214 &t_r,
3215 span_l,
3216 span_r,
3217 tgt_id,
3218 v,
3219 ctx,
3220 state,
3221 output,
3222 ) {
3223 return id;
3224 }
3225 compiler_errors::error_op(
3226 &t_l,
3227 &t_r,
3228 symbol,
3229 span_l,
3230 span_r,
3231 ctx.file_idx,
3232 state.sources,
3233 state.type_names(),
3234 );
3235 }
3236 let id_l = l
3237 .compile(v, ctx, state, output, None, false, true)
3238 .unwrap_id();
3239 let id_r = r
3240 .compile(v, ctx, state, output, None, false, true)
3241 .unwrap_id();
3242 state.free_reg(id_l, v);
3243 state.free_reg(id_r, v);
3244 let id = state.alloc_reg_tgt(tgt_id);
3245 output.push(instr(id_l, id_r, id));
3246 id
3247}
3248
3249#[allow(clippy::too_many_arguments)]
3253fn compile_shift_op(
3254 instr: fn(u16, u16, u16) -> Instr,
3255 symbol: &'static str,
3256 l: &Expr,
3257 r: &Expr,
3258 span_l: Span,
3259 span_r: Span,
3260 tgt_id: Option<u16>,
3261 v: &mut Vec<Variable>,
3262 ctx: Ctx,
3263 state: &mut State<'_>,
3264 output: &mut Vec<Instr>,
3265) -> u16 {
3266 if let Expr::Int(count) = r
3267 && !shift_count_in_range(*count)
3268 && !matches!(
3269 l.infer_type(v, ctx, state),
3270 DataType::Struct(_) | DataType::Enum(_)
3271 )
3272 {
3273 error_shift_count_out_of_range(*count, span_l.extend(span_r), ctx.file_idx, state.sources);
3274 }
3275 int_op2(
3276 instr, symbol, l, r, span_l, span_r, tgt_id, v, ctx, state, output,
3277 )
3278}
3279
3280fn compile_bit_not_op(
3282 l: &Expr,
3283 span_l: Span,
3284 span_r: Span,
3285 tgt_id: Option<u16>,
3286 v: &mut Vec<Variable>,
3287 ctx: Ctx,
3288 state: &mut State<'_>,
3289 output: &mut Vec<Instr>,
3290) -> u16 {
3291 let operand_type = l.infer_type(v, ctx, state);
3292 if let Some(id) = compile_operator_method(
3293 "~",
3294 l,
3295 None,
3296 &operand_type,
3297 &operand_type,
3298 span_l,
3299 span_r,
3300 tgt_id,
3301 v,
3302 ctx,
3303 state,
3304 output,
3305 ) {
3306 return id;
3307 }
3308 let id_l = l
3309 .compile(v, ctx, state, output, None, false, true)
3310 .unwrap_id();
3311 state.free_reg(id_l, v);
3312 let id = state.alloc_reg_tgt(tgt_id);
3313 if operand_type != DataType::Int {
3314 compiler_errors::error_op(
3315 &DataType::Null,
3316 &operand_type,
3317 "~",
3318 span_l,
3319 span_r,
3320 ctx.file_idx,
3321 state.sources,
3322 state.type_names(),
3323 );
3324 }
3325 output.push(Instr::BitNotInt(id_l, id));
3326 id
3327}
3328
3329fn compile_bool_neg_op(
3330 l: &Expr,
3331 span_l: Span,
3332 span_r: Span,
3333 tgt_id: Option<u16>,
3334 v: &mut Vec<Variable>,
3335 ctx: Ctx,
3336 state: &mut State<'_>,
3337 output: &mut Vec<Instr>,
3338) -> u16 {
3339 let operand_type = l.infer_type(v, ctx, state);
3340 let id_l = l
3341 .compile(v, ctx, state, output, None, false, true)
3342 .unwrap_id();
3343 state.free_reg(id_l, v);
3344 let id = state.alloc_reg_tgt(tgt_id);
3345 if operand_type != DataType::Bool {
3346 compiler_errors::error_op(
3347 &DataType::Null,
3348 &operand_type,
3349 "!",
3350 span_l,
3351 span_r,
3352 ctx.file_idx,
3353 state.sources,
3354 state.type_names(),
3355 );
3356 }
3357 output.push(Instr::NegBool(id_l, id));
3358 id
3359}
3360
3361fn compile_inline_condition_branch(
3368 branch: &[Expr],
3369 v: &mut Vec<Variable>,
3370 ctx: Ctx,
3371 state: &mut State<'_>,
3372 output: &mut Vec<Instr>,
3373 tgt_id: u16,
3374 as_fn_value: bool,
3375) {
3376 let regs_before = state.registers.len() as u16;
3377 let output_len = output.len();
3378 output.extend(compile_expr(
3379 &branch[..branch.len() - 1],
3380 v,
3381 ctx.advance_offset(output.len() as u16),
3382 state,
3383 ));
3384 let last = &branch[branch.len() - 1];
3388 let val_id = if as_fn_value {
3389 compile_fn_value(last, v, ctx, state, output, Some(tgt_id))
3390 } else {
3391 last.compile(v, ctx, state, output, Some(tgt_id), false, true)
3392 .unwrap_id()
3393 };
3394 state.free_scope_registers(regs_before, &output[output_len..], v);
3395 if val_id != tgt_id {
3396 output.push(Instr::Mov(val_id, tgt_id));
3397 }
3398}
3399
3400fn compile_inline_condition(
3401 main_condition: &Expr,
3402 code: &[Expr],
3403 span: Span,
3404 condition_span: Span,
3405 v: &mut Vec<Variable>,
3406 ctx: Ctx,
3407 state: &mut State<'_>,
3408 output: &mut Vec<Instr>,
3409 tgt_id: Option<u16>,
3410 as_fn_value: bool,
3411) -> u16 {
3412 let return_id = state.alloc_reg_tgt(tgt_id);
3413
3414 let main_code_limit = code
3416 .iter()
3417 .position(|x| matches!(x, Expr::ElseIfBlock(_, _, _) | Expr::ElseBlock(_)))
3418 .unwrap_or(code.len());
3419
3420 let condition_blocks_count = code.len() - main_code_limit;
3421 let mut false_jumps: Vec<Vec<usize>> = Vec::with_capacity(condition_blocks_count + 1);
3423 let mut jmp_markers: Vec<usize> = Vec::with_capacity(condition_blocks_count);
3424 let mut condition_markers: Vec<usize> = Vec::with_capacity(condition_blocks_count);
3425
3426 let v_len = v.len();
3428 false_jumps.push(compile_guard(
3429 main_condition,
3430 condition_span,
3431 &code[..main_code_limit],
3432 v,
3433 ctx,
3434 state,
3435 output,
3436 ));
3437
3438 compile_inline_condition_branch(
3439 &code[..main_code_limit],
3440 v,
3441 ctx,
3442 state,
3443 output,
3444 return_id,
3445 as_fn_value,
3446 );
3447 v.truncate(v_len);
3448 if main_code_limit != code.len() {
3449 output.push(Instr::Jmp(0));
3450 jmp_markers.push(output.len() - 1);
3451 }
3452
3453 let mut else_exists = false;
3454 for elem in &code[main_code_limit..] {
3455 if let Expr::ElseIfBlock(condition, code, condition_span) = elem {
3456 condition_markers.push(output.len());
3457 false_jumps.push(compile_guard(
3458 condition,
3459 *condition_span,
3460 code,
3461 v,
3462 ctx,
3463 state,
3464 output,
3465 ));
3466 compile_inline_condition_branch(code, v, ctx, state, output, return_id, as_fn_value);
3467 v.truncate(v_len);
3468 output.push(Instr::Jmp(0));
3469 jmp_markers.push(output.len() - 1);
3470 } else if let Expr::ElseBlock(code) = elem {
3471 else_exists = true;
3472 condition_markers.push(output.len());
3473 compile_inline_condition_branch(code, v, ctx, state, output, return_id, as_fn_value);
3474 }
3475 }
3476 if !else_exists {
3477 error_conditional_expression_without_else(span, ctx.file_idx, state.sources);
3478 }
3479
3480 for y in jmp_markers {
3481 let diff = output.len() - y;
3482 output[y] = Instr::Jmp(diff as u16);
3483 }
3484 for (i, jumps) in false_jumps.iter().enumerate() {
3487 let target = condition_markers[i];
3488 for &y in jumps {
3489 set_jmp_size(&mut output[y], (target - y) as u16);
3490 }
3491 }
3492 return_id
3493}
3494
3495fn compile_array_index_assignment(
3496 array: &Expr,
3497 index: &Expr,
3498 value: &Expr,
3499 index_span: Span,
3500 elem_span: Span,
3501 v: &mut Vec<Variable>,
3502 ctx: Ctx,
3503 state: &mut State<'_>,
3504 output: &mut Vec<Instr>,
3505) {
3506 let array_type = array.infer_type(v, ctx, state);
3507 if matches!(array_type, DataType::Map(_)) {
3508 compile_map_entry_assignment(
3509 array,
3510 &array_type,
3511 index,
3512 value,
3513 index_span,
3514 elem_span,
3515 v,
3516 ctx,
3517 state,
3518 output,
3519 );
3520 return;
3521 }
3522 if !array_type.is_indexable() {
3523 error_type_not_indexable(
3524 &array_type,
3525 index_span,
3526 false,
3527 ctx.file_idx,
3528 state.sources,
3529 state.type_names(),
3530 );
3531 }
3532 let id = array
3534 .compile(v, ctx, state, output, None, false, true)
3535 .unwrap_id();
3536
3537 let final_id = index
3538 .compile(v, ctx, state, output, None, false, true)
3539 .unwrap_id();
3540
3541 let elem_type = value.infer_type(v, ctx, state);
3542 let elem_id = value
3543 .compile(v, ctx, state, output, None, false, true)
3544 .unwrap_id();
3545 state.free_reg(elem_id, v);
3546 if matches!(&array_type, DataType::Array(Some(held))
3549 if !param_type_matches(held, &elem_type, state.generics))
3550 || (array_type == DataType::String && elem_type != DataType::String)
3551 {
3552 error_cannot_push_type_to_array(
3553 &array_type,
3554 &elem_type,
3555 index_span,
3556 elem_span,
3557 ctx.file_idx,
3558 state.sources,
3559 state.type_names(),
3560 );
3561 }
3562
3563 let to_push = if array_type == DataType::String {
3564 Instr::SetElementString(id, elem_id, final_id)
3565 } else {
3566 Instr::SetElementObj(id, elem_id, final_id)
3567 };
3568 output.push(to_push);
3569 state.add_to_src(ctx, output, index_span);
3570 state.free_reg(id, v);
3571}
3572
3573fn compile_map_entry_assignment(
3578 map: &Expr,
3579 map_type: &DataType,
3580 key: &Expr,
3581 value: &Expr,
3582 key_span: Span,
3583 value_span: Span,
3584 v: &mut Vec<Variable>,
3585 ctx: Ctx,
3586 state: &mut State<'_>,
3587 output: &mut Vec<Instr>,
3588) {
3589 check_map_key(map_type, key, key_span, v, ctx, state);
3590 let value_type = value.infer_type(v, ctx, state);
3591 if let DataType::Map(entry) = map_type
3592 && let Some(expected) = &entry.1
3593 && !param_type_matches(expected, &value_type, state.generics)
3594 {
3595 error_invalid_type(
3596 expected,
3597 &value_type,
3598 value_span,
3599 None,
3600 Some(format_args!(
3601 "The map holds {} values",
3602 state.type_names().of(expected)
3603 )),
3604 ctx.file_idx,
3605 state.sources,
3606 state.type_names(),
3607 );
3608 }
3609 if matches!(map_type, DataType::Map(m) if m.0.is_none() && m.1.is_none()) {
3610 let key_type = key.infer_type(v, ctx, state);
3611 pin_inserted_entry(map, &key_type, &value_type, v);
3612 }
3613 let map_id = map
3614 .compile(v, ctx, state, output, None, false, true)
3615 .unwrap_id();
3616 let key_id = key
3617 .compile(v, ctx, state, output, None, false, true)
3618 .unwrap_id();
3619 let value_id = value
3620 .compile(v, ctx, state, output, None, false, true)
3621 .unwrap_id();
3622 state.free_reg(key_id, v);
3623 state.free_reg(value_id, v);
3624 output.push(Instr::MapInsertReg(map_id, key_id, value_id));
3625 state.free_reg(map_id, v);
3626}
3627
3628fn compile_struct_field_assignment(
3629 struct_expr: &Expr,
3630 field: &SmolStr,
3631 new_val: &Expr,
3632 struct_span: Span,
3633 field_span: Span,
3634 value_span: Span,
3635 v: &mut Vec<Variable>,
3636 ctx: Ctx,
3637 state: &mut State<'_>,
3638 output: &mut Vec<Instr>,
3639) {
3640 let t = struct_expr.infer_type(v, ctx, state);
3641 let new_val_type = new_val.infer_type(v, ctx, state);
3642 let DataType::Struct(struct_id) = t else {
3643 error_invalid_type(
3644 &DataType::Struct(0),
3645 &t,
3646 struct_span,
3647 None,
3648 None,
3649 ctx.file_idx,
3650 state.sources,
3651 state.type_names(),
3652 );
3653 };
3654 let field_struct = &state.structs[struct_id as usize];
3655 let Some(field_index) = field_struct
3656 .fields
3657 .iter()
3658 .position(|(expected_field_name, _, _)| expected_field_name == field)
3659 else {
3660 compiler_errors::error_struct_unknown_field(
3661 ctx.file_idx,
3662 field_span,
3663 field,
3664 &field_struct.name,
3665 &field_struct.fields,
3666 state.sources,
3667 );
3668 };
3669 let struct_name = field_struct.name.clone();
3670 let as_fn_value = compile_struct_field_type(
3673 &struct_name,
3674 struct_id as usize,
3675 field_index,
3676 new_val,
3677 value_span,
3678 v,
3679 ctx,
3680 state,
3681 output,
3682 );
3683 let field_index = field_index as u16;
3684 let id = struct_expr
3685 .compile(v, ctx, state, output, None, false, true)
3686 .unwrap_id();
3687 if let Expr::Add(read, added, _, _) = new_val
3692 && let (Expr::GetStructField(read_struct, read_field, _, _), Expr::Var(name, _)) =
3693 (read.as_ref(), struct_expr)
3694 && read_field == field
3695 && matches!(read_struct.as_ref(), Expr::Var(read_name, _) if read_name == name)
3696 && state.structs[struct_id as usize].fields[field_index as usize].1 == DataType::Float
3697 && new_val_type == DataType::Float
3698 && calls_nothing(added, v, ctx, state)
3699 {
3700 let added_id = added
3701 .compile(v, ctx, state, output, None, false, true)
3702 .unwrap_id();
3703 state.free_reg(added_id, v);
3704 output.push(Instr::AddFieldFloat(id, added_id, field_index));
3705 return;
3706 }
3707 let new_elem_reg_id = compile_element(new_val, as_fn_value, v, ctx, state, output);
3708 output.push(Instr::SetFieldStruct(id, new_elem_reg_id, field_index));
3709}
3710
3711fn calls_nothing(expr: &Expr, v: &mut Vec<Variable>, ctx: Ctx, state: &mut State<'_>) -> bool {
3714 match expr {
3715 Expr::Var(..) | Expr::Float(_) | Expr::Int(_) => true,
3716 Expr::GetStructField(inner, _, _, _) => calls_nothing(inner, v, ctx, state),
3717 Expr::Add(l, r, _, _)
3718 | Expr::Sub(l, r, _, _)
3719 | Expr::Mul(l, r, _, _)
3720 | Expr::Div(l, r, _, _) => {
3721 [l, r].into_iter().all(|side| {
3724 matches!(
3725 side.infer_type(v, ctx, state),
3726 DataType::Float | DataType::Int
3727 ) && calls_nothing(side, v, ctx, state)
3728 })
3729 }
3730 _ => false,
3731 }
3732}
3733
3734#[allow(clippy::too_many_arguments)]
3740fn compile_guard(
3741 condition: &Expr,
3742 condition_span: Span,
3743 body: &[Expr],
3744 v: &mut Vec<Variable>,
3745 ctx: Ctx,
3746 state: &mut State<'_>,
3747 output: &mut Vec<Instr>,
3748) -> Vec<usize> {
3749 let captured = type_test::captured_binders(condition, body);
3750 let outer = std::mem::replace(&mut state.captured_binders, captured);
3751 let (true_jump_idxs, false_jump_idxs) =
3752 compile_short_circuit_condition(condition, condition_span, v, ctx, state, output, false);
3753 state.captured_binders = outer;
3754 let body_start = output.len();
3755 for j in true_jump_idxs {
3756 set_jmp_size(&mut output[j], (body_start - j) as u16);
3757 }
3758 false_jump_idxs
3759}
3760
3761fn compile_condition(
3762 main_condition: &Expr,
3763 code: &[Expr],
3764 condition_span: Span,
3765 v: &mut Vec<Variable>,
3766 ctx: Ctx,
3767 state: &mut State<'_>,
3768 output: &mut Vec<Instr>,
3769) {
3770 let main_code_limit = code
3772 .iter()
3773 .position(|x| matches!(x, Expr::ElseIfBlock(_, _, _) | Expr::ElseBlock(_)))
3774 .unwrap_or(code.len());
3775
3776 let condition_blocks_count = code.len() - main_code_limit;
3777 let mut conditional_false_jmp_idxs: Vec<Vec<usize>> =
3779 Vec::with_capacity(condition_blocks_count + 1);
3780 let mut jmp_instr_idx: Vec<usize> = Vec::with_capacity(condition_blocks_count);
3781 let mut condition_markers: Vec<usize> = Vec::with_capacity(condition_blocks_count);
3782
3783 let v_len = v.len();
3786 let false_jump_idxs = compile_guard(
3787 main_condition,
3788 condition_span,
3789 &code[0..main_code_limit],
3790 v,
3791 ctx,
3792 state,
3793 output,
3794 );
3795 conditional_false_jmp_idxs.push(false_jump_idxs);
3796
3797 let cond_code = compile_expr(
3799 &code[0..main_code_limit],
3800 v,
3801 ctx.advance_offset(output.len() as u16),
3802 state,
3803 );
3804 output.extend(cond_code);
3805 v.truncate(v_len);
3806 if main_code_limit != code.len() {
3807 output.push(Instr::Jmp(0));
3808 jmp_instr_idx.push(output.len() - 1);
3809 }
3810
3811 for elem in &code[main_code_limit..] {
3812 if let Expr::ElseIfBlock(condition, code, condition_span) = elem {
3813 condition_markers.push(output.len());
3814 let false_jump_idxs =
3815 compile_guard(condition, *condition_span, code, v, ctx, state, output);
3816 conditional_false_jmp_idxs.push(false_jump_idxs);
3817 let cond_code = compile_expr(code, v, ctx.advance_offset(output.len() as u16), state);
3818 output.extend(cond_code);
3819 v.truncate(v_len);
3820 output.push(Instr::Jmp(0));
3821 jmp_instr_idx.push(output.len() - 1);
3822 } else if let Expr::ElseBlock(code) = elem {
3823 condition_markers.push(output.len());
3824 let cond_code = compile_expr(code, v, ctx.advance_offset(output.len() as u16), state);
3825 output.extend(cond_code);
3826 }
3827 }
3828
3829 for y in jmp_instr_idx {
3830 let diff = output.len() - y;
3831 output[y] = Instr::Jmp(diff as u16);
3832 }
3833 for (cm_idx, false_idxs) in conditional_false_jmp_idxs.iter().enumerate() {
3835 let target = if cm_idx < condition_markers.len() {
3836 condition_markers[cm_idx]
3837 } else {
3838 output.len()
3839 };
3840 for &y in false_idxs {
3841 set_jmp_size(&mut output[y], (target - y) as u16);
3842 }
3843 }
3844}
3845
3846fn compile_while_loop(
3847 condition: &Expr,
3848 code: &[Expr],
3849 condition_span: Span,
3850 v: &mut Vec<Variable>,
3851 ctx: Ctx,
3852 state: &mut State<'_>,
3853 output: &mut Vec<Instr>,
3854) {
3855 let output_len_before = output.len();
3856
3857 let v_len = v.len();
3858 let false_jump_idxs = compile_guard(condition, condition_span, code, v, ctx, state, output);
3859
3860 let loop_id = ctx.block_id + 1;
3862
3863 let mut cond_code = compile_expr(
3864 code,
3865 v,
3866 ctx.no_single_run().advance_offset(output.len() as u16),
3867 state,
3868 );
3869 v.truncate(v_len);
3870
3871 let exit = output.len() + cond_code.len() + 1;
3872 for j in false_jump_idxs {
3873 set_jmp_size(&mut output[j], (exit - j) as u16);
3874 }
3875
3876 let cond_len = (output.len() - output_len_before) as u16;
3877 let body_len = cond_code.len() as u16;
3878 let len = cond_len + body_len; parse_loop_flow_control(&mut cond_code, loop_id, body_len + 1, false, false);
3881 output.extend(cond_code);
3882 output.push(Instr::JmpBack(len));
3883}
3884
3885fn compile_for_loop(
3886 var_name: &SmolStr,
3887 array: &Expr,
3888 code: &[Expr],
3889 span: Span,
3890 v: &mut Vec<Variable>,
3891 ctx: Ctx,
3892 state: &mut State<'_>,
3893 output: &mut Vec<Instr>,
3894) {
3895 if state.optimize && compile_slice_for_loop(var_name, array, code, span, v, ctx, state, output)
3896 {
3897 return;
3898 }
3899
3900 let array_type = array.infer_type(v, ctx, state);
3902 let mut array = array
3903 .compile(v, ctx, state, output, None, false, true)
3904 .unwrap_id();
3905
3906 if matches!(array_type, DataType::Map(_)) {
3909 let keys_reg = state.alloc_reg();
3910 output.push(Instr::CallLibFunc(LibFunc::Keys, array, keys_reg));
3911 array = keys_reg;
3912 }
3913
3914 let array_len_id = state.alloc_reg();
3915
3916 output.push(Instr::CallLibFunc(LibFunc::Len, array, array_len_id));
3917
3918 let index_id = if ctx.single_run {
3920 state.registers.push(0.into());
3921 (state.registers.len() - 1) as u16
3922 } else {
3923 let id = state.alloc_reg();
3924 output.push(Instr::SetInt(id, 0));
3925 id
3926 };
3927 compile_walk(
3928 var_name,
3929 &array_type,
3930 array,
3931 index_id,
3932 array_len_id,
3933 code,
3934 span,
3935 v,
3936 ctx,
3937 state,
3938 output,
3939 );
3940}
3941
3942fn compile_slice_for_loop(
3961 var_name: &SmolStr,
3962 array: &Expr,
3963 code: &[Expr],
3964 span: Span,
3965 v: &mut Vec<Variable>,
3966 ctx: Ctx,
3967 state: &mut State<'_>,
3968 output: &mut Vec<Instr>,
3969) -> bool {
3970 let Expr::ArrayGetSlice(list, start, end, slice_span) = array else {
3971 return false;
3972 };
3973 if state.generics.defines_operator_methods() {
3976 return false;
3977 }
3978 let mut receivers: Vec<SmolStr> = Vec::new();
3979 let mut declared: Vec<SmolStr> = vec![var_name.clone()];
3980 if !block_changes_only_pushed_lists(code, &mut receivers, &mut declared) {
3981 return false;
3982 }
3983 for name in &receivers {
3984 if declared.contains(name)
3986 || !v
3987 .iter()
3988 .rfind(|var| var.name == *name)
3989 .is_some_and(|var| matches!(var.var_type, DataType::Array(_)))
3990 {
3991 return false;
3992 }
3993 }
3994 let array_type = list.infer_type(v, ctx, state);
3995 if !matches!(array_type, DataType::Array(_))
3996 || start.infer_type(v, ctx, state) != DataType::Int
3997 || end
3998 .as_ref()
3999 .is_some_and(|end| end.infer_type(v, ctx, state) != DataType::Int)
4000 {
4001 return false;
4002 }
4003
4004 let list_id = list
4005 .compile(v, ctx, state, output, None, false, true)
4006 .unwrap_id();
4007 let start_id = start
4008 .compile(v, ctx, state, output, None, false, true)
4009 .unwrap_id();
4010 let end_id = if let Some(end) = end {
4011 end.compile(v, ctx, state, output, None, false, true)
4012 .unwrap_id()
4013 } else {
4014 let len_id = state.alloc_reg();
4016 output.push(Instr::CallLibFunc(LibFunc::Len, list_id, len_id));
4017 len_id
4018 };
4019
4020 let walked = state.alloc_reg();
4023 let index_id = state.alloc_reg();
4024 let end_at = state.alloc_reg();
4025 let zero = state.const_int_register(0);
4026
4027 let mut to_copy: Vec<usize> = Vec::new();
4030 output.push(Instr::CallLibFunc(LibFunc::Len, list_id, end_at));
4031 to_copy.push(output.len());
4032 output.push(Instr::InfIntJmp(start_id, zero, 0));
4033 to_copy.push(output.len());
4034 output.push(Instr::SupIntJmp(end_id, end_at, 0));
4035 to_copy.push(output.len());
4036 output.push(Instr::SupIntJmp(start_id, end_id, 0));
4037 output.push(Instr::Mov(list_id, walked));
4038 output.push(Instr::Mov(start_id, index_id));
4039 output.push(Instr::Mov(end_id, end_at));
4040 let skip_copy = output.len();
4041 output.push(Instr::Jmp(0));
4042
4043 let copy_at = output.len();
4044 for at in to_copy {
4045 set_jmp_size(&mut output[at], (copy_at - at) as u16);
4046 }
4047 output.push(Instr::StoreFuncArg(end_id));
4048 output.push(Instr::GetSliceArray(list_id, start_id, walked));
4049 state.add_to_src(ctx, output, *slice_span);
4050 output.push(Instr::SetInt(index_id, 0));
4051 output.push(Instr::CallLibFunc(LibFunc::Len, walked, end_at));
4052 let copy_len = (output.len() - skip_copy) as u16;
4053 set_jmp_size(&mut output[skip_copy], copy_len);
4054
4055 state.free_reg(start_id, v);
4056 state.free_reg(end_id, v);
4057 compile_walk(
4058 var_name,
4059 &array_type,
4060 walked,
4061 index_id,
4062 end_at,
4063 code,
4064 span,
4065 v,
4066 ctx,
4067 state,
4068 output,
4069 );
4070 true
4071}
4072
4073fn block_changes_only_pushed_lists(
4077 code: &[Expr],
4078 receivers: &mut Vec<SmolStr>,
4079 declared: &mut Vec<SmolStr>,
4080) -> bool {
4081 code.iter()
4082 .all(|expr| changes_only_pushed_lists(expr, receivers, declared))
4083}
4084
4085fn changes_only_pushed_lists(
4087 expr: &Expr,
4088 receivers: &mut Vec<SmolStr>,
4089 declared: &mut Vec<SmolStr>,
4090) -> bool {
4091 match expr {
4092 Expr::Var(..)
4093 | Expr::Int(_)
4094 | Expr::Float(_)
4095 | Expr::Bool(_)
4096 | Expr::String(_)
4097 | Expr::Null
4098 | Expr::Break
4099 | Expr::Continue => true,
4100 Expr::GetStructField(inner, _, _, _)
4101 | Expr::BoolNeg(inner, _, _)
4102 | Expr::Neg(inner, _, _)
4103 | Expr::BitNot(inner, _, _) => changes_only_pushed_lists(inner, receivers, declared),
4104 Expr::ArrayGetIndex(l, r, _)
4105 | Expr::Mul(l, r, _, _)
4106 | Expr::Div(l, r, _, _)
4107 | Expr::Add(l, r, _, _)
4108 | Expr::Sub(l, r, _, _)
4109 | Expr::Mod(l, r, _, _)
4110 | Expr::Pow(l, r, _, _)
4111 | Expr::BitAnd(l, r, _, _)
4112 | Expr::BitOr(l, r, _, _)
4113 | Expr::BitXor(l, r, _, _)
4114 | Expr::Shl(l, r, _, _)
4115 | Expr::Shr(l, r, _, _)
4116 | Expr::Eq(l, r, _, _)
4117 | Expr::NotEq(l, r, _, _)
4118 | Expr::Sup(l, r, _, _)
4119 | Expr::SupEq(l, r, _, _)
4120 | Expr::Inf(l, r, _, _)
4121 | Expr::InfEq(l, r, _, _)
4122 | Expr::BoolAnd(l, r, _, _)
4123 | Expr::BoolOr(l, r, _, _) => {
4124 changes_only_pushed_lists(l, receivers, declared)
4125 && changes_only_pushed_lists(r, receivers, declared)
4126 }
4127 Expr::VarDeclare(name, value, _) => {
4128 declared.push(name.clone());
4129 changes_only_pushed_lists(value, receivers, declared)
4130 }
4131 Expr::VarAssign(_, value, _, _) => changes_only_pushed_lists(value, receivers, declared),
4132 Expr::ReturnVal(value) => value
4133 .as_ref()
4134 .as_ref()
4135 .is_none_or(|value| changes_only_pushed_lists(value, receivers, declared)),
4136 Expr::ObjFunctionCall(obj, args, namespace, _, _, _, type_args) => {
4137 let Expr::Var(name, _) = obj.as_ref() else {
4138 return false;
4139 };
4140 if !matches!(&**namespace, [method] if method == "push")
4141 || !type_args.is_empty()
4142 || !args
4143 .iter()
4144 .all(|arg| changes_only_pushed_lists(arg, receivers, declared))
4145 {
4146 return false;
4147 }
4148 if !receivers.contains(name) {
4149 receivers.push(name.clone());
4150 }
4151 true
4152 }
4153 Expr::Condition(condition, body, _, _)
4154 | Expr::ElseIfBlock(condition, body, _)
4155 | Expr::WhileBlock(condition, body, _) => {
4156 changes_only_pushed_lists(condition, receivers, declared)
4157 && block_changes_only_pushed_lists(body, receivers, declared)
4158 }
4159 Expr::ElseBlock(body) | Expr::EvalBlock(body) | Expr::LoopBlock(body) => {
4160 block_changes_only_pushed_lists(body, receivers, declared)
4161 }
4162 _ => false,
4163 }
4164}
4165
4166fn compile_walk(
4170 var_name: &SmolStr,
4171 array_type: &DataType,
4172 array: u16,
4173 index_id: u16,
4174 array_len_id: u16,
4175 code: &[Expr],
4176 span: Span,
4177 v: &mut Vec<Variable>,
4178 ctx: Ctx,
4179 state: &mut State<'_>,
4180 output: &mut Vec<Instr>,
4181) {
4182 let real_var = var_name.as_str() != "_";
4183
4184 let current_element_id = if real_var { state.alloc_reg() } else { 0 };
4186
4187 let captured = real_var && code_captures_variable(var_name, code);
4191 let element_cell_id = if captured { state.alloc_reg() } else { 0 };
4192
4193 let v_len = v.len();
4194
4195 let is_str = *array_type == DataType::String;
4196
4197 if real_var {
4198 v.push(Variable {
4199 declared: None,
4200 name: var_name.clone(),
4201 register_id: if captured {
4202 element_cell_id
4203 } else {
4204 current_element_id
4205 },
4206 cell: captured,
4207 var_type: match array_type {
4208 DataType::String => DataType::String,
4209 DataType::Array(a_type) => a_type.clone().map_or(DataType::Null, |t| *t),
4210 DataType::Map(m) => m.0.clone().unwrap_or(DataType::Unknown),
4212 t => {
4213 error_type_not_indexable(
4214 t,
4215 span,
4216 true,
4217 ctx.file_idx,
4218 state.sources,
4219 state.type_names(),
4220 );
4221 }
4222 },
4223 });
4224 }
4225 let loop_id = ctx.block_id + 1;
4226
4227 let pending = u16::from(real_var) + u16::from(captured);
4230
4231 let jmp_idx = output.len();
4240 output.push(Instr::SupEqIntJmp(index_id, array_len_id, 0));
4241
4242 let regs_before = state.registers.len() as u16;
4243 let body = compile_expr(
4244 code,
4245 v,
4246 ctx.no_single_run()
4247 .advance_offset(output.len() as u16 + pending),
4248 state,
4249 );
4250 v.truncate(v_len);
4252 state.free_scope_registers(regs_before, &body, v);
4253 let body_len = body.len() as u16;
4254
4255 if real_var {
4257 if is_str {
4258 output.push(Instr::GetIndexString(array, index_id, current_element_id));
4259 state.add_to_src(ctx, output, span);
4263 } else {
4264 output.push(Instr::GetIndexArray(array, index_id, current_element_id));
4265 }
4266 if captured {
4267 output.push(Instr::NewCell(current_element_id, element_cell_id));
4268 }
4269 }
4270 output.extend(body);
4271 output.push(Instr::StepIntJmpBack(
4273 index_id,
4274 array_len_id,
4275 body_len + pending,
4276 ));
4277
4278 let exit_size = (output.len() - jmp_idx) as u16;
4279 output[jmp_idx] = Instr::SupEqIntJmp(index_id, array_len_id, exit_size);
4280 parse_loop_flow_control(&mut output[jmp_idx + 1..], loop_id, exit_size, true, false);
4281
4282 if ctx.single_run {
4283 state.free_reg(array_len_id, v);
4284 state.free_reg(index_id, v);
4285 if real_var {
4286 state.free_reg(current_element_id, v);
4287 }
4288 }
4289}
4290
4291fn compile_int_for_loop(
4292 var_name: &SmolStr,
4293 start_elem: &Expr,
4294 end_elem: &Expr,
4295 code: &[Expr],
4296 span1: Span,
4297 span2: Span,
4298 v: &mut Vec<Variable>,
4299 ctx: Ctx,
4300 state: &mut State<'_>,
4301 output: &mut Vec<Instr>,
4302) {
4303 let t1 = start_elem.infer_type(v, ctx, state);
4313 let t2 = end_elem.infer_type(v, ctx, state);
4314 if t1 != DataType::Int {
4315 error_range_invalid_type(span1, &t1, ctx.file_idx, state.sources, state.type_names());
4316 }
4317 if t2 != DataType::Int {
4318 error_range_invalid_type(span2, &t2, ctx.file_idx, state.sources, state.type_names());
4319 }
4320 let output_len = output.len();
4326 let start_elem_id = start_elem
4327 .compile(v, ctx, state, output, None, false, true)
4328 .unwrap_id();
4329 let start_val = state.registers[start_elem_id as usize];
4330 let elem_id = state.alloc_reg();
4331 if let Some(n) = int_immediate(start_val)
4333 && state.const_registers.values().any(|&v| v == start_elem_id)
4334 {
4335 output.push(Instr::SetInt(elem_id, n));
4336 } else if !write_scratch_into(output, output_len, start_elem_id, elem_id, v, state) {
4337 output.push(Instr::Mov(start_elem_id, elem_id));
4338 }
4339 let end_elem_id = end_elem
4340 .compile(v, ctx, state, output, None, false, true)
4341 .unwrap_id();
4342
4343 let captured = code_captures_variable(var_name, code);
4346 let elem_cell_id = if captured { state.alloc_reg() } else { 0 };
4347
4348 let v_len = v.len();
4349 v.push(Variable {
4350 declared: None,
4351 name: var_name.clone(),
4352 register_id: if captured { elem_cell_id } else { elem_id },
4353 cell: captured,
4354 var_type: DataType::Int,
4355 });
4356 let loop_id = ctx.block_id + 1;
4357
4358 let jmp_idx = output.len();
4360 output.push(Instr::SupEqIntJmp(elem_id, end_elem_id, 0));
4361 if captured {
4362 output.push(Instr::NewCell(elem_id, elem_cell_id));
4363 }
4364
4365 let regs_before = state.registers.len() as u16;
4366 let compiled_loop_code = compile_expr(
4367 code,
4368 v,
4369 ctx.no_single_run().advance_offset(output.len() as u16),
4370 state,
4371 );
4372 state.free_scope_registers(regs_before, &compiled_loop_code, v);
4373 let compiled_loop_code_len = compiled_loop_code.len() as u16;
4374
4375 output.extend(compiled_loop_code);
4377
4378 output.push(Instr::StepIntJmpBack(
4381 elem_id,
4382 end_elem_id,
4383 compiled_loop_code_len + u16::from(captured),
4384 ));
4385
4386 let exit_size = (output.len() - jmp_idx) as u16;
4387 output[jmp_idx] = Instr::SupEqIntJmp(elem_id, end_elem_id, exit_size);
4388
4389 parse_loop_flow_control(&mut output[jmp_idx + 1..], loop_id, exit_size, true, false);
4390 v.truncate(v_len);
4391
4392 if ctx.single_run {
4393 state.free_reg(end_elem_id, v);
4394 state.free_reg(elem_id, v);
4395 }
4396}
4397
4398fn compile_loop_block(
4399 code: &[Expr],
4400 v: &mut Vec<Variable>,
4401 ctx: Ctx,
4402 state: &mut State<'_>,
4403 output: &mut Vec<Instr>,
4404) {
4405 let loop_id = ctx.block_id + 1;
4406 let regs_before = state.registers.len() as u16;
4407 let mut compiled = compile_expr(
4408 code,
4409 v,
4410 ctx.no_single_run().advance_offset(output.len() as u16),
4411 state,
4412 );
4413 state.free_scope_registers(regs_before, &compiled, v);
4414 let code_length = compiled.len() as u16;
4415 parse_loop_flow_control(&mut compiled, loop_id, code_length + 1, false, true);
4416 output.extend(compiled);
4417 output.push(Instr::JmpBack(code_length));
4418}
4419
4420fn compile_try_catch_block(
4421 e: &[Expr],
4422 err_var: &SmolStr,
4423 catch_code: &[Expr],
4424 v: &mut Vec<Variable>,
4425 ctx: Ctx,
4426 state: &mut State<'_>,
4427 output: &mut Vec<Instr>,
4428) {
4429 output.push(Instr::StartErrorCatch(0, 0)); let err_catch_instr = output.len() - 1;
4431 let main_code = compile_expr(e, v, ctx.advance_offset(output.len() as u16), state);
4437 output.extend(main_code);
4438 output.push(Instr::StopErrorCatch);
4439 output.push(Instr::Jmp(0)); let jmp_catch_instr = output.len() - 1;
4441
4442 let v_len = v.len();
4443 let err_reg_id = state.alloc_reg();
4444 let captured = code_captures_variable(err_var, catch_code);
4447 let err_cell_id = if captured { state.alloc_reg() } else { 0 };
4448 v.push(Variable {
4449 declared: None,
4450 name: err_var.clone(),
4451 register_id: if captured { err_cell_id } else { err_reg_id },
4452 cell: captured,
4453 var_type: DataType::String,
4454 });
4455 output[err_catch_instr] =
4456 Instr::StartErrorCatch((output.len() - err_catch_instr) as u16, err_reg_id);
4457 if captured {
4458 output.push(Instr::NewCell(err_reg_id, err_cell_id));
4459 }
4460 let catch_code = compile_expr(
4461 catch_code,
4462 v,
4463 ctx.advance_offset(output.len() as u16),
4464 state,
4465 );
4466 v.truncate(v_len);
4467 output.extend(catch_code);
4468 output[jmp_catch_instr] = Instr::Jmp((output.len() - jmp_catch_instr) as u16);
4469 state.free_reg(err_reg_id, v);
4470}
4471
4472fn capture_cell(
4479 name: &SmolStr,
4480 span: Span,
4481 v: &mut [Variable],
4482 ctx: Ctx,
4483 state: &mut State<'_>,
4484 output: &mut Vec<Instr>,
4485) -> u16 {
4486 let Some(pos) = v.iter().rposition(|var| var.name == *name) else {
4487 compiler_errors::error_unknown_variable(name, span, v, ctx.file_idx, state.sources);
4488 };
4489 if v[pos].cell {
4490 return v[pos].register_id;
4491 }
4492 let cell_id = state.alloc_reg();
4493 output.push(Instr::NewCell(v[pos].register_id, cell_id));
4494 v[pos].register_id = cell_id;
4495 v[pos].cell = true;
4496 cell_id
4497}
4498
4499#[allow(clippy::too_many_arguments)]
4506fn check_declared_value(
4507 name: &str,
4508 declared: &DataType,
4509 declared_span: Span,
4510 value: &Expr,
4511 value_span: Span,
4512 v: &mut Vec<Variable>,
4513 ctx: Ctx,
4514 state: &mut State<'_>,
4515 output: &mut Vec<Instr>,
4516) {
4517 let value_type = value.infer_type(v, ctx, state);
4518 let fn_value = declared_holds_fn_signature(declared);
4519 if fn_value {
4520 check_declared_fn(value, declared, value_span, output, v, ctx, state);
4521 }
4522 if fn_value
4523 && matches!(declared, DataType::FnValue(_))
4524 && matches!(value_type, DataType::Fn(_) | DataType::FnValue(_))
4525 {
4526 return;
4527 }
4528 let fresh = matches!(value, Expr::Array(..) | Expr::Map(..));
4529 if !let_type_fits(declared, &value_type, fresh, state.generics) {
4530 compiler_errors::error_variable_type_mismatch(
4531 ctx.file_idx,
4532 name,
4533 declared_span,
4534 declared,
4535 value_span,
4536 &value_type,
4537 state.sources,
4538 state.type_names(),
4539 );
4540 }
4541}
4542
4543#[allow(clippy::too_many_arguments)]
4544fn compile_var_declaration(
4545 name: &SmolStr,
4546 value: &Expr,
4547 annotation: Option<&LetAnnotation>,
4548 remaining_code: &[Expr],
4549 v: &mut Vec<Variable>,
4550 ctx: Ctx,
4551 state: &mut State<'_>,
4552 output: &mut Vec<Instr>,
4553) {
4554 if let Some(annotation) = annotation {
4555 let declared = annotation.ty.to_datatype(&mut state.type_ctx(ctx.file_idx));
4556 check_declared_value(
4557 name,
4558 &declared,
4559 annotation.span,
4560 value,
4561 annotation.value_span,
4562 v,
4563 ctx,
4564 state,
4565 output,
4566 );
4567 bind_variable(
4568 name,
4569 value,
4570 declared.clone(),
4571 Some(declared),
4572 remaining_code,
4573 v,
4574 ctx,
4575 state,
4576 output,
4577 );
4578 return;
4579 }
4580 let var_type = value.infer_type(v, ctx, state);
4581 let var_type = match &var_type {
4587 DataType::Fn(fn_id) if code_modifies_variable(name, remaining_code) => {
4588 DataType::FnValue(Box::new(FnValue::from_set(state.new_fn_value_set(*fn_id))))
4589 }
4590 _ => var_type,
4591 };
4592 let var_type = if matches!(var_type, DataType::Fn(_)) {
4596 bare_fn_value_type(value, v, ctx, state).unwrap_or(var_type)
4597 } else {
4598 var_type
4599 };
4600 bind_variable(
4601 name,
4602 value,
4603 var_type,
4604 None,
4605 remaining_code,
4606 v,
4607 ctx,
4608 state,
4609 output,
4610 );
4611}
4612
4613#[allow(clippy::too_many_arguments)]
4616fn bind_variable(
4617 name: &SmolStr,
4618 value: &Expr,
4619 var_type: DataType,
4620 declared: Option<DataType>,
4621 remaining_code: &[Expr],
4622 v: &mut Vec<Variable>,
4623 ctx: Ctx,
4624 state: &mut State<'_>,
4625 output: &mut Vec<Instr>,
4626) {
4627 let as_fn_value = matches!(var_type, DataType::FnValue(_));
4628 let captured = code_captures_variable(name, remaining_code);
4633
4634 let output_len = output.len();
4635 let src_id = if as_fn_value {
4636 compile_fn_value(value, v, ctx, state, output, None)
4637 } else {
4638 value
4639 .compile(v, ctx, state, output, None, ctx.single_run, true)
4640 .unwrap_id()
4641 };
4642 let owned_elsewhere = v.iter().any(|var| var.register_id == src_id)
4646 || state.const_registers.values().any(|®| reg == src_id);
4647 let owner_written = v
4648 .iter()
4649 .filter(|var| var.register_id == src_id)
4650 .any(|var| code_modifies_variable(&var.name, remaining_code));
4651 let written = code_modifies_variable(name, remaining_code);
4652 let needs_own =
4655 !captured && (owner_written || (written && (owned_elsewhere || !ctx.single_run)));
4656 let var_id = if needs_own {
4657 let mutable_id = state.alloc_reg();
4658 if !write_scratch_into(output, output_len, src_id, mutable_id, v, state) {
4659 move_reg_to_reg(output, src_id, mutable_id, state.registers[src_id as usize]);
4660 }
4661 mutable_id
4662 } else {
4663 src_id
4664 };
4665 let var_id = if captured {
4666 let cell_id = state.alloc_reg();
4667 output.push(Instr::NewCell(var_id, cell_id));
4668 state.free_reg(var_id, v);
4669 cell_id
4670 } else {
4671 var_id
4672 };
4673
4674 if let DataType::Fn(fn_id) = &var_type {
4675 let fn_id = *fn_id as usize;
4676 if state.fns[fn_id].direct_calls.contains(name) {
4682 state.fns[fn_id].is_recursive = Some(true);
4683 }
4684 let fn_symbol = SymbolKind::Fn(fn_id as u16);
4685 state
4686 .scope_mut(ctx.file_idx)
4687 .symbols
4688 .push((name.clone(), fn_symbol));
4689 }
4690 v.push(Variable {
4691 declared,
4692 name: name.clone(),
4693 register_id: var_id,
4694 cell: captured,
4695 var_type,
4696 });
4697}
4698
4699fn join_fn_value_set(
4702 held: &FnValue,
4703 fn_id: u16,
4704 span: Span,
4705 output: &mut Vec<Instr>,
4706 v: &mut Vec<Variable>,
4707 ctx: Ctx,
4708 state: &mut State<'_>,
4709) {
4710 let Some(set_idx) = held.set else {
4711 return;
4712 };
4713 let Some(set) = state
4714 .indirect_registers
4715 .value_sets
4716 .get_mut(set_idx as usize)
4717 else {
4718 return;
4719 };
4720 if set.candidates.contains(&fn_id) {
4721 return;
4722 }
4723 set.candidates.push(fn_id);
4724 let used_at = set.used_at.clone();
4725 for arg_types in used_at {
4726 ensure_indirect_impl(fn_id as usize, &arg_types, span, output, v, ctx, state);
4727 }
4728}
4729
4730fn write_scratch_into(
4740 output: &mut [Instr],
4741 from: usize,
4742 scratch: u16,
4743 dest: u16,
4744 v: &[Variable],
4745 state: &mut State<'_>,
4746) -> bool {
4747 let fresh = output.len() > from
4748 && output[from..]
4749 .iter()
4750 .rev()
4751 .find_map(|instr| instr.get_tgt_id())
4752 == Some(scratch)
4753 && !v.iter().any(|var| var.register_id == scratch)
4754 && !state.const_registers.values().any(|®| reg == scratch)
4755 && !state.reserved_registers.contains(&scratch);
4756 if !fresh || !move_to_id(&mut output[from..], dest) {
4757 return false;
4758 }
4759 state.free_reg(scratch, v);
4760 true
4761}
4762
4763#[allow(clippy::too_many_arguments)]
4764fn compile_var_assignment(
4765 name: &SmolStr,
4766 value: &Expr,
4767 span: Span,
4768 value_span: Span,
4769 v: &mut Vec<Variable>,
4770 ctx: Ctx,
4771 state: &mut State<'_>,
4772 output: &mut Vec<Instr>,
4773) {
4774 let var_pos = v.iter().rposition(|x| x.name == *name).unwrap_or_else(|| {
4775 compiler_errors::error_unknown_variable(name, span, v, ctx.file_idx, state.sources);
4776 });
4777 let var_type = if let Some(declared) = v[var_pos].declared.clone() {
4780 check_declared_value(
4781 name, &declared, span, value, value_span, v, ctx, state, output,
4782 );
4783 declared
4784 } else {
4785 value.infer_type(v, ctx, state)
4786 };
4787 let id = v[var_pos].register_id;
4788
4789 let var_type = match (&v[var_pos].var_type, &var_type) {
4790 (DataType::FnValue(held), DataType::Fn(fn_id)) if held.sig.is_none() => {
4791 let held = held.clone();
4792 join_fn_value_set(&held, *fn_id, span, output, v, ctx, state);
4793 DataType::FnValue(held)
4794 }
4795 _ => var_type,
4796 };
4797 let as_fn_value = matches!(var_type, DataType::FnValue(_));
4798
4799 if v[var_pos].cell {
4802 let value_id = if as_fn_value {
4803 compile_fn_value(value, v, ctx, state, output, None)
4804 } else {
4805 value
4806 .compile(v, ctx, state, output, None, false, true)
4807 .unwrap_id()
4808 };
4809 output.push(Instr::StoreCell(id, value_id));
4810 state.free_reg(value_id, v);
4811 v[var_pos].var_type = var_type;
4812 return;
4813 }
4814
4815 if var_type == DataType::Int {
4816 let inc_dec: Option<(bool, &str)> = match value {
4818 Expr::Add(l, r, _, _) => {
4820 let src = if matches!(r.as_ref(), Expr::Int(1)) {
4821 Some(l.as_ref())
4822 } else if matches!(l.as_ref(), Expr::Int(1)) {
4823 Some(r.as_ref())
4824 } else {
4825 None
4826 };
4827 src.and_then(|e| {
4828 if let Expr::Var(src_name, _) = e {
4829 v.iter()
4830 .rfind(|x| x.name == *src_name)
4831 .filter(|x| x.var_type == DataType::Int && !x.cell)
4832 .map(|_| (true, src_name.as_str()))
4833 } else {
4834 None
4835 }
4836 })
4837 }
4838 Expr::Sub(l, r, _, _) => {
4840 if matches!(r.as_ref(), Expr::Int(1)) {
4841 if let Expr::Var(src_name, _) = l.as_ref() {
4842 v.iter()
4843 .rfind(|x| x.name == *src_name)
4844 .filter(|x| x.var_type == DataType::Int && !x.cell)
4845 .map(|_| (false, src_name.as_str()))
4846 } else {
4847 None
4848 }
4849 } else {
4850 None
4851 }
4852 }
4853 _ => None,
4854 };
4855 if let Some((is_inc, src_name)) = inc_dec {
4856 let src_id = v.iter().rfind(|x| x.name == src_name).unwrap().register_id;
4857 output.push(if src_id == id {
4858 if is_inc {
4859 Instr::IncInt(id)
4860 } else {
4861 Instr::DecInt(id)
4862 }
4863 } else {
4864 if is_inc {
4865 Instr::IncIntTo(src_id, id)
4866 } else {
4867 Instr::DecIntTo(src_id, id)
4868 }
4869 });
4870 return;
4871 }
4872 }
4873
4874 let output_len = output.len();
4875 let obj_id = value
4876 .compile(v, ctx, state, output, Some(id), false, true)
4877 .unwrap_id();
4878 if output.len() != output_len {
4879 if !move_to_id(output, id) && obj_id != id {
4880 output.push(Instr::Mov(obj_id, id));
4881 }
4882 } else if state.const_registers.values().any(|&v| v == obj_id) {
4883 move_reg_to_reg(output, obj_id, id, state.registers[obj_id as usize]);
4884 } else if obj_id != id {
4885 output.push(Instr::Mov(obj_id, id));
4886 }
4887 if !v
4888 .iter()
4889 .any(|var| &var.name != name && var.register_id == obj_id)
4890 {
4891 state.free_reg(obj_id, v);
4892 }
4893 v[var_pos].var_type = var_type;
4894}
4895
4896#[allow(clippy::too_many_arguments)]
4897fn compile_struct_definition(
4898 name: &SmolStr,
4899 fields: &[(SmolStr, TypeExpr, Span)],
4900 span: Span,
4901 type_params: &TypeParams,
4902 defaults: &[Option<(Expr, Span)>],
4903 ctx: Ctx,
4904 state: &mut State<'_>,
4905 _output: &mut Vec<Instr>,
4906) {
4907 if !type_params.is_empty() {
4908 let template = state.generics.add_struct_template(
4909 name.clone(),
4910 type_params.clone(),
4911 ctx.file_idx,
4912 Box::from(fields),
4913 Box::from(defaults),
4914 );
4915 state
4916 .scope_mut(ctx.file_idx)
4917 .symbols
4918 .push((name.clone(), SymbolKind::Template(template)));
4919 return;
4920 }
4921 let struct_id = state.structs.len() as u16;
4922 state.structs.push(Struct {
4923 name: name.clone(),
4925 fields: Box::from([]),
4926 id: struct_id,
4927 name_span: span,
4928 });
4929 state
4930 .generics
4931 .set_struct_defaults(struct_id, ctx.file_idx, Box::from(defaults));
4932 let struct_symbol = SymbolKind::Struct((state.structs.len() - 1) as u16);
4933 state
4934 .scope_mut(ctx.file_idx)
4935 .symbols
4936 .push((name.clone(), struct_symbol));
4937 let parsed_fields = fields
4938 .iter()
4939 .map(|(f, f_t, f_span)| {
4940 (
4941 f.clone(),
4942 f_t.to_datatype(&mut state.type_ctx(ctx.file_idx)),
4943 *f_span,
4944 )
4945 })
4946 .collect();
4947 state.structs[struct_id as usize].fields = parsed_fields;
4948}
4949
4950fn compile_function_definition(
4951 fn_name: &SmolStr,
4952 fn_args: &[(SmolStr, Option<TypeExpr>)],
4953 fn_code: &Rc<[Expr]>,
4954 span: Span,
4955 declared_return_type: Option<&(TypeExpr, Span)>,
4956 type_params: &TypeParams,
4957 _v: &mut Vec<Variable>,
4958 ctx: Ctx,
4959 state: &mut State<'_>,
4960 _output: &mut Vec<Instr>,
4961) {
4962 if let Some(func) = state.fns.iter().find(|func| &func.name == fn_name) {
4963 compiler_errors::error_function_already_defined(func, span, ctx.file_idx, state.sources);
4964 }
4965 let mut callees = Vec::new();
4966 collect_direct_fn_calls(fn_code, self_type_of(fn_name), &mut callees);
4967 let fn_symbol = SymbolKind::Fn(state.fns.len() as u16);
4968 state
4969 .scope_mut(ctx.file_idx)
4970 .symbols
4971 .push((fn_name.clone(), fn_symbol));
4972 let args: Box<[(SmolStr, Option<DataType>)]> = fn_args
4975 .iter()
4976 .map(|(a, t)| {
4977 (
4978 a.clone(),
4979 t.clone()
4980 .filter(|t_e| !t_e.mentions_any(type_params))
4981 .map(|t_e| t_e.to_datatype(&mut state.type_ctx(ctx.file_idx))),
4982 )
4983 })
4984 .collect();
4985 let return_type = declared_return_type
4986 .filter(|(t_e, _)| !t_e.mentions_any(type_params))
4987 .map(|(t_e, t_span)| (t_e.to_datatype(&mut state.type_ctx(ctx.file_idx)), *t_span));
4988 let generics = (!type_params.is_empty()).then(|| {
4989 Box::new(FnGenerics {
4990 params: type_params.clone(),
4991 arg_types: fn_args.iter().map(|(_, t)| t.clone()).collect(),
4992 return_type: declared_return_type.map(|t| Box::new(t.clone())),
4993 bindings: Box::from([]),
4994 file_idx: ctx.file_idx,
4995 })
4996 });
4997 state.fns.push(Function {
4998 name: fn_name.clone(),
4999 args,
5000 code: fn_code.clone(),
5001 impls: Vec::new(),
5002 is_recursive: None,
5003 returns_null: check_if_returns_void(fn_code),
5004 src_file: ctx.file_idx,
5005 return_type_cache: Vec::new(),
5006 direct_calls: callees.into_boxed_slice(),
5007 name_span: span,
5008 return_type,
5009 generics,
5010 captures: Box::from([]),
5011 entry_register: None,
5012 });
5013}
5014
5015fn compile_return(
5016 return_value: Option<&Expr>,
5017 v: &mut Vec<Variable>,
5018 ctx: Ctx,
5019 state: &mut State<'_>,
5020 output: &mut Vec<Instr>,
5021) {
5022 if !ctx.in_function {
5026 if let Some(x) = return_value {
5027 let id = x
5028 .compile(v, ctx, state, output, None, false, true)
5029 .unwrap_id();
5030 state.free_reg(id, v);
5031 }
5032 output.push(Instr::Halt(0));
5033 return;
5034 }
5035 let Some(x) = return_value else {
5036 output.push(Instr::VoidReturn);
5040 return;
5041 };
5042 let mut id = x
5043 .compile(v, ctx, state, output, None, false, true)
5044 .unwrap_id();
5045 if let Some(check) = ctx.return_downcast
5050 && matches!(x.infer_type(v, ctx, state), DataType::Unknown)
5051 {
5052 state.free_reg(id, v);
5053 let checked_id = state.alloc_reg();
5054 match check {
5055 ReturnCheck::Builtin(downcast) => {
5056 output.push(Instr::CallLibFunc(downcast, id, checked_id));
5057 }
5058 ReturnCheck::Types(codes) => {
5059 let codes = state.type_codes_register(codes.as_slice());
5060 output.push(Instr::StoreFuncArg(codes));
5061 output.push(Instr::CallLibFunc(LibFunc::AsTypeVal, id, checked_id));
5062 }
5063 ReturnCheck::TypesIn(codes) => {
5064 output.push(Instr::StoreFuncArg(codes));
5065 output.push(Instr::CallLibFunc(LibFunc::AsTypeVal, id, checked_id));
5066 }
5067 }
5068 id = checked_id;
5069 }
5070 if ctx.is_compiling_recursive {
5071 output.push(Instr::RecursiveReturn(id));
5072 } else {
5073 output.push(Instr::Return(id));
5074 }
5075}
5076
5077#[inline]
5078fn compile_loop_break(ctx: Ctx, output: &mut Vec<Instr>) {
5079 output.push(Instr::NotEqJmp(ctx.block_id + 1, 0, 0));
5080}
5081
5082#[inline]
5083fn compile_loop_continue(ctx: Ctx, output: &mut Vec<Instr>) {
5084 output.push(Instr::EqJmp(ctx.block_id + 1, 0, 0));
5085}
5086
5087#[inline]
5088fn compile_eval_block(
5089 code: &[Expr],
5090 v: &mut Vec<Variable>,
5091 ctx: Ctx,
5092 state: &mut State<'_>,
5093 output: &mut Vec<Instr>,
5094) {
5095 output.extend(compile_expr(
5096 code,
5097 v,
5098 ctx.set_offset(output.len() as u16),
5099 state,
5100 ));
5101}
5102
5103fn self_type_of(fn_name: &str) -> Option<&str> {
5107 fn_name
5108 .split_once(METHOD_SEP)
5109 .map(|(type_name, _)| type_name)
5110}
5111
5112pub fn compile_expr(
5113 input: &[Expr],
5114 v: &mut Vec<Variable>,
5115 ctx: Ctx,
5116 state: &mut State<'_>,
5117) -> Vec<Instr> {
5118 let v_len = v.len();
5119 let symbols_len = state.scope(ctx.file_idx).symbols.len();
5120 let mut output: Vec<Instr> = Vec::with_capacity(input.len());
5121 for (idx, x) in input.iter().enumerate() {
5122 if let Some(id) = x.compile_with_code_context(
5126 v,
5127 ctx,
5128 state,
5129 &mut output,
5130 None,
5131 false,
5132 &input[idx + 1..],
5133 x.is_value_only(),
5134 ) {
5135 state.free_reg(id, v);
5136 }
5137 }
5138 v.truncate(v_len);
5139 state.scope_mut(ctx.file_idx).symbols.truncate(symbols_len);
5147 output
5148}
5149
5150impl Expr {
5151 #[must_use]
5152 pub const fn is_constant_literal(&self) -> bool {
5153 matches!(
5154 self,
5155 Self::Int(_) | Self::Float(_) | Self::String(_) | Self::Bool(_) | Self::Null
5156 )
5157 }
5158 #[inline(always)]
5159 pub fn compile(
5160 &self,
5161 v: &mut Vec<Variable>,
5162 ctx: Ctx,
5163 state: &mut State<'_>,
5164 output: &mut Vec<Instr>,
5165 tgt_id: Option<u16>,
5166 var_assignment: bool,
5167 uses_id: bool,
5168 ) -> Option<u16> {
5169 self.compile_with_code_context(v, ctx, state, output, tgt_id, var_assignment, &[], uses_id)
5170 }
5171 pub fn compile_with_code_context(
5172 &self,
5173 v: &mut Vec<Variable>,
5174 ctx: Ctx,
5175 state: &mut State<'_>,
5176 output: &mut Vec<Instr>,
5177 tgt_id: Option<u16>,
5178 var_assignment: bool,
5179 remaining_code: &[Self],
5180 uses_id: bool,
5181 ) -> Option<u16> {
5182 match self {
5183 Self::Int(num) => {
5184 debug_assert!(uses_id);
5185 if var_assignment {
5186 state.registers.push((*num).into());
5187 return Some((state.registers.len() - 1) as u16);
5188 }
5189 let data = (*num).into();
5190 if let Some(&id) = state.const_registers.get(&data) {
5191 Some(id)
5192 } else {
5193 let id = state.registers.len() as u16;
5194 state.const_registers.insert(data, id);
5195 state.registers.push(data);
5196 Some(id)
5197 }
5198 }
5199 Self::Float(num) => {
5200 debug_assert!(uses_id);
5201 if var_assignment {
5202 state.registers.push((*num).into());
5203 return Some((state.registers.len() - 1) as u16);
5204 }
5205 let data = (*num).into();
5206 if let Some(&id) = state.const_registers.get(&data) {
5207 Some(id)
5208 } else {
5209 state.registers.push(data);
5210 let id = (state.registers.len() - 1) as u16;
5211 state.const_registers.insert(data, id);
5212 Some(id)
5213 }
5214 }
5215 Self::String(str) => {
5216 debug_assert!(uses_id);
5217 if var_assignment {
5218 state
5219 .registers
5220 .push(Data::p_str(str, &mut state.pools.strings));
5221 return Some((state.registers.len() - 1) as u16);
5222 }
5223 let data = Data::p_str(str, &mut state.pools.strings);
5224 if let Some(&id) = state.const_registers.get(&data) {
5225 Some(id)
5226 } else {
5227 let id = state.registers.len() as u16;
5228 state.const_registers.insert(data, id);
5229 state.registers.push(data);
5230 Some(id)
5231 }
5232 }
5233 Self::Null => {
5234 debug_assert!(uses_id);
5235 if var_assignment {
5236 state.registers.push(NULL);
5237 return Some((state.registers.len() - 1) as u16);
5238 }
5239 if let Some(&id) = state.const_registers.get(&NULL) {
5240 Some(id)
5241 } else {
5242 let id = state.registers.len() as u16;
5243 state.const_registers.insert(NULL, id);
5244 state.registers.push(NULL);
5245 Some(id)
5246 }
5247 }
5248 Self::Bool(bool) => {
5249 debug_assert!(uses_id);
5250 if var_assignment {
5251 state.registers.push((*bool).into());
5252 return Some((state.registers.len() - 1) as u16);
5253 }
5254 let data: Data = (*bool).into();
5255 if let Some(&id) = state.const_registers.get(&data) {
5256 Some(id)
5257 } else {
5258 let id = state.registers.len() as u16;
5259 state.const_registers.insert(data, id);
5260 state.registers.push(data);
5261 Some(id)
5262 }
5263 }
5264 Self::Var(name, span) => {
5265 debug_assert!(uses_id);
5266 if let Some(var) = v.iter().rfind(|v_temp| *name == v_temp.name) {
5267 if var.cell {
5268 let cell = var.register_id;
5271 let dest = state.alloc_reg_tgt(tgt_id);
5272 output.push(Instr::LoadCell(cell, dest));
5273 Some(dest)
5274 } else if let DataType::Fn(fn_id) = var.var_type
5275 && state.fns[fn_id as usize].captures.is_empty()
5276 {
5277 Some(compile_fn_entry_value(
5281 fn_id as usize,
5282 v,
5283 ctx,
5284 state,
5285 output,
5286 tgt_id,
5287 ))
5288 } else {
5289 Some(var.register_id)
5290 }
5291 } else if let Some(fn_id) = state.scope(ctx.file_idx).find_function(&[], name) {
5292 Some(compile_fn_entry_value(fn_id, v, ctx, state, output, tgt_id))
5297 } else if let Some((enum_id, variant_idx)) =
5298 variant_constructor(std::slice::from_ref(name), &[], *span, v, ctx, state)
5299 {
5300 Some(compile_enum_construction(
5301 enum_id,
5302 variant_idx,
5303 &[],
5304 *span,
5305 &[],
5306 v,
5307 ctx,
5308 state,
5309 output,
5310 ))
5311 } else {
5312 compiler_errors::error_unknown_variable(
5313 name,
5314 *span,
5315 v,
5316 ctx.file_idx,
5317 state.sources,
5318 );
5319 }
5320 }
5321 Self::Array(array_items, spans) => {
5322 debug_assert!(uses_id);
5323 Some(compile_array_literal(
5324 array_items,
5325 spans,
5326 v,
5327 ctx,
5328 state,
5329 output,
5330 ))
5331 }
5332 Self::Struct(namespace, fields, span, type_args, base) => {
5333 debug_assert!(uses_id);
5334 Some(compile_struct_literal(
5335 namespace,
5336 fields,
5337 type_args,
5338 base.as_deref(),
5339 *span,
5340 v,
5341 ctx,
5342 state,
5343 output,
5344 ))
5345 }
5346 Self::StructDefault(struct_id, span) => {
5347 debug_assert!(uses_id);
5348 Some(compile_struct_default(
5349 *struct_id, *span, v, ctx, state, output,
5350 ))
5351 }
5352 Self::Map(kv_pairs, span) => {
5353 debug_assert!(uses_id);
5354 Some(compile_map_literal(kv_pairs, *span, v, ctx, state, output))
5355 }
5356 Self::GetStructField(struct_expr, field, struct_span, field_span) => {
5357 debug_assert!(uses_id);
5358 Some(compile_struct_field_access(
5359 struct_expr,
5360 field,
5361 *struct_span,
5362 *field_span,
5363 v,
5364 ctx,
5365 state,
5366 output,
5367 ))
5368 }
5369 Self::Propagate(operand, span) => {
5370 debug_assert!(uses_id);
5371 Some(compile_propagate(
5372 operand, *span, tgt_id, v, ctx, state, output,
5373 ))
5374 }
5375 Self::Is(operand, target, operand_span, target_span) => {
5376 debug_assert!(uses_id);
5377 Some(
5378 type_test::compile_is(
5379 operand,
5380 target,
5381 *operand_span,
5382 *target_span,
5383 tgt_id,
5384 false,
5385 v,
5386 ctx,
5387 state,
5388 output,
5389 )
5390 .0,
5391 )
5392 }
5393 Self::Cast(operand, target, operand_span, target_span) => {
5394 debug_assert!(uses_id);
5395 Some(type_test::compile_cast(
5396 operand,
5397 target,
5398 *operand_span,
5399 *target_span,
5400 tgt_id,
5401 v,
5402 ctx,
5403 state,
5404 output,
5405 ))
5406 }
5407 Self::ArrayGetIndex(array, index, span) => {
5409 debug_assert!(uses_id);
5410 Some(compile_array_indexing(
5411 array, index, *span, v, ctx, state, output,
5412 ))
5413 }
5414 Self::ArrayGetSlice(array, idx_start, idx_end, span) => {
5416 debug_assert!(uses_id);
5417 Some(compile_array_slice(
5418 array,
5419 idx_start,
5420 idx_end.as_deref(),
5421 *span,
5422 v,
5423 ctx,
5424 state,
5425 output,
5426 ))
5427 }
5428 Self::Mul(l, r, span1, span2) => {
5429 debug_assert!(uses_id);
5430 Some(uniform_op2(
5431 Instr::MulFloat,
5432 &DataType::Float,
5433 Instr::MulInt,
5434 &DataType::Int,
5435 "*",
5436 l,
5437 r,
5438 *span1,
5439 *span2,
5440 tgt_id,
5441 v,
5442 ctx,
5443 state,
5444 output,
5445 ))
5446 }
5447 Self::Div(l, r, span1, span2) => {
5448 debug_assert!(uses_id);
5449 Some(compile_div_op(
5450 l, r, *span1, *span2, tgt_id, v, ctx, state, output,
5451 ))
5452 }
5453 Self::Add(l, r, span1, span2) => {
5454 debug_assert!(uses_id);
5455 Some(compile_add_op(
5456 l, r, *span1, *span2, tgt_id, v, ctx, state, output,
5457 ))
5458 }
5459 Self::Sub(l, r, span1, span2) => {
5460 debug_assert!(uses_id);
5461 Some(compile_sub_op(
5462 l, r, *span1, *span2, tgt_id, v, ctx, state, output,
5463 ))
5464 }
5465 Self::Mod(l, r, span1, span2) => {
5466 debug_assert!(uses_id);
5467 Some(compile_mod_op(
5468 l, r, *span1, *span2, tgt_id, v, ctx, state, output,
5469 ))
5470 }
5471 Self::Pow(l, r, span1, span2) => {
5472 debug_assert!(uses_id);
5473 Some(uniform_op2(
5474 Instr::PowFloat,
5475 &DataType::Float,
5476 Instr::PowInt,
5477 &DataType::Int,
5478 "^",
5479 l,
5480 r,
5481 *span1,
5482 *span2,
5483 tgt_id,
5484 v,
5485 ctx,
5486 state,
5487 output,
5488 ))
5489 }
5490 Self::BitAnd(l, r, span1, span2) => {
5491 debug_assert!(uses_id);
5492 Some(int_op2(
5493 Instr::BitAndInt,
5494 "&",
5495 l,
5496 r,
5497 *span1,
5498 *span2,
5499 tgt_id,
5500 v,
5501 ctx,
5502 state,
5503 output,
5504 ))
5505 }
5506 Self::BitOr(l, r, span1, span2) => {
5507 debug_assert!(uses_id);
5508 Some(int_op2(
5509 Instr::BitOrInt,
5510 "|",
5511 l,
5512 r,
5513 *span1,
5514 *span2,
5515 tgt_id,
5516 v,
5517 ctx,
5518 state,
5519 output,
5520 ))
5521 }
5522 Self::BitXor(l, r, span1, span2) => {
5523 debug_assert!(uses_id);
5524 Some(int_op2(
5525 Instr::BitXorInt,
5526 "^^",
5527 l,
5528 r,
5529 *span1,
5530 *span2,
5531 tgt_id,
5532 v,
5533 ctx,
5534 state,
5535 output,
5536 ))
5537 }
5538 Self::Shl(l, r, span1, span2) => {
5539 debug_assert!(uses_id);
5540 Some(compile_shift_op(
5541 Instr::ShlInt,
5542 "<<",
5543 l,
5544 r,
5545 *span1,
5546 *span2,
5547 tgt_id,
5548 v,
5549 ctx,
5550 state,
5551 output,
5552 ))
5553 }
5554 Self::Shr(l, r, span1, span2) => {
5555 debug_assert!(uses_id);
5556 Some(compile_shift_op(
5557 Instr::ShrInt,
5558 ">>",
5559 l,
5560 r,
5561 *span1,
5562 *span2,
5563 tgt_id,
5564 v,
5565 ctx,
5566 state,
5567 output,
5568 ))
5569 }
5570 Self::BitNot(l, span1, span2) => {
5571 debug_assert!(uses_id);
5572 Some(compile_bit_not_op(
5573 l, *span1, *span2, tgt_id, v, ctx, state, output,
5574 ))
5575 }
5576 Self::Eq(l, r, span1, span2) => {
5577 debug_assert!(uses_id);
5578 Some(compile_eq_op(
5579 l, r, *span1, *span2, tgt_id, v, ctx, state, output,
5580 ))
5581 }
5582 Self::NotEq(l, r, span1, span2) => {
5583 debug_assert!(uses_id);
5584 Some(compile_neq_op(
5585 l, r, *span1, *span2, tgt_id, v, ctx, state, output,
5586 ))
5587 }
5588 Self::Sup(l, r, span1, span2) => {
5589 debug_assert!(uses_id);
5590 Some(compile_cmp_op(
5591 Instr::SupFloat,
5592 Instr::SupInt,
5593 Instr::SupStr,
5594 ">",
5595 l,
5596 r,
5597 *span1,
5598 *span2,
5599 tgt_id,
5600 v,
5601 ctx,
5602 state,
5603 output,
5604 ))
5605 }
5606 Self::SupEq(l, r, span1, span2) => {
5607 debug_assert!(uses_id);
5608 Some(compile_cmp_op(
5609 Instr::SupEqFloat,
5610 Instr::SupEqInt,
5611 Instr::SupEqStr,
5612 ">=",
5613 l,
5614 r,
5615 *span1,
5616 *span2,
5617 tgt_id,
5618 v,
5619 ctx,
5620 state,
5621 output,
5622 ))
5623 }
5624 Self::Inf(l, r, span1, span2) => {
5625 debug_assert!(uses_id);
5626 Some(compile_cmp_op(
5627 Instr::InfFloat,
5628 Instr::InfInt,
5629 Instr::InfStr,
5630 "<",
5631 l,
5632 r,
5633 *span1,
5634 *span2,
5635 tgt_id,
5636 v,
5637 ctx,
5638 state,
5639 output,
5640 ))
5641 }
5642 Self::InfEq(l, r, span1, span2) => {
5643 debug_assert!(uses_id);
5644 Some(compile_cmp_op(
5645 Instr::InfEqFloat,
5646 Instr::InfEqInt,
5647 Instr::InfEqStr,
5648 "<=",
5649 l,
5650 r,
5651 *span1,
5652 *span2,
5653 tgt_id,
5654 v,
5655 ctx,
5656 state,
5657 output,
5658 ))
5659 }
5660 Self::BoolAnd(l, r, span1, span2) => {
5661 debug_assert!(uses_id);
5662 Some(compile_short_circuit_value(
5663 l, r, *span1, *span2, "&&", tgt_id, v, ctx, state, output, false,
5664 ))
5665 }
5666 Self::BoolOr(l, r, span1, span2) => {
5667 debug_assert!(uses_id);
5668 Some(compile_short_circuit_value(
5669 l, r, *span1, *span2, "||", tgt_id, v, ctx, state, output, false,
5670 ))
5671 }
5672 Self::Neg(l, span1, span2) => {
5673 debug_assert!(uses_id);
5674 Some(compile_neg_op(
5675 l, *span1, *span2, tgt_id, v, ctx, state, output,
5676 ))
5677 }
5678 Self::BoolNeg(l, span1, span2) => {
5679 debug_assert!(uses_id);
5680 Some(compile_bool_neg_op(
5681 l, *span1, *span2, tgt_id, v, ctx, state, output,
5682 ))
5683 }
5684 Self::InlineCondition(main_condition, code, span, condition_span) => {
5685 debug_assert!(uses_id);
5686 let as_fn_value = matches!(self.infer_type(v, ctx, state), DataType::FnValue(_));
5687 Some(compile_inline_condition(
5688 main_condition,
5689 code,
5690 *span,
5691 *condition_span,
5692 v,
5693 ctx,
5694 state,
5695 output,
5696 tgt_id,
5697 as_fn_value,
5698 ))
5699 }
5700 Self::FunctionCall(args, namespace, markers, args_indexes, type_args) if uses_id => {
5701 Some(
5702 handle_functions(
5703 output,
5704 v,
5705 ctx,
5706 state,
5707 tgt_id,
5708 args,
5709 namespace,
5710 *markers,
5711 args_indexes,
5712 type_args,
5713 )
5714 .unwrap_or_else(|| {
5715 if let Some(&id) = state.const_registers.get(&NULL) {
5716 id
5717 } else {
5718 let id = state.registers.len() as u16;
5719 state.const_registers.insert(NULL, id);
5720 state.registers.push(NULL);
5721 id
5722 }
5723 }),
5724 )
5725 }
5726 Self::CallValue(callee, args, span, args_indexes) if uses_id => Some(
5727 handle_value_call(
5728 output,
5729 v,
5730 ctx,
5731 state,
5732 tgt_id,
5733 callee,
5734 args,
5735 *span,
5736 args_indexes,
5737 )
5738 .unwrap_or_else(|| {
5739 if let Some(&id) = state.const_registers.get(&NULL) {
5740 id
5741 } else {
5742 let id = state.registers.len() as u16;
5743 state.const_registers.insert(NULL, id);
5744 state.registers.push(NULL);
5745 id
5746 }
5747 }),
5748 ),
5749 Self::AnonymousFunction(_, _, span) => {
5750 debug_assert!(uses_id);
5751 let DataType::Fn(fn_id) = self.infer_type(v, ctx, state) else {
5752 unsafe { unreachable_unchecked() }
5753 };
5754 let fn_id = fn_id as usize;
5755 let captures = state.fns[fn_id].captures.clone();
5756 if captures.is_empty() {
5757 Some(compile_fn_entry_value(fn_id, v, ctx, state, output, tgt_id))
5763 } else {
5764 let entry_id = state.fn_entry_register(fn_id);
5770 let env_id = state.alloc_reg_tgt(tgt_id);
5771 output.push(Instr::EmptyFnValue(env_id));
5772 output.push(Instr::Push(env_id, entry_id));
5773 for (name, _, _) in &captures {
5774 let cell_id = capture_cell(name, *span, v, ctx, state, output);
5775 output.push(Instr::Push(env_id, cell_id));
5776 }
5777 Some(env_id)
5778 }
5779 }
5780
5781 Self::ArrayModify(array, index, value, index_markers, elem_markers) => {
5787 debug_assert!(!uses_id);
5788 compile_array_index_assignment(
5789 array,
5790 index,
5791 value,
5792 *index_markers,
5793 *elem_markers,
5794 v,
5795 ctx,
5796 state,
5797 output,
5798 );
5799 None
5800 }
5801 Self::SetStructField(
5802 struct_expr,
5803 field,
5804 new_val,
5805 struct_span,
5806 field_span,
5807 value_span,
5808 ) => {
5809 debug_assert!(!uses_id);
5810 compile_struct_field_assignment(
5811 struct_expr,
5812 field,
5813 new_val,
5814 *struct_span,
5815 *field_span,
5816 *value_span,
5817 v,
5818 ctx,
5819 state,
5820 output,
5821 );
5822 None
5823 }
5824 Self::Condition(main_condition, code, _, condition_span) => {
5825 debug_assert!(!uses_id);
5826 compile_condition(main_condition, code, *condition_span, v, ctx, state, output);
5827 None
5828 }
5829 Self::WhileBlock(condition, code, condition_span) => {
5830 debug_assert!(!uses_id);
5831 compile_while_loop(condition, code, *condition_span, v, ctx, state, output);
5832 None
5833 }
5834 Self::ForLoop(var_name, array, code, span) => {
5835 debug_assert!(!uses_id);
5836 compile_for_loop(var_name, array, code, *span, v, ctx, state, output);
5837 None
5838 }
5839 Self::IntForLoop(var_name, start_elem, end_elem, code, span1, span2) => {
5840 debug_assert!(!uses_id);
5841 compile_int_for_loop(
5842 var_name, start_elem, end_elem, code, *span1, *span2, v, ctx, state, output,
5843 );
5844 None
5845 }
5846 Self::LoopBlock(code) => {
5847 debug_assert!(!uses_id);
5848 compile_loop_block(code, v, ctx, state, output);
5849 None
5850 }
5851 Self::TryCatchBlock(e, err_var, catch_code) => {
5852 debug_assert!(!uses_id);
5853 compile_try_catch_block(e, err_var, catch_code, v, ctx, state, output);
5854 None
5855 }
5856 Self::VarDeclare(name, value, annotation) => {
5857 debug_assert!(!uses_id);
5858 compile_var_declaration(
5859 name,
5860 value,
5861 annotation.as_deref(),
5862 remaining_code,
5863 v,
5864 ctx,
5865 state,
5866 output,
5867 );
5868 None
5869 }
5870 Self::VarAssign(name, value, span, value_span) => {
5871 debug_assert!(!uses_id);
5872 compile_var_assignment(name, value, *span, *value_span, v, ctx, state, output);
5873 None
5874 }
5875 Self::StructDeclare(name, fields, span, type_params, defaults) => {
5876 debug_assert!(!uses_id);
5877 compile_struct_definition(
5878 name,
5879 fields,
5880 *span,
5881 type_params,
5882 defaults,
5883 ctx,
5884 state,
5885 output,
5886 );
5887 None
5888 }
5889 Self::EnumDeclare(name, variants, span, type_params) => {
5890 debug_assert!(!uses_id);
5891 compile_enum_definition(name, variants, *span, type_params, ctx, state);
5892 None
5893 }
5894 Self::Match(scrutinee, arms, wildcard, span, true) => {
5895 debug_assert!(uses_id);
5896 let as_fn_value = matches!(self.infer_type(v, ctx, state), DataType::FnValue(_));
5897 Some(compile_match_value(
5898 scrutinee,
5899 arms,
5900 wildcard.as_deref(),
5901 *span,
5902 v,
5903 ctx,
5904 state,
5905 output,
5906 tgt_id,
5907 as_fn_value,
5908 ))
5909 }
5910 Self::Match(scrutinee, arms, wildcard, span, false) => {
5911 debug_assert!(!uses_id);
5912 compile_match(
5913 scrutinee,
5914 arms,
5915 wildcard.as_deref(),
5916 *span,
5917 v,
5918 ctx,
5919 state,
5920 output,
5921 );
5922 None
5923 }
5924 Self::NamespacedRef(path, span, type_args) => {
5925 debug_assert!(uses_id);
5926 if !type_args.is_empty() {
5927 let (enum_id, variant_idx) =
5928 resolve_generic_variant(path, type_args, *span, ctx, state);
5929 return Some(compile_enum_construction(
5930 enum_id,
5931 variant_idx,
5932 &[],
5933 *span,
5934 &[],
5935 v,
5936 ctx,
5937 state,
5938 output,
5939 ));
5940 }
5941 if let Some((enum_id, variant_idx)) =
5942 variant_constructor(path, &[], *span, v, ctx, state)
5943 {
5944 Some(compile_enum_construction(
5945 enum_id,
5946 variant_idx,
5947 &[],
5948 *span,
5949 &[],
5950 v,
5951 ctx,
5952 state,
5953 output,
5954 ))
5955 } else {
5956 compiler_errors::error_enum(
5957 "Unknown enum variant",
5958 &format!("{} does not name an enum variant", path.join("::")),
5959 *span,
5960 ctx.file_idx,
5961 state.sources,
5962 );
5963 }
5964 }
5965 Self::CallValue(callee, args, span, args_indexes) if !uses_id => {
5966 let output_id = handle_value_call(
5967 output,
5968 v,
5969 ctx,
5970 state,
5971 tgt_id,
5972 callee,
5973 args,
5974 *span,
5975 args_indexes,
5976 );
5977 if let Some(id) = output_id {
5978 state.free_reg(id, v);
5979 }
5980 None
5981 }
5982 Self::FunctionCall(args, namespace, markers, args_indexes, type_args) if !uses_id => {
5983 let output_id = handle_functions(
5984 output,
5985 v,
5986 ctx,
5987 state,
5988 tgt_id,
5989 args,
5990 namespace,
5991 *markers,
5992 args_indexes,
5993 type_args,
5994 );
5995 if let Some(id) = output_id {
5996 state.free_reg(id, v);
5997 }
5998 None
5999 }
6000 Self::ObjFunctionCall(
6001 obj,
6002 args,
6003 namespace,
6004 obj_span,
6005 fn_span,
6006 args_indexes,
6007 type_args,
6008 ) if !uses_id => {
6009 let output_id = handle_method_calls(
6010 output,
6011 v,
6012 ctx,
6013 state,
6014 tgt_id,
6015 obj,
6016 args,
6017 namespace,
6018 *obj_span,
6019 *fn_span,
6020 args_indexes,
6021 type_args,
6022 );
6023 if let Some(id) = output_id {
6024 state.free_reg(id, v);
6025 }
6026 None
6027 }
6028 Self::ObjFunctionCall(
6029 obj,
6030 args,
6031 namespace,
6032 obj_span,
6033 fn_span,
6034 args_indexes,
6035 type_args,
6036 ) if uses_id => Some(
6037 handle_method_calls(
6038 output,
6039 v,
6040 ctx,
6041 state,
6042 tgt_id,
6043 obj,
6044 args,
6045 namespace,
6046 *obj_span,
6047 *fn_span,
6048 args_indexes,
6049 type_args,
6050 )
6051 .unwrap_or_else(|| {
6052 if let Some(&id) = state.const_registers.get(&NULL) {
6053 id
6054 } else {
6055 let id = state.registers.len() as u16;
6056 state.const_registers.insert(NULL, id);
6057 state.registers.push(NULL);
6058 id
6059 }
6060 }),
6061 ),
6062 Self::FunctionDecl(fn_name, fn_args, fn_code, span, return_type, type_params) => {
6063 debug_assert!(!uses_id);
6064 compile_function_definition(
6065 fn_name,
6066 fn_args,
6067 fn_code,
6068 *span,
6069 return_type.as_deref(),
6070 type_params,
6071 v,
6072 ctx,
6073 state,
6074 output,
6075 );
6076 None
6077 }
6078 Self::ReturnVal(return_value) => {
6079 debug_assert!(!uses_id);
6080 compile_return(return_value.as_ref().as_ref(), v, ctx, state, output);
6081 None
6082 }
6083 Self::Break => {
6084 debug_assert!(!uses_id);
6085 compile_loop_break(ctx, output);
6086 None
6087 }
6088 Self::Continue => {
6089 debug_assert!(!uses_id);
6090 compile_loop_continue(ctx, output);
6091 None
6092 }
6093 Self::EvalBlock(code) => {
6094 debug_assert!(!uses_id);
6095 compile_eval_block(code, v, ctx, state, output);
6096 None
6097 }
6098 _ => unsafe { unreachable_unchecked() },
6099 }
6100 }
6101}
6102
6103#[cfg(target_arch = "aarch64")]
6104const ARCH_SUFFIX: &str = "-aarch64";
6105#[cfg(target_arch = "x86_64")]
6106const ARCH_SUFFIX: &str = "-x86_64";
6107#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
6108const ARCH_SUFFIX: &str = "";
6109
6110#[derive(Debug, Copy, Clone)]
6111pub enum SymbolKind {
6112 Fn(u16),
6113 Struct(u16),
6114 Enum(u16),
6115 Template(u32),
6121}
6122
6123const fn symbol_ids_equal(a: SymbolKind, b: SymbolKind) -> bool {
6126 match (a, b) {
6127 (SymbolKind::Fn(x), SymbolKind::Fn(y))
6128 | (SymbolKind::Struct(x), SymbolKind::Struct(y))
6129 | (SymbolKind::Enum(x), SymbolKind::Enum(y)) => x == y,
6130 (SymbolKind::Template(x), SymbolKind::Template(y)) => x == y,
6131 _ => false,
6132 }
6133}
6134
6135#[derive(Debug, Clone, Default)]
6136pub struct Namespace {
6137 pub symbols: Vec<(SmolStr, SymbolKind)>,
6138 pub children: Vec<(SmolStr, Self)>,
6139 pub file: SmolStr,
6143 pub import_path: SmolStr,
6146}
6147
6148#[derive(Debug, Clone, Default)]
6160pub struct FileNamespaces {
6161 files: FxHashMap<u16, Namespace>,
6162 empty: Namespace,
6165 std_files: Vec<u16>,
6168}
6169
6170impl FileNamespaces {
6171 pub fn mark_std(&mut self, file_idx: u16) {
6173 self.std_files.push(file_idx);
6174 }
6175 #[must_use]
6177 pub fn is_std(&self, file_idx: u16) -> bool {
6178 self.std_files.contains(&file_idx)
6179 }
6180 pub fn insert(&mut self, file_idx: u16, namespace: Namespace) {
6181 self.files.insert(file_idx, namespace);
6182 }
6183 #[must_use]
6184 pub fn get(&self, file_idx: u16) -> &Namespace {
6185 self.files.get(&file_idx).unwrap_or(&self.empty)
6186 }
6187 pub fn get_mut(&mut self, file_idx: u16) -> &mut Namespace {
6188 self.files.entry(file_idx).or_default()
6189 }
6190 #[must_use]
6192 pub fn root(&self) -> &Namespace {
6193 self.get(0)
6194 }
6195 #[must_use]
6211 pub fn checkpoint(&self) -> FileNamespacesCheckpoint {
6212 FileNamespacesCheckpoint {
6213 symbols: self
6214 .files
6215 .iter()
6216 .map(|(file_idx, namespace)| (*file_idx, namespace.symbols.len()))
6217 .collect(),
6218 }
6219 }
6220 pub fn rollback_to(&mut self, checkpoint: &FileNamespacesCheckpoint) {
6226 for (file_idx, namespace) in &mut self.files {
6227 let len = checkpoint.symbols.get(file_idx).copied().unwrap_or(0);
6228 namespace.symbols.truncate(len);
6229 }
6230 }
6231}
6232
6233#[derive(Debug, Clone, Default)]
6236pub struct FileNamespacesCheckpoint {
6237 symbols: FxHashMap<u16, usize>,
6238}
6239
6240impl Namespace {
6241 pub fn fns(&self) -> impl Iterator<Item = &(SmolStr, SymbolKind)> {
6242 self.symbols
6243 .iter()
6244 .filter(|(_, kind)| matches!(kind, SymbolKind::Fn(_)))
6245 }
6246 pub fn structs(&self) -> impl Iterator<Item = &(SmolStr, SymbolKind)> {
6247 self.symbols
6248 .iter()
6249 .filter(|(_, kind)| matches!(kind, SymbolKind::Struct(_)))
6250 }
6251 #[must_use]
6259 pub fn find_function(&self, path: &[SmolStr], function_name: &str) -> Option<usize> {
6260 self.resolve(path)?.symbols.iter().find_map(|(name, kind)| {
6261 if name.as_str() == function_name
6262 && let SymbolKind::Fn(fn_id) = kind
6263 {
6264 Some(*fn_id as usize)
6265 } else {
6266 None
6267 }
6268 })
6269 }
6270 #[must_use]
6271 pub fn find_struct(
6272 &self,
6273 path: &[SmolStr],
6274 struct_name: &str,
6275 span: Span,
6276 file_idx: u16,
6277 sources: &[Source],
6278 ) -> Option<usize> {
6279 self.walk_to_namespace(path, span, file_idx, sources)
6280 .symbols
6281 .iter()
6282 .find_map(|(name, kind)| {
6283 if name.as_str() == struct_name
6284 && let SymbolKind::Struct(struct_id) = kind
6285 {
6286 Some(*struct_id as usize)
6287 } else {
6288 None
6289 }
6290 })
6291 }
6292 #[must_use]
6297 pub fn find_enum(&self, path: &[SmolStr], enum_name: &str) -> Option<usize> {
6298 self.resolve(path)?.symbols.iter().find_map(|(name, kind)| {
6299 if name.as_str() == enum_name
6300 && let SymbolKind::Enum(enum_id) = kind
6301 {
6302 Some(*enum_id as usize)
6303 } else {
6304 None
6305 }
6306 })
6307 }
6308 #[must_use]
6314 pub fn find_template(&self, path: &[SmolStr], type_name: &str) -> Option<usize> {
6315 self.resolve(path)?.symbols.iter().find_map(|(name, kind)| {
6316 if name.as_str() == type_name
6317 && let SymbolKind::Template(idx) = kind
6318 {
6319 Some(*idx as usize)
6320 } else {
6321 None
6322 }
6323 })
6324 }
6325 #[must_use]
6329 pub fn resolve(&self, path: &[SmolStr]) -> Option<&Self> {
6330 let mut current = self;
6331 for sub in path {
6332 current = ¤t.children.iter().find(|(name, _)| name == sub)?.1;
6333 }
6334 Some(current)
6335 }
6336 #[must_use]
6339 pub fn walk_to_namespace(
6340 &self,
6341 path: &[SmolStr],
6342 span: Span,
6343 file_idx: u16,
6344 sources: &[Source],
6345 ) -> &Self {
6346 match self.resolve(path) {
6347 Some(namespace) => namespace,
6348 None => error_unknown_namespace(path, span, self, file_idx, sources),
6349 }
6350 }
6351}
6352
6353fn loaded_file_key(path: PathBuf) -> PathBuf {
6364 path.canonicalize().unwrap_or(path)
6365}
6366
6367pub const PRELUDE_MODULES: [&str; 5] = [
6376 "std/option.cdl",
6377 "std/result.cdl",
6378 "std/list.cdl",
6379 "std/string.cdl",
6380 "std/map.cdl",
6381];
6382
6383fn embedded_std_module(path: &str) -> Option<&'static str> {
6391 match path {
6392 "std/option.cdl" => Some(include_str!("../../libs/std/option.cdl")),
6393 "std/result.cdl" => Some(include_str!("../../libs/std/result.cdl")),
6394 "std/list.cdl" => Some(include_str!("../../libs/std/list.cdl")),
6395 "std/string.cdl" => Some(include_str!("../../libs/std/string.cdl")),
6396 "std/map.cdl" => Some(include_str!("../../libs/std/map.cdl")),
6397 #[cfg(target_arch = "wasm32")]
6398 "std/math.cdl" => Some(include_str!("../../libs/std/math.cdl")),
6399 #[cfg(target_arch = "wasm32")]
6400 "std/time.cdl" => Some(include_str!("../../libs/std/time.cdl")),
6401 #[cfg(target_arch = "wasm32")]
6402 "std/random.cdl" => Some(include_str!("../../libs/std/random.cdl")),
6403 #[cfg(target_arch = "wasm32")]
6404 "std/hash.cdl" => Some(include_str!("../../libs/std/hash.cdl")),
6405 _ => None,
6406 }
6407}
6408
6409fn library_module(
6418 path: &str,
6419 resolver: &ImportResolver,
6420) -> Option<(PathBuf, Option<&'static str>)> {
6421 if let Some(text) = embedded_std_module(path) {
6422 return Some((PathBuf::from(path), Some(text)));
6423 }
6424 #[cfg(not(target_arch = "wasm32"))]
6425 return resolver
6426 .library_path(path)
6427 .map(|on_disk| (loaded_file_key(on_disk), None));
6428 #[cfg(target_arch = "wasm32")]
6429 {
6430 let _ = resolver;
6431 None
6432 }
6433}
6434
6435fn carried_module_filename(path: &str, resolver: &ImportResolver) -> SmolStr {
6440 #[cfg(not(target_arch = "wasm32"))]
6441 if let Some(on_disk) = resolver.library_path(path).filter(|p| p.is_file())
6442 && let Some(name) = loaded_file_key(on_disk).to_str()
6443 {
6444 return SmolStr::from(name);
6445 }
6446 #[cfg(target_arch = "wasm32")]
6447 let _ = resolver;
6448 SmolStr::from(path)
6449}
6450
6451fn tried_paths(chosen: &Path, library: Option<&Path>) -> Vec<PathBuf> {
6458 let mut tried = vec![chosen.to_path_buf()];
6459 if let Some(library) = library
6460 && library != chosen
6461 {
6462 tried.push(library.to_path_buf());
6463 }
6464 tried
6465}
6466
6467fn load_prelude(
6474 fns: &mut Vec<Function>,
6475 structs: &mut Vec<Struct>,
6476 enums: &mut Vec<EnumType>,
6477 dynamic_libs: &mut Vec<Dynamiclib>,
6478 sources: &mut Vec<Source>,
6479 files: &mut FxHashMap<PathBuf, Namespace>,
6480 file_namespaces: &mut FileNamespaces,
6481 pending_structs: &mut Vec<(u16, u16, Box<[(SmolStr, TypeExpr, Span)]>)>,
6482 pending_enums: &mut PendingEnums,
6483 pending_fns: &mut PendingFns,
6484 pending_dylibs: &mut Vec<PendingDylib>,
6485 pending_host: &mut Vec<(
6486 u16,
6487 u16,
6488 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
6489 SmolStr,
6490 Span,
6491 )>,
6492 generics: &mut Generics,
6493 resolver: &ImportResolver,
6494) {
6495 for rel in PRELUDE_MODULES {
6496 let (Some((path, _)), Some(text)) =
6497 (library_module(rel, resolver), embedded_std_module(rel))
6498 else {
6499 continue;
6500 };
6501 if files.contains_key(&path) {
6502 continue;
6503 }
6504 let child_src_idx = sources.len() as u16;
6505 sources.push(Source {
6506 filename: carried_module_filename(rel, resolver),
6507 contents: String::from(text),
6508 });
6509 let parsed = parser::parse(&sources.last().unwrap().contents, sources.last().unwrap());
6510 generics.add_impls(parsed.impls, child_src_idx);
6511
6512 let mut child_namespace = Namespace::default();
6513 parse_toplevel(
6514 parsed.code,
6515 &path,
6516 child_src_idx,
6517 fns,
6518 structs,
6519 enums,
6520 dynamic_libs,
6521 sources,
6522 &mut child_namespace,
6523 files,
6524 file_namespaces,
6525 pending_structs,
6526 pending_enums,
6527 pending_fns,
6528 pending_dylibs,
6529 pending_host,
6530 generics,
6531 resolver,
6532 );
6533 file_namespaces.insert(child_src_idx, child_namespace);
6534 file_namespaces.mark_std(child_src_idx);
6535 generics.mark_prelude_file(child_src_idx);
6536 }
6537}
6538
6539#[cfg(not(target_arch = "wasm32"))]
6557fn open_logical_dylib(dirs: &[&Path], filename: &str) -> Option<Library> {
6558 let mut candidates: Vec<PathBuf> = Vec::with_capacity(dirs.len() + 1);
6559 for dir in dirs {
6560 let candidate = dir.join(filename);
6561 if !candidates.contains(&candidate) {
6562 candidates.push(candidate);
6563 }
6564 }
6565 let cwd_candidate = Path::new(".").join(filename);
6566 if !candidates.contains(&cwd_candidate) {
6567 candidates.push(cwd_candidate);
6568 }
6569 for candidate in &candidates {
6570 if let Ok(lib) = unsafe { Library::new(candidate) } {
6571 return Some(lib);
6572 }
6573 }
6574 unsafe { Library::new(filename) }.ok()
6575}
6576
6577fn has_c_representation(ty: &DataType, structs: &[Struct], depth: usize) -> bool {
6582 match ty {
6583 DataType::Int
6584 | DataType::Float
6585 | DataType::String
6586 | DataType::Array(_)
6587 | DataType::Null => true,
6588 DataType::Struct(id) => {
6589 depth < structs.len()
6590 && structs.get(*id as usize).is_some_and(|s| {
6591 s.fields
6592 .iter()
6593 .all(|(_, field, _)| has_c_representation(field, structs, depth + 1))
6594 })
6595 }
6596 _ => false,
6597 }
6598}
6599
6600#[cfg(not(target_arch = "wasm32"))]
6604fn dylib_namespace(spec: &str, is_logical: bool) -> SmolStr {
6605 if is_logical {
6606 return SmolStr::from(spec);
6607 }
6608 Path::new(spec)
6609 .file_prefix()
6610 .and_then(|s| s.to_str())
6611 .unwrap_or(spec)
6612 .to_smolstr()
6613}
6614
6615#[cfg(not(target_arch = "wasm32"))]
6623fn open_path_dylib(dirs: &[&Path], spec: &str) -> (Option<Library>, SmolStr) {
6624 let name = dylib_namespace(spec, false);
6625
6626 if Path::new(spec).is_absolute() {
6627 return (open_library_path(spec), name);
6628 }
6629 for dir in dirs {
6630 if let Some(lib) = open_library_path(dir.join(spec).to_string_lossy().as_ref()) {
6631 return (Some(lib), name);
6632 }
6633 }
6634 (None, name)
6635}
6636
6637#[cfg(not(target_arch = "wasm32"))]
6647fn std_library_spec(lib_dir: Option<&Path>, file_path: &Path, spec: &str) -> Option<SmolStr> {
6648 let lib_dir = lib_dir?.canonicalize().ok()?;
6649 if !file_path.canonicalize().ok()?.starts_with(&lib_dir) {
6650 return None;
6651 }
6652 let target = file_path
6653 .parent()
6654 .unwrap_or_else(|| Path::new("."))
6655 .join(spec);
6656 let relative = target
6659 .parent()?
6660 .canonicalize()
6661 .ok()?
6662 .strip_prefix(&lib_dir)
6663 .ok()?
6664 .join(target.file_name()?);
6665 let mut recorded = String::new();
6668 for part in relative.components() {
6669 if !recorded.is_empty() {
6670 recorded.push('/');
6671 }
6672 recorded.push_str(part.as_os_str().to_str()?);
6673 }
6674 Some(SmolStr::from(recorded))
6675}
6676
6677#[cfg(not(target_arch = "wasm32"))]
6684fn open_library_path(base: &str) -> Option<Library> {
6685 let resolved = if Path::new(base).extension().is_none() {
6686 let arch_path = format!(
6687 "{base}{ARCH_SUFFIX}.{}",
6688 TargetOs::CURRENT.dynamic_lib_extension()
6689 );
6690 if Path::new(&arch_path).exists() {
6691 arch_path
6692 } else {
6693 resolve_library_filename(base, TargetOs::CURRENT)
6694 }
6695 } else {
6696 base.to_owned()
6697 };
6698 unsafe { Library::new(&resolved) }.ok()
6699}
6700
6701type PendingEnums = Vec<(u16, u16, Box<[(SmolStr, Box<[TypeExpr]>, Span)]>)>;
6706type PendingFns = Vec<(
6710 u16,
6711 u16,
6712 Box<[(SmolStr, Option<TypeExpr>)]>,
6713 ReturnAnnotation,
6714 TypeParams,
6715)>;
6716
6717fn declare_host_structs(
6727 host_namespace: &SmolStr,
6728 host_structs: Box<[Expr]>,
6729 src_file_idx: u16,
6730 structs: &mut Vec<Struct>,
6731 namespace: &mut Namespace,
6732 pending_structs: &mut Vec<(u16, u16, Box<[(SmolStr, TypeExpr, Span)]>)>,
6733 generics: &mut Generics,
6734) {
6735 if host_structs.is_empty() {
6736 return;
6737 }
6738 let local: Vec<SmolStr> = host_structs
6739 .iter()
6740 .filter_map(|decl| match decl {
6741 Expr::StructDeclare(name, ..) => Some(name.clone()),
6742 _ => None,
6743 })
6744 .collect();
6745 let child = if let Some(idx) = namespace
6746 .children
6747 .iter()
6748 .position(|(name, _)| name == host_namespace)
6749 {
6750 idx
6751 } else {
6752 namespace
6753 .children
6754 .push((host_namespace.clone(), Namespace::default()));
6755 namespace.children.len() - 1
6756 };
6757 for decl in host_structs {
6758 let Expr::StructDeclare(name, fields, span, _, defaults) = decl else {
6759 continue;
6760 };
6761 let struct_id = structs.len() as u16;
6762 structs.push(Struct {
6763 name: format_args!("{host_namespace}::{name}").to_smolstr(),
6764 fields: Box::from([]),
6765 id: struct_id,
6766 name_span: span,
6767 });
6768 generics.set_struct_defaults(struct_id, src_file_idx, defaults);
6769 namespace.children[child]
6770 .1
6771 .symbols
6772 .push((name, SymbolKind::Struct(struct_id)));
6773 let fields = fields
6774 .iter()
6775 .map(|(field, field_type, field_span)| {
6776 (
6777 field.clone(),
6778 qualify_host_type(field_type, host_namespace, &local),
6779 *field_span,
6780 )
6781 })
6782 .collect();
6783 pending_structs.push((struct_id, src_file_idx, fields));
6784 }
6785}
6786
6787fn qualify_host_type(t: &TypeExpr, host_namespace: &SmolStr, local: &[SmolStr]) -> TypeExpr {
6791 let qualify = |inner: &TypeExpr| qualify_host_type(inner, host_namespace, local);
6792 match t {
6793 TypeExpr::Identifier(name, span) if local.contains(name) => {
6794 TypeExpr::NamespacedIdentifier(Box::from([host_namespace.clone(), name.clone()]), *span)
6795 }
6796 TypeExpr::Array(inner) => TypeExpr::Array(Box::new(qualify(inner))),
6797 TypeExpr::Map(key, value) => {
6798 TypeExpr::Map(Box::new(qualify(key)), Box::new(qualify(value)))
6799 }
6800 TypeExpr::Union(members) => TypeExpr::Union(members.iter().map(qualify).collect()),
6801 TypeExpr::Fn(f) => TypeExpr::Fn(Box::new(FnTypeExpr {
6802 args: f.args.iter().map(qualify).collect(),
6803 return_type: f.return_type.as_ref().map(qualify),
6804 span: f.span,
6805 })),
6806 other => other.clone(),
6807 }
6808}
6809
6810fn bind_import(
6818 namespace: &mut Namespace,
6819 exports: Namespace,
6820 binding: ImportBinding,
6821 file_path: &Path,
6822 module_display: &SmolStr,
6823 merged_symbol_origins: &mut Vec<(SmolStr, SmolStr)>,
6824 span: Span,
6825 src_file_idx: u16,
6826 sources: &[Source],
6827) {
6828 let file: SmolStr = file_path.to_string_lossy().as_ref().into();
6829 match binding {
6830 ImportBinding::Namespace(name) => {
6831 if let Some((_, existing)) = namespace.children.iter().find(|(n, _)| *n == name) {
6832 if existing.file == file {
6834 return;
6835 }
6836 compiler_errors::error_import_name_collision(
6837 &name,
6838 &existing.import_path,
6839 module_display,
6840 span,
6841 src_file_idx,
6842 sources,
6843 );
6844 }
6845 namespace.children.push((
6846 name,
6847 Namespace {
6848 file,
6849 import_path: module_display.clone(),
6850 ..exports
6851 },
6852 ));
6853 }
6854 ImportBinding::Items(items) => {
6855 for (item, item_span) in items {
6856 let found: Vec<(SmolStr, SymbolKind)> = exports
6857 .symbols
6858 .iter()
6859 .filter(|(name, kind)| {
6860 *name == item && !(name == "main" && matches!(kind, SymbolKind::Fn(_)))
6861 })
6862 .cloned()
6863 .collect();
6864 if found.is_empty() {
6865 compiler_errors::error_import_item_not_exported(
6866 &item,
6867 module_display,
6868 &exported_names(&exports),
6869 item_span,
6870 src_file_idx,
6871 sources,
6872 );
6873 }
6874 let methods = exports.symbols.iter().filter(|(name, kind)| {
6876 matches!(kind, SymbolKind::Fn(_))
6877 && name
6878 .split_once(METHOD_SEP)
6879 .is_some_and(|(type_name, _)| type_name == item)
6880 });
6881 let methods: Vec<(SmolStr, SymbolKind)> = if found
6882 .iter()
6883 .any(|(_, kind)| !matches!(kind, SymbolKind::Fn(_)))
6884 {
6885 methods.cloned().collect()
6886 } else {
6887 Vec::new()
6888 };
6889 for (name, kind) in found.into_iter().chain(methods) {
6890 if let Some((_, existing)) = namespace.symbols.iter().find(|(n, _)| *n == name)
6891 {
6892 if symbol_ids_equal(*existing, kind) {
6895 continue;
6896 }
6897 let existing_origin = merged_symbol_origins
6898 .iter()
6899 .find(|(n, _)| *n == name)
6900 .map_or_else(
6901 || String::from("defined in this file"),
6902 |(_, module)| format!("imported from \"{module}\""),
6903 );
6904 compiler_errors::error_import_symbol_collision(
6905 &name,
6906 &existing_origin,
6907 module_display,
6908 item_span,
6909 src_file_idx,
6910 sources,
6911 );
6912 }
6913 merged_symbol_origins.push((name.clone(), module_display.clone()));
6914 namespace.symbols.push((name, kind));
6915 }
6916 }
6917 }
6918 }
6919}
6920
6921fn exported_names(exports: &Namespace) -> Vec<SmolStr> {
6925 let mut names: Vec<SmolStr> = Vec::new();
6926 for (name, kind) in &exports.symbols {
6927 if name.contains(METHOD_SEP)
6928 || name.starts_with(type_system::ANON_FN_PREFIX)
6929 || (name == "main" && matches!(kind, SymbolKind::Fn(_)))
6930 || names.contains(name)
6931 {
6932 continue;
6933 }
6934 names.push(name.clone());
6935 }
6936 names
6937}
6938
6939fn parse_toplevel(
6940 code: Vec<Expr>,
6941 file_path: &Path,
6942 src_file_idx: u16,
6943 fns: &mut Vec<Function>,
6944 structs: &mut Vec<Struct>,
6945 enums: &mut Vec<EnumType>,
6946 dynamic_libs: &mut Vec<Dynamiclib>,
6947 sources: &mut Vec<Source>,
6948 namespace: &mut Namespace,
6949 files: &mut FxHashMap<PathBuf, Namespace>,
6950 file_namespaces: &mut FileNamespaces,
6951 pending_structs: &mut Vec<(u16, u16, Box<[(SmolStr, TypeExpr, Span)]>)>,
6952 pending_enums: &mut PendingEnums,
6953 pending_fns: &mut PendingFns,
6954 pending_dylibs: &mut Vec<PendingDylib>,
6955 pending_host: &mut Vec<(
6956 u16,
6957 u16,
6958 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
6959 SmolStr,
6960 Span,
6961 )>,
6962 generics: &mut Generics,
6963 resolver: &ImportResolver,
6964) {
6965 let mut imports = Vec::new();
6966 for expr in code {
6967 match expr {
6968 Expr::FunctionDecl(fn_name, fn_args, fn_code, span, fn_return_type, type_params) => {
6969 if let Some((_, SymbolKind::Fn(func_id))) =
6970 namespace.symbols.iter().rfind(|(f, _)| f == &fn_name)
6971 {
6972 let func = &fns[*func_id as usize];
6973 compiler_errors::error_function_already_defined(
6974 func,
6975 span,
6976 src_file_idx,
6977 sources,
6978 );
6979 }
6980 check_operator_method(&fn_name, &fn_args, span, src_file_idx, sources);
6981 let returns_void = check_if_returns_void(&fn_code);
6982 let mut callees = Vec::new();
6983 collect_direct_fn_calls(&fn_code, self_type_of(&fn_name), &mut callees);
6984
6985 let fn_id = fns.len() as u16;
6986 fns.push(Function {
6987 name: fn_name.clone(),
6988 args: Box::new([]),
6989 code: fn_code,
6990 impls: Vec::new(),
6991 is_recursive: None,
6992 returns_null: returns_void,
6993 src_file: src_file_idx,
6994 return_type_cache: Vec::new(),
6995 direct_calls: callees.into_boxed_slice(),
6996 name_span: span,
6997 return_type: None,
7000 captures: Box::from([]),
7001 generics: (!type_params.is_empty()).then(|| {
7002 Box::new(FnGenerics {
7003 params: type_params.clone(),
7004 arg_types: fn_args.iter().map(|(_, t)| t.clone()).collect(),
7005 return_type: fn_return_type.clone(),
7006 bindings: Box::from([]),
7007 file_idx: src_file_idx,
7008 })
7009 }),
7010 entry_register: None,
7011 });
7012 pending_fns.push((fn_id, src_file_idx, fn_args, fn_return_type, type_params));
7013 namespace.symbols.push((fn_name, SymbolKind::Fn(fn_id)));
7014 }
7015 Expr::StructDeclare(name, fields, span, type_params, defaults) => {
7016 if !type_params.is_empty() {
7019 let template = generics.add_struct_template(
7020 name.clone(),
7021 type_params,
7022 src_file_idx,
7023 fields,
7024 defaults,
7025 );
7026 namespace
7027 .symbols
7028 .push((name, SymbolKind::Template(template)));
7029 continue;
7030 }
7031 let struct_id = structs.len() as u16;
7032 structs.push(Struct {
7033 name: name.clone(),
7034 fields: Box::from([]),
7035 id: struct_id,
7036 name_span: span,
7037 });
7038 generics.set_struct_defaults(struct_id, src_file_idx, defaults);
7039 namespace
7040 .symbols
7041 .push((name, SymbolKind::Struct(struct_id)));
7042 pending_structs.push((struct_id, src_file_idx, fields));
7043 }
7044 Expr::HostBlock(host_namespace, fn_signatures, span, host_structs) => {
7045 declare_host_structs(
7046 &host_namespace,
7047 host_structs,
7048 src_file_idx,
7049 structs,
7050 namespace,
7051 pending_structs,
7052 generics,
7053 );
7054 let local: Vec<SmolStr> = namespace
7055 .children
7056 .iter()
7057 .find(|(child, _)| *child == host_namespace)
7058 .map(|(_, ns)| ns.structs().map(|(n, _)| n.clone()).collect())
7059 .unwrap_or_default();
7060 let fn_signatures = if local.is_empty() {
7061 fn_signatures
7062 } else {
7063 fn_signatures
7064 .iter()
7065 .map(|(fn_name, args, ret, fn_span)| {
7066 (
7067 fn_name.clone(),
7068 args.iter()
7069 .map(|t| qualify_host_type(t, &host_namespace, &local))
7070 .collect(),
7071 qualify_host_type(ret, &host_namespace, &local),
7072 *fn_span,
7073 )
7074 })
7075 .collect()
7076 };
7077 imports.push(Expr::HostBlock(
7078 host_namespace,
7079 fn_signatures,
7080 span,
7081 Box::from([]),
7082 ));
7083 }
7084 Expr::EnumDeclare(name, variants, span, type_params) => {
7085 if !type_params.is_empty() {
7086 let template = generics.add_enum_template(
7087 name.clone(),
7088 type_params,
7089 src_file_idx,
7090 variants,
7091 );
7092 namespace
7093 .symbols
7094 .push((name, SymbolKind::Template(template)));
7095 continue;
7096 }
7097 let enum_id = enums.len() as u16;
7098 enums.push(EnumType {
7099 name: name.clone(),
7100 variants: Box::from([]),
7101 id: enum_id,
7102 name_span: span,
7103 });
7104 namespace.symbols.push((name, SymbolKind::Enum(enum_id)));
7105 pending_enums.push((enum_id, src_file_idx, variants));
7106 }
7107 #[cfg(target_arch = "wasm32")]
7111 Expr::ImportDylib(..) if file_path.to_str().and_then(embedded_std_module).is_none() => {
7112 wasm_error("WASM does not support loading dynamic libraries")
7113 }
7114 #[cfg(target_arch = "wasm32")]
7115 Expr::ImportFile(ref path, _, is_logical, _)
7116 if !is_logical || embedded_std_module(path).is_none() =>
7117 {
7118 wasm_error(
7119 "WASM does not support importing files. The standard library modules std/math, std/time, std/random and std/hash import",
7120 )
7121 }
7122 import @ (Expr::ImportFile(..) | Expr::ImportDylib(..)) => {
7123 imports.push(import);
7124 }
7125 _ => {}
7126 }
7127 }
7128
7129 files.insert(file_path.to_path_buf(), namespace.clone());
7130
7131 let mut merged_symbol_origins: Vec<(SmolStr, SmolStr)> = Vec::new();
7134
7135 for import in imports {
7136 match import {
7137 #[cfg(not(target_arch = "wasm32"))]
7138 Expr::ImportDylib(path, fn_signatures, span) => {
7139 let spec = path.clone();
7142 let is_logical = !spec.contains('/')
7147 && !spec.contains('\\')
7148 && !Path::new(spec.as_str()).is_absolute()
7149 && Path::new(spec.as_str()).extension().is_none();
7150
7151 let host_dirs = crate::rt::dylib_dirs();
7159 let package_dirs = resolver.root_dirs();
7160 let file_dir = file_path.parent().unwrap_or_else(|| Path::new("."));
7161 let mut dirs: Vec<&Path> = Vec::with_capacity(host_dirs.len() + 2);
7162 dirs.extend(host_dirs.iter().map(PathBuf::as_path));
7163 dirs.extend(package_dirs.iter().map(PathBuf::as_path));
7164 dirs.push(file_dir);
7165
7166 let (lib, dylib_name) = if is_check_only() {
7169 (None, dylib_namespace(spec.as_str(), is_logical))
7170 } else {
7171 let (lib, dylib_name) = if is_logical {
7172 let filename = resolve_library_filename(spec.as_str(), TargetOs::CURRENT);
7174 (open_logical_dylib(&dirs, &filename), spec.clone())
7175 } else {
7176 open_path_dylib(&dirs, spec.as_str())
7177 };
7178 let lib = lib
7179 .unwrap_or_else(|| error_cannot_load_dynlib(span, src_file_idx, sources));
7180 (Some(Rc::new(lib)), dylib_name)
7181 };
7182 let recorded = match std_library_spec(resolver.lib_dir(), file_path, spec.as_str())
7188 {
7189 Some(relative) => (relative, LibraryOrigin::StandardLibrary),
7190 None => (spec, LibraryOrigin::Program),
7191 };
7192 pending_dylibs.push((
7193 src_file_idx,
7194 dynamic_libs.len() as u16,
7195 fn_signatures,
7196 lib,
7197 recorded,
7198 span,
7199 ));
7200 dynamic_libs.push(Dynamiclib {
7201 name: dylib_name,
7202 fns: Box::new([]),
7203 is_host: false,
7204 });
7205 }
7206 #[cfg(target_arch = "wasm32")]
7211 Expr::ImportDylib(path, fn_signatures, span) => {
7212 let recorded = path.strip_prefix("../").unwrap_or(path.as_str());
7213 let dylib_name = Path::new(recorded)
7214 .file_prefix()
7215 .and_then(|s| s.to_str())
7216 .unwrap_or(recorded)
7217 .to_smolstr();
7218 pending_dylibs.push((
7219 src_file_idx,
7220 dynamic_libs.len() as u16,
7221 fn_signatures,
7222 (),
7223 (SmolStr::from(recorded), LibraryOrigin::StandardLibrary),
7224 span,
7225 ));
7226 dynamic_libs.push(Dynamiclib {
7227 name: dylib_name,
7228 fns: Box::new([]),
7229 is_host: false,
7230 });
7231 }
7232 Expr::HostBlock(host_namespace, fn_signatures, span, _) => {
7233 pending_host.push((
7234 src_file_idx,
7235 dynamic_libs.len() as u16,
7236 fn_signatures,
7237 host_namespace.clone(),
7238 span,
7239 ));
7240 dynamic_libs.push(Dynamiclib {
7241 name: host_namespace,
7242 fns: Box::new([]),
7243 is_host: true,
7244 });
7245 }
7246 Expr::ImportFile(path, binding, is_logical, span) => {
7247 let library = library_module(path.as_str(), resolver);
7251 #[cfg(not(target_arch = "wasm32"))]
7252 let library_path = library.as_ref().map(|(path, _)| path.clone());
7253 #[cfg(target_arch = "wasm32")]
7256 let file_path = library
7257 .as_ref()
7258 .map_or_else(|| PathBuf::from(path.as_str()), |(path, _)| path.clone());
7259 #[cfg(not(target_arch = "wasm32"))]
7260 let file_path = if is_logical {
7261 library_path.clone().unwrap_or_else(|| {
7266 error_cannot_read_file(span, src_file_idx, sources, &[]);
7267 })
7268 } else {
7269 let beside = file_path
7272 .parent()
7273 .unwrap_or_else(|| Path::new("."))
7274 .join(path.as_str());
7275 beside.canonicalize().unwrap_or_else(|_| {
7276 library_path.clone().unwrap_or_else(|| {
7277 error_cannot_read_file(
7278 span,
7279 src_file_idx,
7280 sources,
7281 std::slice::from_ref(&beside),
7282 );
7283 })
7284 })
7285 };
7286
7287 let child_namespace = if let Some(cached) = files.get(&file_path) {
7288 cached.clone()
7289 } else {
7290 let carried = library
7291 .as_ref()
7292 .filter(|(path, _)| *path == file_path)
7293 .and_then(|(_, text)| *text);
7294 let file_contents = match carried {
7295 Some(text) => String::from(text),
7296 #[cfg(target_arch = "wasm32")]
7297 None => String::new(),
7298 #[cfg(not(target_arch = "wasm32"))]
7299 None => std::fs::read_to_string(&file_path).unwrap_or_else(|_| {
7300 error_cannot_read_file(
7301 span,
7302 src_file_idx,
7303 sources,
7304 &tried_paths(&file_path, library_path.as_deref()),
7305 );
7306 }),
7307 };
7308 let file_name: SmolStr = if carried.is_some() {
7309 carried_module_filename(path.as_str(), resolver)
7310 } else {
7311 file_path.to_str().unwrap_or(path.as_str()).into()
7312 };
7313
7314 let child_src_idx = sources.len() as u16;
7315
7316 sources.push(Source {
7317 filename: file_name.clone(),
7318 contents: file_contents,
7319 });
7320
7321 let parsed =
7323 parser::parse(&sources.last().unwrap().contents, sources.last().unwrap());
7324 generics.add_impls(parsed.impls, child_src_idx);
7325
7326 let mut child_namespace = Namespace::default();
7327
7328 parse_toplevel(
7329 parsed.code,
7330 &file_path,
7331 child_src_idx,
7332 fns,
7333 structs,
7334 enums,
7335 dynamic_libs,
7336 sources,
7337 &mut child_namespace,
7338 files,
7339 file_namespaces,
7340 pending_structs,
7341 pending_enums,
7342 pending_fns,
7343 pending_dylibs,
7344 pending_host,
7345 generics,
7346 resolver,
7347 );
7348 file_namespaces.insert(child_src_idx, child_namespace.clone());
7349 if is_logical && path.starts_with("std/") {
7350 file_namespaces.mark_std(child_src_idx);
7351 }
7352 if is_logical && PRELUDE_MODULES.contains(&path.as_str()) {
7353 generics.mark_prelude_file(child_src_idx);
7354 }
7355 files.get(&file_path).cloned().unwrap_or_default()
7359 };
7360 let module_display: SmolStr = if is_logical {
7362 path.strip_suffix(".cdl").unwrap_or(path.as_str()).into()
7363 } else {
7364 path.clone()
7365 };
7366 bind_import(
7367 namespace,
7368 child_namespace,
7369 binding,
7370 &file_path,
7371 &module_display,
7372 &mut merged_symbol_origins,
7373 span,
7374 src_file_idx,
7375 sources,
7376 );
7377 }
7378 _ => unsafe { unreachable_unchecked() },
7379 }
7380 }
7381
7382 if src_file_idx == 0 {
7387 let first_template = generics.template_count();
7388 load_prelude(
7389 fns,
7390 structs,
7391 enums,
7392 dynamic_libs,
7393 sources,
7394 files,
7395 file_namespaces,
7396 pending_structs,
7397 pending_enums,
7398 pending_fns,
7399 pending_dylibs,
7400 pending_host,
7401 generics,
7402 resolver,
7403 );
7404 generics.set_prelude_templates(first_template..generics.template_count());
7405 }
7406}
7407
7408fn resolve_types(
7409 structs: &mut Vec<Struct>,
7410 enums: &mut Vec<EnumType>,
7411 fns: &mut Vec<Function>,
7412 generics: &mut Generics,
7413 pending_structs: Vec<(u16, u16, Box<[(SmolStr, TypeExpr, Span)]>)>,
7414 pending_enums: PendingEnums,
7415 pending_fns: PendingFns,
7416 pending_dylibs: Vec<PendingDylib>,
7417 pending_host: Vec<(
7418 u16,
7419 u16,
7420 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
7421 SmolStr,
7422 Span,
7423 )>,
7424 file_namespaces: &FileNamespaces,
7425 dynamic_libs_fns: &mut Vec<DynamicLibFn>,
7426 host_fns: &mut Vec<HostFnSig>,
7427 dynamic_libs: &mut [Dynamiclib],
7428 sources: &[Source],
7429) {
7430 for func in fns.iter() {
7433 let Some((type_name, _)) = func.name.split_once(METHOD_SEP) else {
7434 continue;
7435 };
7436 if let Some(params) = generics.params_of(type_name) {
7437 error_type_arg_count(
7438 func.name_span,
7439 func.src_file,
7440 type_name,
7441 params.len(),
7442 0,
7443 sources,
7444 );
7445 }
7446 }
7447 for (struct_id, src_file_idx, fields) in pending_structs {
7448 let resolved_fields = fields
7449 .iter()
7450 .map(|(field_name, field_type, field_span)| {
7451 (
7452 field_name.clone(),
7453 field_type.to_datatype(&mut TypeCtx {
7454 file_idx: src_file_idx,
7455 namespaces: file_namespaces,
7456 sources,
7457 structs,
7458 enums,
7459 fns,
7460 generics,
7461 }),
7462 *field_span,
7463 )
7464 })
7465 .collect();
7466 structs[struct_id as usize].fields = resolved_fields;
7467 }
7468 for (enum_id, src_file_idx, variants) in pending_enums {
7469 let resolved_variants = variants
7470 .iter()
7471 .map(|(variant_name, payload, name_span)| EnumVariant {
7472 name: variant_name.clone(),
7473 payload: payload
7474 .iter()
7475 .map(|t| {
7476 t.to_datatype(&mut TypeCtx {
7477 file_idx: src_file_idx,
7478 namespaces: file_namespaces,
7479 sources,
7480 structs,
7481 enums,
7482 fns,
7483 generics,
7484 })
7485 })
7486 .collect(),
7487 name_span: *name_span,
7488 })
7489 .collect();
7490 enums[enum_id as usize].variants = resolved_variants;
7491 }
7492 for (fn_id, src_file_idx, args, return_type, type_params) in pending_fns {
7493 let resolved_args = args
7497 .iter()
7498 .map(|(arg_name, arg_type)| {
7499 (
7500 arg_name.clone(),
7501 arg_type
7502 .clone()
7503 .filter(|t_e| !t_e.mentions_any(&type_params))
7504 .map(|t_e| {
7505 t_e.to_datatype(&mut TypeCtx {
7506 file_idx: src_file_idx,
7507 namespaces: file_namespaces,
7508 sources,
7509 structs,
7510 enums,
7511 fns,
7512 generics,
7513 })
7514 }),
7515 )
7516 })
7517 .collect();
7518 fns[fn_id as usize].args = resolved_args;
7519 fns[fn_id as usize].return_type = return_type
7520 .map(|annotation| *annotation)
7521 .filter(|(t_e, _)| !t_e.mentions_any(&type_params))
7522 .map(|(t_e, t_span)| {
7523 (
7524 t_e.to_datatype(&mut TypeCtx {
7525 file_idx: src_file_idx,
7526 namespaces: file_namespaces,
7527 sources,
7528 structs,
7529 enums,
7530 fns,
7531 generics,
7532 }),
7533 t_span,
7534 )
7535 });
7536 }
7537 for func in fns.iter() {
7540 if let Some(struct_id) = struct_of_default_fn(&func.name, structs) {
7541 check_struct_default_fn(
7542 func,
7543 struct_id,
7544 func.return_type.as_ref().map(|(t, span)| (t, *span)),
7545 sources,
7546 TypeNames { structs, enums },
7547 );
7548 }
7549 }
7550 #[cfg(not(target_arch = "wasm32"))]
7553 let mut unbound_dylib_fns = 0_usize;
7554 #[cfg(target_arch = "wasm32")]
7555 let unbound_dylib_fns = 0_usize;
7556 for (src_file_idx, dynlib_id, fn_signatures, lib, (library_spec, origin), span) in
7557 pending_dylibs
7558 {
7559 #[cfg(target_arch = "wasm32")]
7561 let () = lib;
7562 let resolved: Vec<FnSignature> = fn_signatures
7563 .iter()
7564 .map(|(fn_name, fn_args, fn_return_type, fn_name_span)| {
7565 let fn_args = fn_args
7566 .iter()
7567 .map(|t| {
7568 t.to_datatype(&mut TypeCtx {
7569 file_idx: src_file_idx,
7570 namespaces: file_namespaces,
7571 sources,
7572 structs,
7573 enums,
7574 fns,
7575 generics,
7576 })
7577 })
7578 .collect::<Vec<DataType>>()
7579 .into_boxed_slice();
7580 let fn_return_type = fn_return_type.to_datatype(&mut TypeCtx {
7581 file_idx: src_file_idx,
7582 namespaces: file_namespaces,
7583 sources,
7584 structs,
7585 enums,
7586 fns,
7587 generics,
7588 });
7589 if let Some(ty) = fn_args
7593 .iter()
7594 .chain(std::iter::once(&fn_return_type))
7595 .find(|ty| !has_c_representation(ty, structs, 0))
7596 {
7597 error_type_has_no_c_representation(
7598 fn_name,
7599 ty,
7600 *fn_name_span,
7601 src_file_idx,
7602 sources,
7603 TypeNames { structs, enums },
7604 );
7605 }
7606 let return_val = FnSignature {
7607 name: fn_name.clone(),
7608 args: fn_args.clone(),
7609 return_type: fn_return_type.clone(),
7610 id: (dynamic_libs_fns.len() + unbound_dylib_fns) as u16,
7611 variadic: false,
7612 };
7613 #[cfg(not(target_arch = "wasm32"))]
7617 let Some(lib) = lib.as_ref() else {
7618 unbound_dylib_fns += 1;
7619 return return_val;
7620 };
7621 #[cfg(not(target_arch = "wasm32"))]
7622 let arg_types: Vec<_> = fn_args.iter().map(|t| t.to_c_type(structs)).collect();
7623 #[cfg(not(target_arch = "wasm32"))]
7624 let return_type = fn_return_type.to_c_type(structs);
7625 #[cfg(not(target_arch = "wasm32"))]
7626 let cif = libffi::middle::Cif::new(arg_types, return_type);
7627 #[cfg(not(target_arch = "wasm32"))]
7628 let ptr = unsafe {
7629 libffi::middle::CodePtr(
7630 lib.get::<*const ()>(fn_name.as_bytes())
7631 .unwrap_or_else(|_| {
7632 error_cannot_find_dynlib_symbol(
7633 fn_name,
7634 *fn_name_span,
7635 span,
7636 src_file_idx,
7637 sources,
7638 );
7639 })
7640 .try_as_raw_ptr()
7641 .unwrap_unchecked(),
7642 )
7643 };
7644
7645 let mut types = vec![fn_return_type];
7646 types.extend(fn_args);
7647
7648 #[cfg(target_arch = "wasm32")]
7651 let intrinsic = candela_vm::intrinsics::lookup(&library_spec, fn_name)
7652 .unwrap_or_else(|| {
7653 error_cannot_find_dynlib_symbol(
7654 fn_name,
7655 *fn_name_span,
7656 span,
7657 src_file_idx,
7658 sources,
7659 );
7660 });
7661
7662 dynamic_libs_fns.push(DynamicLibFn {
7663 types: Box::from(types),
7664 library: library_spec.clone(),
7665 origin,
7666 symbol: fn_name.clone(),
7667 #[cfg(not(target_arch = "wasm32"))]
7668 _lib: Rc::clone(lib),
7669 #[cfg(not(target_arch = "wasm32"))]
7670 ptr,
7671 #[cfg(not(target_arch = "wasm32"))]
7672 cif,
7673 #[cfg(target_arch = "wasm32")]
7674 intrinsic,
7675 });
7676 return_val
7677 })
7678 .collect();
7679 dynamic_libs[dynlib_id as usize].fns = resolved.into_boxed_slice();
7680 }
7681
7682 for (src_file_idx, dynlib_id, fn_signatures, host_namespace, _span) in pending_host {
7687 let resolved: Vec<FnSignature> = fn_signatures
7688 .iter()
7689 .map(|(fn_name, fn_args, fn_return_type, _fn_name_span)| {
7690 let variadic = fn_args.len() == 1
7694 && matches!(&fn_args[0], TypeExpr::Identifier(s, _) if s.as_str() == "...");
7695 let fn_args = if variadic {
7696 Box::from([])
7697 } else {
7698 fn_args
7699 .iter()
7700 .map(|t| {
7701 t.to_datatype(&mut TypeCtx {
7702 file_idx: src_file_idx,
7703 namespaces: file_namespaces,
7704 sources,
7705 structs,
7706 enums,
7707 fns,
7708 generics,
7709 })
7710 })
7711 .collect::<Vec<DataType>>()
7712 .into_boxed_slice()
7713 };
7714 let fn_return_type = fn_return_type.to_datatype(&mut TypeCtx {
7715 file_idx: src_file_idx,
7716 namespaces: file_namespaces,
7717 sources,
7718 structs,
7719 enums,
7720 fns,
7721 generics,
7722 });
7723 let return_val = FnSignature {
7724 name: fn_name.clone(),
7725 args: fn_args.clone(),
7726 return_type: fn_return_type.clone(),
7727 id: host_fns.len() as u16,
7728 variadic,
7729 };
7730
7731 let mut types = vec![fn_return_type];
7732 types.extend(fn_args);
7733 host_fns.push(HostFnSig {
7734 types: types.into_boxed_slice(),
7735 namespace: host_namespace.clone(),
7736 name: fn_name.clone(),
7737 variadic,
7738 });
7739 return_val
7740 })
7741 .collect();
7742 dynamic_libs[dynlib_id as usize].fns = resolved.into_boxed_slice();
7743 }
7744}
7745
7746pub struct CompileOutput {
7754 pub instructions: Vec<Instr>,
7755 pub registers: Vec<Data>,
7756 pub pools: Pools,
7757 pub instr_src: Vec<InstrSrc>,
7758 pub callsite_registers: Vec<Vec<u16>>,
7759 pub dyn_lib_fns: Vec<DynamicLibFn>,
7760 pub host_fns: Vec<HostFnSig>,
7761 pub allocated_arg_count: usize,
7762 pub allocated_call_depth: usize,
7763 pub sources: Vec<Source>,
7764 pub structs: Vec<Struct>,
7765 pub enums: Vec<EnumType>,
7766 pub functions: Vec<Function>,
7767 pub dyn_libs: Vec<Dynamiclib>,
7768 pub namespaces: FileNamespaces,
7771 pub const_registers: FxHashMap<Data, u16>,
7772 pub free_registers: Vec<u16>,
7773 pub indirect_registers: IndirectRegisters,
7776 pub generics: Generics,
7780 pub optimize: bool,
7783}
7784
7785impl CompileOutput {
7786 pub fn require_main(&self) {
7793 if !self
7794 .functions
7795 .iter()
7796 .any(|func| func.name == "main" && func.src_file == 0)
7797 {
7798 compiler_errors::error_no_main(&self.sources);
7799 }
7800 }
7801}
7802
7803#[must_use]
7808pub fn compile(
7809 contents: String,
7810 filename: &str,
7811 debug: bool,
7812 resolver: &ImportResolver,
7813) -> CompileOutput {
7814 compile_profile(contents, filename, debug, resolver, false)
7815}
7816
7817#[must_use]
7824pub fn compile_profile(
7825 contents: String,
7826 filename: &str,
7827 debug: bool,
7828 resolver: &ImportResolver,
7829 optimize: bool,
7830) -> CompileOutput {
7831 #[cfg(not(target_arch = "wasm32"))]
7832 let now = std::time::Instant::now();
7833
7834 type_system::reset_inference_state();
7837 std_index::set_lib_dir(resolver.lib_dir().map(Path::to_path_buf));
7838
7839 let main_src = Source {
7840 filename: SmolStr::from(filename),
7841 contents,
7842 };
7843
7844 let parsed = parser::parse(&main_src.contents, &main_src);
7845
7846 #[cfg(not(target_arch = "wasm32"))]
7847 if debug {
7848 println!("PARSING TIME: {:.2?}", now.elapsed());
7849 }
7850
7851 let mut variables: Vec<Variable> = Vec::new();
7852 let mut registers: Vec<Data> = Vec::new();
7853 let mut pools: Pools = Pools::new(
7854 Pool::with_capacity(10),
7855 Pool::with_capacity(2),
7856 StringPool::with_capacity(10),
7857 );
7858 let mut instr_src: Vec<InstrSrc> = Vec::new();
7859 let mut callsite_registers: Vec<Vec<u16>> = Vec::new();
7860 let mut functions: Vec<Function> = Vec::new();
7861 let mut structs: Vec<Struct> = Vec::new();
7862 let mut enums: Vec<EnumType> = Vec::new();
7863 let mut dyn_libs: Vec<Dynamiclib> = Vec::new();
7864 let mut dyn_lib_fns: Vec<DynamicLibFn> = Vec::new();
7865 let mut host_fns: Vec<HostFnSig> = Vec::new();
7866 let mut allocated_arg_count = 0;
7867 let mut allocated_call_depth = 0;
7868 let mut const_registers: FxHashMap<Data, u16> = FxHashMap::default();
7869 let mut free_registers = Vec::new();
7870
7871 let mut sources: Vec<Source> = vec![main_src];
7872 let main_path = loaded_file_key(PathBuf::from(filename));
7873 let mut namespace = Namespace::default();
7874
7875 let mut files: FxHashMap<PathBuf, Namespace> = FxHashMap::default();
7876 let mut file_namespaces = FileNamespaces::default();
7877 let mut pending_structs: Vec<(u16, u16, Box<[(SmolStr, TypeExpr, Span)]>)> = Vec::new();
7878 let mut pending_enums: PendingEnums = Vec::new();
7879 let mut pending_fns: PendingFns = Vec::with_capacity(2);
7880 let mut generics = Generics::default();
7881 let mut pending_dylibs: Vec<PendingDylib> = Vec::new();
7882 let mut pending_host: Vec<(
7883 u16,
7884 u16,
7885 Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>,
7886 SmolStr,
7887 Span,
7888 )> = Vec::new();
7889
7890 generics.add_impls(parsed.impls, 0);
7891 parse_toplevel(
7892 parsed.code,
7893 &main_path,
7894 0,
7895 &mut functions,
7896 &mut structs,
7897 &mut enums,
7898 &mut dyn_libs,
7899 &mut sources,
7900 &mut namespace,
7901 &mut files,
7902 &mut file_namespaces,
7903 &mut pending_structs,
7904 &mut pending_enums,
7905 &mut pending_fns,
7906 &mut pending_dylibs,
7907 &mut pending_host,
7908 &mut generics,
7909 resolver,
7910 );
7911 file_namespaces.insert(0, namespace);
7912 qualify_duplicate_type_names(
7915 &mut structs,
7916 &mut enums,
7917 &mut functions,
7918 &pending_structs
7919 .iter()
7920 .map(|(struct_id, file_idx, _)| (*struct_id, *file_idx))
7921 .collect::<Vec<(u16, u16)>>(),
7922 &pending_enums
7923 .iter()
7924 .map(|(enum_id, file_idx, _)| (*enum_id, *file_idx))
7925 .collect::<Vec<(u16, u16)>>(),
7926 &file_namespaces,
7927 &sources,
7928 );
7929 resolve_types(
7930 &mut structs,
7931 &mut enums,
7932 &mut functions,
7933 &mut generics,
7934 pending_structs,
7935 pending_enums,
7936 pending_fns,
7937 pending_dylibs,
7938 pending_host,
7939 &file_namespaces,
7940 &mut dyn_lib_fns,
7941 &mut host_fns,
7942 &mut dyn_libs,
7943 &sources,
7944 );
7945
7946 let ctx = Ctx {
7947 block_id: 0,
7948 is_compiling_recursive: false,
7949 file_idx: 0,
7950 single_run: true,
7951 in_function: false,
7952 offset: 0,
7953 return_downcast: None,
7954 };
7955 let mut indirect_registers = IndirectRegisters::default();
7956 let mut state = State {
7957 registers: &mut registers,
7958 fns: &mut functions,
7959 structs: &mut structs,
7960 enums: &mut enums,
7961 pools: &mut pools,
7962 instr_src: &mut instr_src,
7963 callsite_registers: &mut callsite_registers,
7964 dyn_libs: &mut dyn_libs,
7965 allocated_arg_count: &mut allocated_arg_count,
7966 allocated_call_depth: &mut allocated_call_depth,
7967 const_registers: &mut const_registers,
7968 free_registers: &mut free_registers,
7969 sources: &mut sources,
7970 reserved_registers: FxHashSet::default(),
7971 value_callsites: FxHashSet::default(),
7972 optimize,
7973 namespaces: &mut file_namespaces,
7974 generics: &mut generics,
7975 indirect_registers: &mut indirect_registers,
7976 propagations: Vec::new(),
7977 fn_returns: Vec::new(),
7978 captured_binders: Vec::new(),
7979 };
7980 let main_body = state
7986 .fns
7987 .iter()
7988 .find(|func| func.name == "main" && func.src_file == 0)
7989 .map(|func| func.code.clone());
7990 let mut instructions = if let Some(code) = main_body {
7991 compile_expr(&code, &mut variables, ctx, &mut state)
7992 } else {
7993 Vec::new()
7994 };
7995 instructions.push(Instr::Halt(0));
7996 if optimize {
7997 let mut program = flow::Program {
7998 instructions: &mut instructions,
7999 registers: &mut registers,
8000 objs: &pools.objs,
8001 strings: &mut pools.strings,
8002 const_registers: &mut const_registers,
8003 instr_src: &mut instr_src,
8004 callsite_registers: &mut callsite_registers,
8005 functions: &mut functions,
8006 indirect: &indirect_registers,
8007 };
8008 while scalar::replace_scalars(&mut program) {}
8011 fold::fold_constants(&mut program);
8012 copies::forward_copies(&mut program);
8013 }
8014 use_immediates(&mut instructions, &[], ®isters, &const_registers);
8015 copies::chain_moves(&mut flow::Program {
8016 instructions: &mut instructions,
8017 registers: &mut registers,
8018 objs: &pools.objs,
8019 strings: &mut pools.strings,
8020 const_registers: &mut const_registers,
8021 instr_src: &mut instr_src,
8022 callsite_registers: &mut callsite_registers,
8023 functions: &mut functions,
8024 indirect: &indirect_registers,
8025 });
8026
8027 #[cfg(debug_assertions)]
8028 if debug {
8029 println!("---- DEBUG ----");
8030 if !pools.objs.is_empty() {
8031 println!("--- ARRAYS ---");
8032 for (i, data) in pools.objs.iter().enumerate() {
8033 println!(" {i} {data:?}");
8034 }
8035 }
8036 println!("-- REGISTERS --");
8037 for (i, data) in registers.iter().enumerate() {
8038 println!(
8039 " [{i}] {}",
8040 data.format(
8041 &pools.objs,
8042 &pools.strings,
8043 &pools.maps,
8044 &structs,
8045 &enums,
8046 true
8047 )
8048 );
8049 }
8050 if !instructions.is_empty() {
8051 println!("-- INSTRUCTIONS --");
8052 for (i, instr) in instructions.iter().enumerate() {
8053 println!(" {i}: {instr:?}");
8054 }
8055 }
8056 println!("------------------");
8057 }
8058
8059 CompileOutput {
8060 instructions,
8061 registers,
8062 pools,
8063 instr_src,
8064 callsite_registers,
8065 dyn_lib_fns,
8066 host_fns,
8067 allocated_arg_count,
8068 allocated_call_depth,
8069 sources,
8070 structs,
8071 enums,
8072 functions,
8073 dyn_libs,
8074 namespaces: file_namespaces,
8075 const_registers,
8076 free_registers,
8077 indirect_registers,
8078 generics,
8079 optimize,
8080 }
8081}