1use crate::value::VmDictExt;
2use std::collections::BTreeMap;
3use std::sync::Arc;
4
5use harn_parser::{substitute_type_expr, Node, SNode, ShapeField, TypeExpr, TypedParam};
6
7use crate::chunk::{Chunk, CompiledFunction, Constant, Op};
8use crate::value::VmValue;
9
10use super::error::CompileError;
11use super::yield_scan::body_contains_yield;
12use super::{peel_node, Compiler, CompilerOptions, FinallyEntry};
13
14#[cfg(test)]
15thread_local! {
16 pub(super) static FORCE_DISCARDED_PRODUCES_VALUE: std::cell::Cell<Option<bool>> =
23 const { std::cell::Cell::new(None) };
24}
25
26impl Compiler {
27 pub fn new() -> Self {
28 Self::with_options(CompilerOptions::from_env())
29 }
30
31 pub fn with_options(options: CompilerOptions) -> Self {
32 Self {
33 options,
34 chunk: Chunk::new(),
35 line: 1,
36 column: 1,
37 enum_names: std::collections::HashSet::new(),
38 enum_variant_owners: std::collections::HashMap::new(),
39 predeclared_enum_declarations: std::collections::HashSet::new(),
40 enum_catalog_scopes: Vec::new(),
41 struct_layouts: std::collections::HashMap::new(),
42 interface_methods: std::collections::HashMap::new(),
43 loop_stack: Vec::new(),
44 handler_depth: 0,
45 finally_bodies: Vec::new(),
46 temp_counter: 0,
47 scope_depth: 0,
48 type_aliases: std::collections::HashMap::new(),
49 type_scopes: vec![std::collections::HashMap::new()],
50 monomorphic_bindings: std::collections::HashSet::new(),
51 string_constants: std::collections::HashMap::new(),
52 local_scopes: vec![std::collections::HashMap::new()],
53 module_level: true,
54 captured_bindings: std::collections::HashSet::new(),
55 }
56 }
57
58 pub(super) fn for_nested_body(options: CompilerOptions) -> Self {
62 let mut c = Self::with_options(options);
63 c.module_level = false;
64 c
65 }
66
67 pub(super) fn nested_body(&self) -> Self {
68 Self::for_nested_body(self.options)
69 }
70
71 pub(super) fn nominal_type_names(&self) -> Vec<String> {
72 let mut names: Vec<String> = self
73 .struct_layouts
74 .keys()
75 .chain(self.enum_names.iter())
76 .cloned()
77 .collect();
78 names.sort();
79 names.dedup();
80 names
81 }
82
83 pub(super) fn string_constant(&mut self, value: &str) -> u16 {
84 if let Some(idx) = self.string_constants.get(value) {
85 return *idx;
86 }
87 let owned = value.to_string();
88 let idx = self.chunk.add_constant(Constant::String(owned.clone()));
89 self.string_constants.insert(owned, idx);
90 idx
91 }
92
93 pub(super) fn owned_string_constant(&mut self, value: String) -> u16 {
94 if let Some(idx) = self.string_constants.get(value.as_str()) {
95 return *idx;
96 }
97 let idx = self.chunk.add_constant(Constant::String(value.clone()));
98 self.string_constants.insert(value, idx);
99 idx
100 }
101
102 pub(crate) fn collect_type_aliases(&mut self, program: &[SNode]) {
106 for sn in program {
107 match peel_node(sn) {
108 Node::SelectiveImport { names, .. } => {
109 for name in names {
110 self.type_aliases.entry(name.clone()).or_insert_with(|| {
111 super::TypeAliasDefinition {
112 type_params: Vec::new(),
113 body: None,
114 }
115 });
116 }
117 }
118 Node::TypeDecl {
119 name,
120 type_expr,
121 type_params,
122 is_pub: _,
123 } => {
124 self.type_aliases.insert(
125 name.clone(),
126 super::TypeAliasDefinition {
127 type_params: type_params.clone(),
128 body: Some(type_expr.clone()),
129 },
130 );
131 }
132 _ => {}
133 }
134 }
135 }
136
137 pub(crate) fn expand_alias(&self, ty: &TypeExpr) -> TypeExpr {
147 let mut visiting = std::collections::HashSet::new();
148 self.expand_alias_inner(ty, &mut visiting)
149 }
150
151 fn expand_alias_inner(
152 &self,
153 ty: &TypeExpr,
154 visiting: &mut std::collections::HashSet<String>,
155 ) -> TypeExpr {
156 match ty {
157 TypeExpr::Named(name) => {
158 if let Some(target) = self
159 .type_aliases
160 .get(name)
161 .filter(|alias| alias.type_params.is_empty() && alias.body.is_some())
162 {
163 if !visiting.insert(name.clone()) {
164 return TypeExpr::Named(name.clone());
165 }
166 let resolved = self.expand_alias_inner(target.body.as_ref().unwrap(), visiting);
167 visiting.remove(name);
168 resolved
169 } else {
170 TypeExpr::Named(name.clone())
171 }
172 }
173 TypeExpr::Union(types) => TypeExpr::Union(
174 types
175 .iter()
176 .map(|t| self.expand_alias_inner(t, visiting))
177 .collect(),
178 ),
179 TypeExpr::Intersection(types) => TypeExpr::Intersection(
180 types
181 .iter()
182 .map(|t| self.expand_alias_inner(t, visiting))
183 .collect(),
184 ),
185 TypeExpr::Shape(fields) => TypeExpr::Shape(
186 fields
187 .iter()
188 .map(|field| ShapeField {
189 type_expr: self.expand_alias_inner(&field.type_expr, visiting),
190 ..field.clone()
191 })
192 .collect(),
193 ),
194 TypeExpr::OpenShape { fields, rests } => TypeExpr::OpenShape {
195 fields: fields
196 .iter()
197 .map(|field| ShapeField {
198 type_expr: self.expand_alias_inner(&field.type_expr, visiting),
199 ..field.clone()
200 })
201 .collect(),
202 rests: rests
203 .iter()
204 .map(|r| self.expand_alias_inner(r, visiting))
205 .collect(),
206 },
207 TypeExpr::List(inner) => {
208 TypeExpr::List(Box::new(self.expand_alias_inner(inner, visiting)))
209 }
210 TypeExpr::Iter(inner) => {
211 TypeExpr::Iter(Box::new(self.expand_alias_inner(inner, visiting)))
212 }
213 TypeExpr::Generator(inner) => {
214 TypeExpr::Generator(Box::new(self.expand_alias_inner(inner, visiting)))
215 }
216 TypeExpr::Stream(inner) => {
217 TypeExpr::Stream(Box::new(self.expand_alias_inner(inner, visiting)))
218 }
219 TypeExpr::DictType(k, v) => TypeExpr::DictType(
220 Box::new(self.expand_alias_inner(k, visiting)),
221 Box::new(self.expand_alias_inner(v, visiting)),
222 ),
223 TypeExpr::FnType {
224 params,
225 return_type,
226 } => TypeExpr::FnType {
227 params: params
228 .iter()
229 .map(|p| self.expand_alias_inner(p, visiting))
230 .collect(),
231 return_type: Box::new(self.expand_alias_inner(return_type, visiting)),
232 },
233 TypeExpr::Applied { name, args } => {
234 let args = args
235 .iter()
236 .map(|arg| self.expand_alias_inner(arg, visiting))
237 .collect::<Vec<_>>();
238 let Some(alias) = self.type_aliases.get(name) else {
239 return TypeExpr::Applied {
240 name: name.clone(),
241 args,
242 };
243 };
244 let Some(body) = alias.body.as_ref() else {
245 return TypeExpr::Applied {
246 name: name.clone(),
247 args,
248 };
249 };
250 if alias.type_params.len() != args.len() || !visiting.insert(name.clone()) {
251 return TypeExpr::Applied {
252 name: name.clone(),
253 args,
254 };
255 }
256 let bindings = alias
257 .type_params
258 .iter()
259 .zip(args.iter().cloned())
260 .map(|(param, arg)| (param.name.clone(), arg))
261 .collect();
262 let instantiated = substitute_type_expr(body, &bindings);
263 let resolved = self.expand_alias_inner(&instantiated, visiting);
264 visiting.remove(name);
265 resolved
266 }
267 TypeExpr::Never => TypeExpr::Never,
268 TypeExpr::LitString(s) => TypeExpr::LitString(s.clone()),
269 TypeExpr::LitInt(v) => TypeExpr::LitInt(*v),
270 TypeExpr::Owned(inner) => {
271 TypeExpr::Owned(Box::new(self.expand_alias_inner(inner, visiting)))
272 }
273 }
274 }
275
276 pub fn compile_public_type_schema_initializers(
280 program: &[SNode],
281 source_file: Option<String>,
282 ) -> Result<Option<Chunk>, CompileError> {
283 let mut compiler = Compiler::new();
284 compiler.collect_type_aliases(program);
285 compiler.chunk.source_file = source_file;
286 let mut emitted = false;
287 for sn in program {
288 let Node::TypeDecl {
289 name, is_pub: true, ..
290 } = peel_node(sn)
291 else {
292 continue;
293 };
294 if compiler.emit_schema_for_alias(name) {
295 compiler.emit_define_binding(name, false);
296 emitted = true;
297 }
298 }
299 if !emitted {
300 return Ok(None);
301 }
302 compiler.chunk.emit(Op::Nil, compiler.line);
303 compiler.chunk.emit(Op::Return, compiler.line);
304 super::ensure_chunk_addressable(
305 &compiler.chunk,
306 "the module type-schema initializer",
307 compiler.line,
308 )?;
309 Ok(Some(compiler.chunk))
310 }
311
312 pub(super) fn is_schema_guard(name: &str) -> bool {
316 matches!(
317 name,
318 "schema_is"
319 | "schema_expect"
320 | "schema_parse"
321 | "schema_check"
322 | "schema_report"
323 | "is_type"
324 | "json_validate"
325 )
326 }
327
328 pub(super) fn entry_key_is(key: &SNode, keyword: &str) -> bool {
331 matches!(
332 &key.node,
333 Node::Identifier(name) | Node::StringLiteral(name) | Node::RawStringLiteral(name)
334 if name == keyword
335 )
336 }
337
338 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
341 self.seed_module_catalog(program);
344 self.seed_module_captured_idents(program);
349
350 for sn in program {
351 match &sn.node {
352 Node::ImportDecl { .. } | Node::SelectiveImport { .. } => {
353 self.compile_node(sn)?;
354 }
355 _ => {}
356 }
357 }
358 let main = program
359 .iter()
360 .find(|sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == "default"))
361 .or_else(|| {
362 program
363 .iter()
364 .find(|sn| matches!(peel_node(sn), Node::Pipeline { .. }))
365 });
366
367 let mut pipeline_emits_value = false;
371 if let Some(sn) = main {
372 self.compile_top_level_declarations(program)?;
373 if let Node::Pipeline { body, extends, .. } = peel_node(sn) {
374 self.compile_with_pipeline_captures(
375 program,
376 body,
377 extends.as_deref(),
378 |compiler| {
379 if let Some(parent_name) = extends {
380 compiler.compile_parent_pipeline(program, parent_name)?;
381 }
382 let saved = std::mem::replace(&mut compiler.module_level, false);
383 let result = compiler.compile_block(body);
384 compiler.module_level = saved;
385 result
386 },
387 )?;
388 pipeline_emits_value = true;
389 }
390 } else {
391 let top_level: Vec<&SNode> = program
393 .iter()
394 .filter(|sn| {
395 !matches!(
396 &sn.node,
397 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
398 )
399 })
400 .collect();
401 for sn in &top_level {
402 self.compile_discarded_stmt(sn)?;
403 }
404 if Self::has_top_level_fn_main(program) {
409 let harness_name = self.string_constant("harness");
410 self.chunk.emit_u16(Op::GetVar, harness_name, self.line);
411 self.emit_named_call("main", 1);
412 pipeline_emits_value = true;
413 }
414 }
415
416 self.drain_finallys_to_floor(0)?;
417 if !pipeline_emits_value {
418 self.chunk.emit(Op::Nil, self.line);
419 }
420 self.chunk.emit(Op::Return, self.line);
421 super::ensure_chunk_addressable(&self.chunk, "the program body", self.line)?;
422 Ok(self.chunk)
423 }
424
425 fn has_top_level_fn_main(program: &[SNode]) -> bool {
429 program
430 .iter()
431 .any(|sn| matches!(peel_node(sn), Node::FnDecl { name, .. } if name == "main"))
432 }
433
434 pub fn compile_named(
436 self,
437 program: &[SNode],
438 pipeline_name: &str,
439 ) -> Result<Chunk, CompileError> {
440 self.compile_named_inner(program, pipeline_name, false)
441 }
442
443 pub fn compile_named_with_param_globals(
450 self,
451 program: &[SNode],
452 pipeline_name: &str,
453 ) -> Result<Chunk, CompileError> {
454 self.compile_named_inner(program, pipeline_name, true)
455 }
456
457 fn compile_named_inner(
458 mut self,
459 program: &[SNode],
460 pipeline_name: &str,
461 bind_params_from_globals: bool,
462 ) -> Result<Chunk, CompileError> {
463 self.collect_module_enum_catalog(program);
464 if self.enum_names.insert("Result".to_string()) {
465 Self::seed_builtin_variant_owners(&mut self.enum_variant_owners);
466 }
467 Self::collect_struct_layouts(program, &mut self.struct_layouts);
468 Self::collect_interface_methods(program, &mut self.interface_methods);
469 self.collect_type_aliases(program);
470 self.seed_module_captured_idents(program);
475
476 for sn in program {
477 if matches!(
478 &sn.node,
479 Node::ImportDecl { .. } | Node::SelectiveImport { .. }
480 ) {
481 self.compile_node(sn)?;
482 }
483 }
484 let target = program.iter().find(
485 |sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == pipeline_name),
486 );
487
488 let mut pipeline_emits_value = false;
489 if let Some(sn) = target {
490 self.compile_top_level_declarations(program)?;
491 if let Node::Pipeline {
492 name,
493 body,
494 extends,
495 params,
496 ..
497 } = peel_node(sn)
498 {
499 if bind_params_from_globals {
500 let callable = self.compile_pipeline_callable(
501 program,
502 name,
503 params,
504 body,
505 extends.as_deref(),
506 )?;
507 let function_index = self.chunk.functions.len();
508 self.chunk.functions.push(Arc::new(callable));
509 self.chunk
510 .emit_u16(Op::Closure, function_index as u16, self.line);
511 for param in params {
512 let index = self.string_constant(¶m.name);
513 self.chunk.emit_u16(Op::GetVar, index, self.line);
514 }
515 self.chunk.emit_u8(Op::Call, params.len() as u8, self.line);
516 pipeline_emits_value = true;
517 } else {
518 self.compile_with_pipeline_captures(
519 program,
520 body,
521 extends.as_deref(),
522 |compiler| {
523 if let Some(parent_name) = extends {
524 compiler.compile_parent_pipeline(program, parent_name)?;
525 }
526 let saved = std::mem::replace(&mut compiler.module_level, false);
527 let result = compiler.compile_block(body);
528 compiler.module_level = saved;
529 result
530 },
531 )?;
532 }
533 }
534 }
535
536 self.drain_finallys_to_floor(0)?;
537 if !pipeline_emits_value {
538 self.chunk.emit(Op::Nil, self.line);
539 }
540 self.chunk.emit(Op::Return, self.line);
541 super::ensure_chunk_addressable(&self.chunk, "the pipeline body", self.line)?;
542 Ok(self.chunk)
543 }
544
545 pub(super) fn emit_default_preamble(
550 &mut self,
551 params: &[TypedParam],
552 ) -> Result<(), CompileError> {
553 for (i, param) in params.iter().enumerate() {
554 if let Some(default_expr) = ¶m.default_value {
555 self.chunk.emit(Op::GetArgc, self.line);
556 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
557 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
558 self.chunk.emit(Op::GreaterEqual, self.line);
559 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
560 self.chunk.emit(Op::Pop, self.line);
562 let masked = self.mask_param_names(¶ms[i..]);
571 let result = self.compile_node(default_expr);
572 self.restore_param_names(masked);
573 result?;
574 self.emit_init_or_define_binding(¶m.name, false);
575 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
576 self.chunk.patch_jump(skip_jump);
577 self.chunk.emit(Op::Pop, self.line);
578 self.chunk.patch_jump(end_jump);
579 }
580 }
581 Ok(())
582 }
583
584 pub(super) fn emit_type_checks(&mut self, params: &[TypedParam]) {
591 for (param_index, param) in params.iter().enumerate() {
592 if let Some(type_expr) = ¶m.type_expr {
593 let check_type = if param.rest {
594 harn_parser::TypeExpr::List(Box::new(type_expr.clone()))
595 } else {
596 type_expr.clone()
597 };
598
599 if let harn_parser::TypeExpr::Named(name) = &check_type {
600 if let Some(methods) = self.interface_methods.get(name).cloned() {
601 let fn_idx = self.string_constant("__assert_interface");
602 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
603 self.emit_get_binding(¶m.name);
604 let name_idx = self.string_constant(¶m.name);
605 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
606 let iface_idx = self.string_constant(name);
607 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
608 let methods_str = methods.join(",");
609 let methods_idx = self.owned_string_constant(methods_str);
610 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
611 self.chunk.emit_u8(Op::Call, 4, self.line);
612 self.chunk.emit(Op::Pop, self.line);
613 continue;
614 }
615 }
616
617 if param.default_value.is_some() {
618 if let Some(schema) = Self::type_expr_to_schema_value(&check_type) {
619 self.emit_default_param_schema_check(param_index, param, &schema);
620 }
621 }
622 }
623 }
624 }
625
626 fn emit_default_param_schema_check(
627 &mut self,
628 param_index: usize,
629 param: &TypedParam,
630 schema: &VmValue,
631 ) {
632 self.chunk.emit(Op::GetArgc, self.line);
633 let threshold_idx = self
634 .chunk
635 .add_constant(Constant::Int((param_index + 1) as i64));
636 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
637 self.chunk.emit(Op::GreaterEqual, self.line);
638 let supplied_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
639 self.chunk.emit(Op::Pop, self.line);
640 self.emit_schema_assert_call(param, schema);
641 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
642 self.chunk.patch_jump(supplied_jump);
643 self.chunk.emit(Op::Pop, self.line);
644 self.chunk.patch_jump(end_jump);
645 }
646
647 fn emit_schema_assert_call(&mut self, param: &TypedParam, schema: &VmValue) {
648 let fn_idx = self.string_constant("__assert_schema");
649 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
650 self.emit_get_binding(¶m.name);
651 let name_idx = self.string_constant(¶m.name);
652 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
653 self.emit_vm_value_literal(schema);
654 self.chunk.emit_u8(Op::Call, 3, self.line);
655 self.chunk.emit(Op::Pop, self.line);
656 }
657
658 pub(crate) fn type_expr_to_schema_value(type_expr: &harn_parser::TypeExpr) -> Option<VmValue> {
659 match type_expr {
660 harn_parser::TypeExpr::Named(name) => match name.as_str() {
661 "any" | "unknown" => Some(VmValue::dict(BTreeMap::<String, VmValue>::new())),
662 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
663 | "closure" | "bytes" => Some(VmValue::dict(BTreeMap::from([(
664 "type".to_string(),
665 VmValue::String(arcstr::ArcStr::from(name.as_str())),
666 )]))),
667 _ => None,
668 },
669 harn_parser::TypeExpr::Shape(fields) => {
670 let mut properties = BTreeMap::new();
671 let mut required = Vec::new();
672 for field in fields {
673 let mut field_schema = Self::type_expr_to_schema_value(&field.type_expr)?;
674 if field.optional {
675 field_schema = VmValue::dict(BTreeMap::from([(
676 "union".to_string(),
677 VmValue::List(std::sync::Arc::new(vec![
678 field_schema,
679 VmValue::dict(BTreeMap::from([(
680 "type".to_string(),
681 VmValue::String(arcstr::ArcStr::from("nil")),
682 )])),
683 ])),
684 )]));
685 }
686 properties.insert(field.name.clone(), field_schema);
687 if !field.optional {
688 required.push(VmValue::String(arcstr::ArcStr::from(field.name.as_str())));
689 }
690 }
691 let mut out = BTreeMap::new();
692 out.put_str("type", "dict");
693 out.insert("properties".to_string(), VmValue::dict(properties));
694 if !required.is_empty() {
695 out.insert(
696 "required".to_string(),
697 VmValue::List(std::sync::Arc::new(required)),
698 );
699 }
700 Some(VmValue::dict(out))
701 }
702 harn_parser::TypeExpr::OpenShape { .. } => None,
703 harn_parser::TypeExpr::List(inner) => {
704 let mut out = BTreeMap::new();
705 out.put_str("type", "list");
706 let item_schema = Self::type_expr_to_schema_value(inner)?;
707 out.insert("items".to_string(), item_schema);
708 Some(VmValue::dict(out))
709 }
710 harn_parser::TypeExpr::DictType(key, value) => {
711 let mut out = BTreeMap::new();
712 out.put_str("type", "dict");
713 if matches!(key.as_ref(), harn_parser::TypeExpr::Named(name) if name == "string") {
714 let value_schema = Self::type_expr_to_schema_value(value)?;
715 out.insert("additional_properties".to_string(), value_schema);
716 }
717 Some(VmValue::dict(out))
718 }
719 harn_parser::TypeExpr::Union(members) => {
720 if !members.is_empty()
725 && members
726 .iter()
727 .all(|m| matches!(m, harn_parser::TypeExpr::LitString(_)))
728 {
729 let values = members
730 .iter()
731 .map(|m| match m {
732 harn_parser::TypeExpr::LitString(s) => {
733 VmValue::String(arcstr::ArcStr::from(s.as_str()))
734 }
735 _ => unreachable!(),
736 })
737 .collect::<Vec<_>>();
738 return Some(VmValue::dict(BTreeMap::from([
739 (
740 "type".to_string(),
741 VmValue::String(arcstr::ArcStr::from("string")),
742 ),
743 (
744 "enum".to_string(),
745 VmValue::List(std::sync::Arc::new(values)),
746 ),
747 ])));
748 }
749 if !members.is_empty()
750 && members
751 .iter()
752 .all(|m| matches!(m, harn_parser::TypeExpr::LitInt(_)))
753 {
754 let values = members
755 .iter()
756 .map(|m| match m {
757 harn_parser::TypeExpr::LitInt(v) => VmValue::Int(*v),
758 _ => unreachable!(),
759 })
760 .collect::<Vec<_>>();
761 return Some(VmValue::dict(BTreeMap::from([
762 (
763 "type".to_string(),
764 VmValue::String(arcstr::ArcStr::from("int")),
765 ),
766 (
767 "enum".to_string(),
768 VmValue::List(std::sync::Arc::new(values)),
769 ),
770 ])));
771 }
772 let branches = members
773 .iter()
774 .map(Self::type_expr_to_schema_value)
775 .collect::<Option<Vec<_>>>()?;
776 if branches.is_empty() {
777 None
778 } else {
779 Some(VmValue::dict(BTreeMap::from([(
780 "union".to_string(),
781 VmValue::List(std::sync::Arc::new(branches)),
782 )])))
783 }
784 }
785 harn_parser::TypeExpr::Intersection(members) => {
786 let branches = members
790 .iter()
791 .map(Self::type_expr_to_schema_value)
792 .collect::<Option<Vec<_>>>()?;
793 if branches.is_empty() {
794 None
795 } else {
796 Some(VmValue::dict(BTreeMap::from([(
797 "all_of".to_string(),
798 VmValue::List(std::sync::Arc::new(branches)),
799 )])))
800 }
801 }
802 harn_parser::TypeExpr::FnType { .. } => Some(VmValue::dict(BTreeMap::from([(
803 "type".to_string(),
804 VmValue::String(arcstr::ArcStr::from("closure")),
805 )]))),
806 harn_parser::TypeExpr::Applied { .. } => None,
807 harn_parser::TypeExpr::Iter(_)
808 | harn_parser::TypeExpr::Generator(_)
809 | harn_parser::TypeExpr::Stream(_) => None,
810 harn_parser::TypeExpr::Never => None,
811 harn_parser::TypeExpr::LitString(s) => Some(VmValue::dict(BTreeMap::from([
812 (
813 "type".to_string(),
814 VmValue::String(arcstr::ArcStr::from("string")),
815 ),
816 (
817 "const".to_string(),
818 VmValue::String(arcstr::ArcStr::from(s.as_str())),
819 ),
820 ]))),
821 harn_parser::TypeExpr::LitInt(v) => Some(VmValue::dict(BTreeMap::from([
822 (
823 "type".to_string(),
824 VmValue::String(arcstr::ArcStr::from("int")),
825 ),
826 ("const".to_string(), VmValue::Int(*v)),
827 ]))),
828 harn_parser::TypeExpr::Owned(inner) => Self::type_expr_to_schema_value(inner),
829 }
830 }
831
832 pub(super) fn emit_vm_value_literal(&mut self, value: &VmValue) {
833 match value {
834 VmValue::String(text) => {
835 let idx = self.string_constant(text);
836 self.chunk.emit_u16(Op::Constant, idx, self.line);
837 }
838 VmValue::Int(number) => {
839 let idx = self.chunk.add_constant(Constant::Int(*number));
840 self.chunk.emit_u16(Op::Constant, idx, self.line);
841 }
842 VmValue::Float(number) => {
843 let idx = self.chunk.add_constant(Constant::Float(*number));
844 self.chunk.emit_u16(Op::Constant, idx, self.line);
845 }
846 VmValue::Bool(value) => {
847 let idx = self.chunk.add_constant(Constant::Bool(*value));
848 self.chunk.emit_u16(Op::Constant, idx, self.line);
849 }
850 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
851 VmValue::List(items) => {
852 for item in items.iter() {
853 self.emit_vm_value_literal(item);
854 }
855 self.chunk
856 .emit_u16(Op::BuildList, items.len() as u16, self.line);
857 }
858 VmValue::Dict(entries) => {
859 for (key, item) in entries.iter() {
860 let key_idx = self.string_constant(key);
861 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
862 self.emit_vm_value_literal(item);
863 }
864 self.chunk
865 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
866 }
867 _ => {}
868 }
869 }
870
871 pub(super) fn emit_type_name_extra(&mut self, type_name_idx: u16) {
873 let hi = (type_name_idx >> 8) as u8;
874 let lo = type_name_idx as u8;
875 self.chunk.code.push(hi);
876 self.chunk.code.push(lo);
877 self.chunk.lines.push(self.line);
878 self.chunk.columns.push(self.column);
879 self.chunk.lines.push(self.line);
880 self.chunk.columns.push(self.column);
881 }
882
883 pub(super) fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
885 if body.is_empty() {
886 self.chunk.emit(Op::Nil, self.line);
887 } else {
888 self.compile_scoped_block(body)?;
889 }
890 Ok(())
891 }
892
893 pub(super) fn compile_catch_binding(
895 &mut self,
896 error_var: &Option<String>,
897 ) -> Result<(), CompileError> {
898 if let Some(var_name) = error_var {
899 self.emit_define_binding(var_name, false);
900 } else {
901 self.chunk.emit(Op::Pop, self.line);
902 }
903 Ok(())
904 }
905
906 pub(super) fn compile_finally_inline(
913 &mut self,
914 finally_body: &[SNode],
915 ) -> Result<(), CompileError> {
916 if !finally_body.is_empty() {
917 self.compile_scoped_block(finally_body)?;
918 self.chunk.emit(Op::Pop, self.line);
919 }
920 Ok(())
921 }
922
923 pub(super) fn has_pending_finally_until_barrier(&self) -> bool {
927 self.finally_bodies
928 .iter()
929 .rev()
930 .take_while(|entry| !matches!(entry, FinallyEntry::CatchBarrier))
931 .any(|entry| matches!(entry, FinallyEntry::Finally(_)))
932 }
933
934 pub(super) fn has_pending_finally(&self) -> bool {
936 self.finally_bodies
937 .iter()
938 .any(|e| matches!(e, FinallyEntry::Finally(_)))
939 }
940
941 pub(super) fn compile_plain_rethrow(&mut self) -> Result<(), CompileError> {
950 self.temp_counter += 1;
951 let temp_name = format!("__finally_err_{}__", self.temp_counter);
952 self.emit_define_binding(&temp_name, true);
953 self.emit_get_binding(&temp_name);
954 self.chunk.emit(Op::Throw, self.line);
955 Ok(())
956 }
957
958 pub(super) fn declare_param_slots(&mut self, params: &[TypedParam]) {
959 for param in params {
960 self.define_local_slot(¶m.name, false);
961 }
962 }
963
964 fn mask_param_names(&mut self, params: &[TypedParam]) -> Vec<(String, super::LocalBinding)> {
970 let mut removed = Vec::new();
971 if let Some(scope) = self.local_scopes.last_mut() {
972 for param in params {
973 if let Some(binding) = scope.remove(¶m.name) {
974 removed.push((param.name.clone(), binding));
975 }
976 }
977 }
978 removed
979 }
980
981 fn restore_param_names(&mut self, removed: Vec<(String, super::LocalBinding)>) {
983 if let Some(scope) = self.local_scopes.last_mut() {
984 for (name, binding) in removed {
985 scope.insert(name, binding);
986 }
987 }
988 }
989
990 pub(super) fn seed_captured_idents(&mut self, body: &[SNode]) {
995 let match_patterns = self.lexical_match_pattern_catalog();
996 self.captured_bindings =
997 harn_parser::lexical::captured_bindings_in_nested_callables(body, &match_patterns);
998 }
999
1000 fn seed_module_captured_idents(&mut self, body: &[SNode]) {
1001 let match_patterns = self.lexical_match_pattern_catalog();
1002 self.captured_bindings =
1003 harn_parser::lexical::captured_bindings_in_compiled_module(body, &match_patterns);
1004 }
1005
1006 pub(super) fn lexical_match_pattern_catalog(
1007 &self,
1008 ) -> harn_parser::lexical::MatchPatternCatalog {
1009 harn_parser::lexical::MatchPatternCatalog::new(&self.enum_names, &self.enum_variant_owners)
1010 }
1011
1012 pub(super) fn begin_scope(&mut self) {
1013 self.chunk.emit(Op::PushScope, self.line);
1014 self.scope_depth += 1;
1015 let enum_catalog = self.enum_catalog_snapshot();
1016 self.enum_catalog_scopes.push(enum_catalog);
1017 self.type_scopes.push(std::collections::HashMap::new());
1018 self.local_scopes.push(std::collections::HashMap::new());
1019 }
1020
1021 pub(super) fn end_scope(&mut self) {
1022 if self.scope_depth > 0 {
1023 self.chunk.emit(Op::PopScope, self.line);
1024 self.scope_depth -= 1;
1025 if let Some(snapshot) = self.enum_catalog_scopes.pop() {
1026 self.restore_enum_catalog(snapshot);
1027 }
1028 self.type_scopes.pop();
1029 self.local_scopes.pop();
1030 }
1031 }
1032
1033 pub(super) fn emit_scope_unwind_to(&mut self, target_depth: usize) {
1036 for _ in target_depth..self.scope_depth {
1037 self.chunk.emit(Op::PopScope, self.line);
1038 }
1039 }
1040
1041 pub(super) fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
1042 self.begin_scope();
1043 let finally_floor = self.finally_bodies.len();
1044 if stmts.is_empty() {
1045 self.chunk.emit(Op::Nil, self.line);
1046 } else {
1047 self.compile_block(stmts)?;
1048 }
1049 self.drain_finallys_to_floor(finally_floor)?;
1050 self.end_scope();
1051 Ok(())
1052 }
1053
1054 pub(super) fn compile_scoped_statements(
1055 &mut self,
1056 stmts: &[SNode],
1057 ) -> Result<(), CompileError> {
1058 self.begin_scope();
1059 self.record_monomorphic_var_bindings(stmts);
1060 let finally_floor = self.finally_bodies.len();
1061 for sn in stmts {
1062 self.compile_discarded_stmt(sn)?;
1063 }
1064 self.drain_finallys_to_floor(finally_floor)?;
1065 self.end_scope();
1066 Ok(())
1067 }
1068
1069 pub(super) fn drain_finallys_to_floor(&mut self, floor: usize) -> Result<(), CompileError> {
1074 while self.finally_bodies.len() > floor {
1075 let entry = self.finally_bodies.pop().expect("non-empty by guard");
1076 if let FinallyEntry::Finally(body) = entry {
1077 self.compile_finally_inline(&body)?;
1078 }
1079 }
1080 Ok(())
1081 }
1082
1083 pub(super) fn run_pending_finallys_for_transfer(
1096 &mut self,
1097 floor: usize,
1098 ) -> Result<(), CompileError> {
1099 if self.finally_bodies.len() <= floor {
1100 return Ok(());
1101 }
1102 let saved = self.finally_bodies[floor..].to_vec();
1103 let result = self.drain_finallys_to_floor(floor);
1104 self.finally_bodies.extend(saved);
1105 result
1106 }
1107
1108 pub(super) fn run_pending_finallys_until_barrier(&mut self) -> Result<(), CompileError> {
1113 let floor = self
1114 .finally_bodies
1115 .iter()
1116 .rposition(|e| matches!(e, FinallyEntry::CatchBarrier))
1117 .map(|i| i + 1)
1118 .unwrap_or(0);
1119 self.run_pending_finallys_for_transfer(floor)
1120 }
1121
1122 pub(super) fn maybe_register_owned_drop(
1127 &mut self,
1128 pattern: &harn_parser::BindingPattern,
1129 type_ann: Option<&TypeExpr>,
1130 span: harn_lexer::Span,
1131 ) {
1132 let Some(ty) = type_ann else {
1138 return;
1139 };
1140 if !matches!(ty, TypeExpr::Owned(_)) {
1141 return;
1142 }
1143 let harn_parser::BindingPattern::Identifier(name) = pattern else {
1144 return;
1145 };
1146 if harn_parser::is_discard_name(name) {
1147 return;
1148 }
1149 let call = harn_parser::spanned(
1150 Node::FunctionCall {
1151 name: "drop".to_string(),
1152 args: vec![harn_parser::spanned(Node::Identifier(name.clone()), span)],
1153 type_args: Vec::new(),
1154 },
1155 span,
1156 );
1157 self.finally_bodies.push(FinallyEntry::Finally(vec![call]));
1158 }
1159
1160 pub(super) fn compile_discarded_stmt(&mut self, sn: &SNode) -> Result<(), CompileError> {
1176 #[cfg(debug_assertions)]
1177 let probe = self.chunk.balance_probe();
1178 self.compile_node(sn)?;
1179 #[allow(unused_mut)]
1180 let mut produces = Self::produces_value(&sn.node);
1181 #[cfg(test)]
1185 if let Some(forced) = FORCE_DISCARDED_PRODUCES_VALUE.with(std::cell::Cell::get) {
1186 produces = forced;
1187 }
1188 #[cfg(debug_assertions)]
1189 if let Some(delta) = self.chunk.balance_delta_since(probe) {
1190 let expected = i32::from(produces);
1191 debug_assert_eq!(
1192 delta, expected,
1193 "operand-stack imbalance at line {}: produces_value={produces} but the \
1194 node's emitted bytecode netted {delta} (expected {expected}). A \
1195 `produces_value` arm is out of sync with this node's codegen — see #2622.\n\
1196 node: {:?}",
1197 self.line, sn.node,
1198 );
1199 }
1200 if produces {
1201 self.chunk.emit(Op::Pop, self.line);
1202 }
1203 Ok(())
1204 }
1205
1206 pub(super) fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
1207 self.record_monomorphic_var_bindings(stmts);
1208 for (i, snode) in stmts.iter().enumerate() {
1209 if i == stmts.len() - 1 {
1210 self.compile_node(snode)?;
1214 if !Self::produces_value(&snode.node) {
1215 self.chunk.emit(Op::Nil, self.line);
1216 }
1217 } else {
1218 self.compile_discarded_stmt(snode)?;
1219 }
1220 }
1221 Ok(())
1222 }
1223
1224 pub(super) fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
1226 self.begin_scope();
1227 let finally_floor = self.finally_bodies.len();
1228 if body.is_empty() {
1229 self.chunk.emit(Op::Nil, self.line);
1230 } else {
1231 self.compile_block(body)?;
1232 if !Self::produces_value(&body.last().unwrap().node) {
1233 self.chunk.emit(Op::Nil, self.line);
1234 }
1235 }
1236 self.drain_finallys_to_floor(finally_floor)?;
1237 self.end_scope();
1238 Ok(())
1239 }
1240
1241 pub(super) fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
1243 match op {
1244 "+" => self.chunk.emit(Op::Add, self.line),
1245 "-" => self.chunk.emit(Op::Sub, self.line),
1246 "*" => self.chunk.emit(Op::Mul, self.line),
1247 "/" => self.chunk.emit(Op::Div, self.line),
1248 "%" => self.chunk.emit(Op::Mod, self.line),
1249 _ => {
1250 return Err(CompileError {
1251 message: format!("Unknown compound operator: {op}"),
1252 line: self.line,
1253 })
1254 }
1255 }
1256 Ok(())
1257 }
1258
1259 pub(super) fn compile_top_level_declarations(
1260 &mut self,
1261 program: &[SNode],
1262 ) -> Result<(), CompileError> {
1263 for sn in program {
1275 if !harn_parser::lexical::is_deferred_module_declaration(sn) {
1276 self.compile_discarded_stmt(sn)?;
1277 }
1278 }
1279 for sn in program {
1285 let inner_kind = match &sn.node {
1286 Node::AttributedDecl { inner, .. } => &inner.node,
1287 other => other,
1288 };
1289 match inner_kind {
1290 Node::EvalPackDecl {
1291 binding_name,
1292 pack_id,
1293 fields,
1294 body,
1295 summarize,
1296 ..
1297 } => {
1298 self.compile_eval_pack_decl(
1299 binding_name,
1300 pack_id,
1301 fields,
1302 body,
1303 summarize,
1304 false,
1305 )?;
1306 }
1307 Node::FnDecl { .. }
1308 | Node::ToolDecl { .. }
1309 | Node::SkillDecl { .. }
1310 | Node::ImplBlock { .. }
1311 | Node::StructDecl { .. }
1312 | Node::EnumDecl { .. }
1313 | Node::InterfaceDecl { .. } => {
1314 self.compile_node(sn)?;
1315 }
1316 Node::TypeDecl { .. } => {}
1317 _ => {}
1318 }
1319 }
1320 Ok(())
1321 }
1322
1323 pub fn compile_fn_body(
1336 &mut self,
1337 type_params: &[harn_parser::TypeParam],
1338 params: &[TypedParam],
1339 body: &[SNode],
1340 source_file: Option<String>,
1341 ) -> Result<CompiledFunction, CompileError> {
1342 let mut fn_compiler = self.nested_body();
1343 fn_compiler.enum_names = self.enum_names.clone();
1344 fn_compiler.enum_variant_owners = self.enum_variant_owners.clone();
1345 fn_compiler.interface_methods = self.interface_methods.clone();
1346 fn_compiler.type_aliases = self.type_aliases.clone();
1347 fn_compiler.struct_layouts = self.struct_layouts.clone();
1348 fn_compiler.declare_param_slots(params);
1349 fn_compiler.record_param_types(params);
1350 fn_compiler.emit_default_preamble(params)?;
1351 fn_compiler.emit_type_checks(params);
1352 let is_gen = body_contains_yield(body);
1353 fn_compiler.seed_captured_idents(body);
1354 fn_compiler.compile_block(body)?;
1355 fn_compiler.chunk.emit(Op::Nil, 0);
1356 fn_compiler.chunk.emit(Op::Return, 0);
1357 fn_compiler.chunk.source_file = source_file;
1358 let param_slots = fn_compiler.compile_param_slots(params);
1359 let has_runtime_type_checks =
1360 CompiledFunction::has_runtime_type_checks_for_params(¶m_slots);
1361 super::ensure_chunk_addressable(&fn_compiler.chunk, "function body", self.line)?;
1362 Ok(CompiledFunction {
1363 name: String::new(),
1364 type_params: type_params.iter().map(|param| param.name.clone()).collect(),
1365 nominal_type_names: fn_compiler.nominal_type_names(),
1366 params: param_slots,
1367 default_start: TypedParam::default_start(params),
1368 chunk: Arc::new(fn_compiler.chunk),
1369 is_generator: is_gen,
1370 is_stream: false,
1371 has_rest_param: false,
1372 has_runtime_type_checks,
1373 })
1374 }
1375
1376 pub(super) fn produces_value(node: &Node) -> bool {
1378 harn_parser::node_produces_value(node)
1379 }
1380}
1381
1382impl Default for Compiler {
1383 fn default() -> Self {
1384 Self::new()
1385 }
1386}