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_imported_enum_candidates(
39 mut self,
40 candidates: impl IntoIterator<Item = String>,
41 ) -> Self {
42 self.add_imported_enum_candidates(candidates);
43 self
44 }
45
46 pub(crate) fn prepare_module_context(&mut self, program: &[SNode]) {
52 self.collect_module_enum_catalog(program);
53 if self.enum_names.insert("Result".to_string()) {
54 Self::seed_builtin_variant_owners(&mut self.enum_variant_owners);
55 }
56 Self::collect_struct_layouts(program, &mut self.struct_layouts);
57 Self::collect_interface_methods(program, &mut self.interface_methods);
58 self.collect_type_aliases(program);
59 self.collect_imported_enum_candidates(program);
60 self.collect_source_callable_names(program);
61 self.seed_module_captured_idents(program);
65 }
66
67 pub(crate) fn add_imported_enum_candidates(
68 &mut self,
69 candidates: impl IntoIterator<Item = String>,
70 ) {
71 self.imported_enum_candidates_authoritative = true;
72 self.imported_enum_candidates.extend(candidates);
73 }
74
75 pub(crate) fn compile_module_init(
80 mut self,
81 context: &[SNode],
82 init_nodes: &[SNode],
83 imported_enum_candidates: &[String],
84 ) -> Result<Chunk, CompileError> {
85 self.add_imported_enum_candidates(imported_enum_candidates.iter().cloned());
86 self.prepare_module_context(context);
87 self.compile_top_level_declarations(init_nodes)?;
88 self.chunk.emit(Op::Nil, self.line);
89 self.chunk.emit(Op::Return, self.line);
90 super::ensure_chunk_addressable(&self.chunk, "the module initialization body", self.line)?;
91 Ok(self.chunk)
92 }
93
94 pub fn with_options(options: CompilerOptions) -> Self {
95 harn_builtin_registry::install_builtin_manifest(crate::stdlib::all_builtin_manifest());
100
101 Self {
102 options,
103 chunk: Chunk::new(),
104 line: 1,
105 column: 1,
106 enum_names: std::collections::HashSet::new(),
107 enum_variant_owners: std::collections::HashMap::new(),
108 imported_enum_candidates: std::collections::HashSet::new(),
109 imported_enum_candidates_authoritative: false,
110 source_callable_names: std::collections::HashSet::new(),
111 predeclared_enum_declarations: std::collections::HashSet::new(),
112 enum_catalog_scopes: Vec::new(),
113 struct_layouts: std::collections::HashMap::new(),
114 interface_methods: std::collections::HashMap::new(),
115 loop_stack: Vec::new(),
116 handler_depth: 0,
117 finally_bodies: Vec::new(),
118 temp_counter: 0,
119 scope_depth: 0,
120 type_aliases: std::collections::HashMap::new(),
121 type_scopes: vec![std::collections::HashMap::new()],
122 monomorphic_bindings: std::collections::HashSet::new(),
123 string_constants: std::collections::HashMap::new(),
124 local_scopes: vec![std::collections::HashMap::new()],
125 module_level: true,
126 captured_bindings: std::collections::HashSet::new(),
127 }
128 }
129
130 pub(super) fn for_nested_body(options: CompilerOptions) -> Self {
134 let mut c = Self::with_options(options);
135 c.module_level = false;
136 c
137 }
138
139 pub(super) fn nested_body(&self) -> Self {
140 let mut nested = Self::for_nested_body(self.options);
141 nested.source_callable_names = self.source_callable_names.clone();
142 nested
143 }
144
145 pub(super) fn nominal_type_names(&self) -> Vec<String> {
146 let mut names: Vec<String> = self
147 .struct_layouts
148 .keys()
149 .chain(self.enum_names.iter())
150 .cloned()
151 .collect();
152 names.sort();
153 names.dedup();
154 names
155 }
156
157 pub(super) fn string_constant(&mut self, value: &str) -> u16 {
158 if let Some(idx) = self.string_constants.get(value) {
159 return *idx;
160 }
161 let owned = value.to_string();
162 let idx = self.chunk.add_constant(Constant::String(owned.clone()));
163 self.string_constants.insert(owned, idx);
164 idx
165 }
166
167 pub(super) fn owned_string_constant(&mut self, value: String) -> u16 {
168 if let Some(idx) = self.string_constants.get(value.as_str()) {
169 return *idx;
170 }
171 let idx = self.chunk.add_constant(Constant::String(value.clone()));
172 self.string_constants.insert(value, idx);
173 idx
174 }
175
176 pub(crate) fn collect_type_aliases(&mut self, program: &[SNode]) {
180 for sn in program {
181 match peel_node(sn) {
182 Node::SelectiveImport { names, .. } => {
183 for name in names {
184 self.type_aliases.entry(name.clone()).or_insert_with(|| {
185 super::TypeAliasDefinition {
186 type_params: Vec::new(),
187 body: None,
188 }
189 });
190 }
191 }
192 Node::TypeDecl {
193 name,
194 type_expr,
195 type_params,
196 is_pub: _,
197 } => {
198 self.type_aliases.insert(
199 name.clone(),
200 super::TypeAliasDefinition {
201 type_params: type_params.clone(),
202 body: Some(type_expr.clone()),
203 },
204 );
205 }
206 _ => {}
207 }
208 }
209 }
210
211 pub(crate) fn expand_alias(&self, ty: &TypeExpr) -> TypeExpr {
221 let mut visiting = std::collections::HashSet::new();
222 self.expand_alias_inner(ty, &mut visiting)
223 }
224
225 fn expand_alias_inner(
226 &self,
227 ty: &TypeExpr,
228 visiting: &mut std::collections::HashSet<String>,
229 ) -> TypeExpr {
230 match ty {
231 TypeExpr::Named(name) => {
232 if let Some(target) = self
233 .type_aliases
234 .get(name)
235 .filter(|alias| alias.type_params.is_empty() && alias.body.is_some())
236 {
237 if !visiting.insert(name.clone()) {
238 return TypeExpr::Named(name.clone());
239 }
240 let resolved = self.expand_alias_inner(target.body.as_ref().unwrap(), visiting);
241 visiting.remove(name);
242 resolved
243 } else {
244 TypeExpr::Named(name.clone())
245 }
246 }
247 TypeExpr::Union(types) => TypeExpr::Union(
248 types
249 .iter()
250 .map(|t| self.expand_alias_inner(t, visiting))
251 .collect(),
252 ),
253 TypeExpr::Intersection(types) => TypeExpr::Intersection(
254 types
255 .iter()
256 .map(|t| self.expand_alias_inner(t, visiting))
257 .collect(),
258 ),
259 TypeExpr::Shape(fields) => TypeExpr::Shape(
260 fields
261 .iter()
262 .map(|field| ShapeField {
263 type_expr: self.expand_alias_inner(&field.type_expr, visiting),
264 ..field.clone()
265 })
266 .collect(),
267 ),
268 TypeExpr::OpenShape { fields, rests } => TypeExpr::OpenShape {
269 fields: fields
270 .iter()
271 .map(|field| ShapeField {
272 type_expr: self.expand_alias_inner(&field.type_expr, visiting),
273 ..field.clone()
274 })
275 .collect(),
276 rests: rests
277 .iter()
278 .map(|r| self.expand_alias_inner(r, visiting))
279 .collect(),
280 },
281 TypeExpr::List(inner) => {
282 TypeExpr::List(Box::new(self.expand_alias_inner(inner, visiting)))
283 }
284 TypeExpr::Tuple(elements) => TypeExpr::Tuple(
285 elements
286 .iter()
287 .map(|element| self.expand_alias_inner(element, visiting))
288 .collect(),
289 ),
290 TypeExpr::Iter(inner) => {
291 TypeExpr::Iter(Box::new(self.expand_alias_inner(inner, visiting)))
292 }
293 TypeExpr::Generator(inner) => {
294 TypeExpr::Generator(Box::new(self.expand_alias_inner(inner, visiting)))
295 }
296 TypeExpr::Stream(inner) => {
297 TypeExpr::Stream(Box::new(self.expand_alias_inner(inner, visiting)))
298 }
299 TypeExpr::DictType(k, v) => TypeExpr::DictType(
300 Box::new(self.expand_alias_inner(k, visiting)),
301 Box::new(self.expand_alias_inner(v, visiting)),
302 ),
303 TypeExpr::FnType {
304 params,
305 return_type,
306 } => TypeExpr::FnType {
307 params: params
308 .iter()
309 .map(|p| self.expand_alias_inner(p, visiting))
310 .collect(),
311 return_type: Box::new(self.expand_alias_inner(return_type, visiting)),
312 },
313 TypeExpr::Applied { name, args } => {
314 let args = args
315 .iter()
316 .map(|arg| self.expand_alias_inner(arg, visiting))
317 .collect::<Vec<_>>();
318 let Some(alias) = self.type_aliases.get(name) else {
319 return TypeExpr::Applied {
320 name: name.clone(),
321 args,
322 };
323 };
324 let Some(body) = alias.body.as_ref() else {
325 return TypeExpr::Applied {
326 name: name.clone(),
327 args,
328 };
329 };
330 if alias.type_params.len() != args.len() || !visiting.insert(name.clone()) {
331 return TypeExpr::Applied {
332 name: name.clone(),
333 args,
334 };
335 }
336 let bindings = alias
337 .type_params
338 .iter()
339 .zip(args.iter().cloned())
340 .map(|(param, arg)| (param.name.clone(), arg))
341 .collect();
342 let instantiated = substitute_type_expr(body, &bindings);
343 let resolved = self.expand_alias_inner(&instantiated, visiting);
344 visiting.remove(name);
345 resolved
346 }
347 TypeExpr::Never => TypeExpr::Never,
348 TypeExpr::LitString(s) => TypeExpr::LitString(s.clone()),
349 TypeExpr::LitInt(v) => TypeExpr::LitInt(*v),
350 TypeExpr::Owned(inner) => {
351 TypeExpr::Owned(Box::new(self.expand_alias_inner(inner, visiting)))
352 }
353 }
354 }
355
356 pub fn compile_public_type_schema_initializers(
360 program: &[SNode],
361 source_file: Option<String>,
362 ) -> Result<Option<Chunk>, CompileError> {
363 let mut compiler = Compiler::new();
364 compiler.collect_type_aliases(program);
365 compiler.chunk.source_file = source_file;
366 let mut emitted = false;
367 for sn in program {
368 let Node::TypeDecl {
369 name, is_pub: true, ..
370 } = peel_node(sn)
371 else {
372 continue;
373 };
374 if compiler.emit_schema_for_alias(name) {
375 compiler.emit_define_binding(name, false);
376 emitted = true;
377 }
378 }
379 if !emitted {
380 return Ok(None);
381 }
382 compiler.chunk.emit(Op::Nil, compiler.line);
383 compiler.chunk.emit(Op::Return, compiler.line);
384 super::ensure_chunk_addressable(
385 &compiler.chunk,
386 "the module type-schema initializer",
387 compiler.line,
388 )?;
389 Ok(Some(compiler.chunk))
390 }
391
392 pub(super) fn is_schema_guard(name: &str) -> bool {
396 matches!(
397 name,
398 "schema_is"
399 | "schema_expect"
400 | "schema_parse"
401 | "schema_check"
402 | "schema_report"
403 | "is_type"
404 | "json_validate"
405 )
406 }
407
408 pub(super) fn entry_key_is(key: &SNode, keyword: &str) -> bool {
411 matches!(
412 &key.node,
413 Node::Identifier(name) | Node::StringLiteral(name) | Node::RawStringLiteral(name)
414 if name == keyword
415 )
416 }
417
418 pub fn compile(mut self, program: &[SNode]) -> Result<Chunk, CompileError> {
421 self.prepare_module_context(program);
424
425 for sn in program {
426 match &sn.node {
427 Node::ImportDecl { .. }
428 | Node::SelectiveImport { .. }
429 | Node::NamespaceImport { .. } => {
430 self.compile_node(sn)?;
431 }
432 _ => {}
433 }
434 }
435 let main = program
436 .iter()
437 .find(|sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == "default"))
438 .or_else(|| {
439 program
440 .iter()
441 .find(|sn| matches!(peel_node(sn), Node::Pipeline { .. }))
442 });
443
444 let mut pipeline_emits_value = false;
448 if let Some(sn) = main {
449 self.compile_top_level_declarations(program)?;
450 if let Node::Pipeline {
451 params,
452 body,
453 extends,
454 ..
455 } = peel_node(sn)
456 {
457 self.compile_with_pipeline_captures(
458 program,
459 body,
460 extends.as_deref(),
461 |compiler| {
462 let saved = std::mem::replace(&mut compiler.module_level, false);
463 if let Some(harness) = params.first().filter(|param| {
464 matches!(
465 param.type_expr.as_ref(),
466 Some(TypeExpr::Named(name)) if name == "Harness"
467 )
468 }) {
469 compiler.chunk.emit(Op::RootHarness, compiler.line);
470 compiler.emit_define_binding(&harness.name, false);
471 }
472 if let Some(parent_name) = extends {
473 compiler.compile_parent_pipeline(program, parent_name)?;
474 }
475 let result = compiler.compile_block(body);
476 compiler.module_level = saved;
477 result
478 },
479 )?;
480 pipeline_emits_value = true;
481 }
482 } else {
483 let top_level: Vec<&SNode> = program
485 .iter()
486 .filter(|sn| {
487 !matches!(
488 &sn.node,
489 Node::ImportDecl { .. }
490 | Node::SelectiveImport { .. }
491 | Node::NamespaceImport { .. }
492 )
493 })
494 .collect();
495 for sn in &top_level {
496 self.compile_discarded_stmt(sn)?;
497 }
498 if Self::has_top_level_fn_main(program) {
503 self.chunk.emit(Op::RootHarness, self.line);
504 self.emit_named_call("main", 1);
505 pipeline_emits_value = true;
506 }
507 }
508
509 self.drain_finallys_to_floor(0)?;
510 if !pipeline_emits_value {
511 self.chunk.emit(Op::Nil, self.line);
512 }
513 self.chunk.emit(Op::Return, self.line);
514 super::ensure_chunk_addressable(&self.chunk, "the program body", self.line)?;
515 Ok(self.chunk)
516 }
517
518 fn has_top_level_fn_main(program: &[SNode]) -> bool {
522 program
523 .iter()
524 .any(|sn| matches!(peel_node(sn), Node::FnDecl { name, .. } if name == "main"))
525 }
526
527 pub fn compile_named(
529 self,
530 program: &[SNode],
531 pipeline_name: &str,
532 ) -> Result<Chunk, CompileError> {
533 self.compile_named_inner(program, pipeline_name)
534 }
535
536 fn compile_named_inner(
537 mut self,
538 program: &[SNode],
539 pipeline_name: &str,
540 ) -> Result<Chunk, CompileError> {
541 self.prepare_module_context(program);
542
543 for sn in program {
544 if matches!(
545 &sn.node,
546 Node::ImportDecl { .. }
547 | Node::SelectiveImport { .. }
548 | Node::NamespaceImport { .. }
549 ) {
550 self.compile_node(sn)?;
551 }
552 }
553 let target = program.iter().find(
554 |sn| matches!(peel_node(sn), Node::Pipeline { name, .. } if name == pipeline_name),
555 );
556
557 if let Some(sn) = target {
558 self.compile_top_level_declarations(program)?;
559 if let Node::Pipeline {
560 body,
561 extends,
562 params,
563 ..
564 } = peel_node(sn)
565 {
566 self.compile_with_pipeline_captures(
567 program,
568 body,
569 extends.as_deref(),
570 |compiler| {
571 let saved = std::mem::replace(&mut compiler.module_level, false);
572 if let Some(harness) = params.first().filter(|param| {
573 matches!(
574 param.type_expr.as_ref(),
575 Some(TypeExpr::Named(name)) if name == "Harness"
576 )
577 }) {
578 compiler.chunk.emit(Op::RootHarness, compiler.line);
579 compiler.emit_define_binding(&harness.name, false);
580 }
581 if let Some(parent_name) = extends {
582 compiler.compile_parent_pipeline(program, parent_name)?;
583 }
584 let result = compiler.compile_block(body);
585 compiler.module_level = saved;
586 result
587 },
588 )?;
589 }
590 }
591
592 self.drain_finallys_to_floor(0)?;
593 self.chunk.emit(Op::Nil, self.line);
594 self.chunk.emit(Op::Return, self.line);
595 super::ensure_chunk_addressable(&self.chunk, "the pipeline body", self.line)?;
596 Ok(self.chunk)
597 }
598
599 pub(super) fn emit_default_preamble(
604 &mut self,
605 params: &[TypedParam],
606 ) -> Result<(), CompileError> {
607 for (i, param) in params.iter().enumerate() {
608 if let Some(default_expr) = ¶m.default_value {
609 self.chunk.emit(Op::GetArgc, self.line);
610 let threshold_idx = self.chunk.add_constant(Constant::Int((i + 1) as i64));
611 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
612 self.chunk.emit(Op::GreaterEqual, self.line);
613 let skip_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
614 self.chunk.emit(Op::Pop, self.line);
616 let masked = self.mask_param_names(¶ms[i..]);
625 let result = self.compile_node(default_expr);
626 self.restore_param_names(masked);
627 result?;
628 self.emit_init_or_define_binding(¶m.name, false);
629 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
630 self.chunk.patch_jump(skip_jump);
631 self.chunk.emit(Op::Pop, self.line);
632 self.chunk.patch_jump(end_jump);
633 }
634 }
635 Ok(())
636 }
637
638 pub(super) fn emit_type_checks(&mut self, params: &[TypedParam]) {
645 for (param_index, param) in params.iter().enumerate() {
646 if let Some(type_expr) = ¶m.type_expr {
647 let check_type = if param.rest {
648 harn_parser::TypeExpr::List(Box::new(type_expr.clone()))
649 } else {
650 type_expr.clone()
651 };
652
653 if let harn_parser::TypeExpr::Named(name) = &check_type {
654 if let Some(methods) = self.interface_methods.get(name).cloned() {
655 let fn_idx = self.string_constant("__assert_interface");
656 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
657 self.emit_get_binding(¶m.name);
658 let name_idx = self.string_constant(¶m.name);
659 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
660 let iface_idx = self.string_constant(name);
661 self.chunk.emit_u16(Op::Constant, iface_idx, self.line);
662 let methods_str = methods.join(",");
663 let methods_idx = self.owned_string_constant(methods_str);
664 self.chunk.emit_u16(Op::Constant, methods_idx, self.line);
665 self.chunk.emit_u8(Op::Call, 4, self.line);
666 self.chunk.emit(Op::Pop, self.line);
667 continue;
668 }
669 }
670
671 if param.default_value.is_some() {
672 if let Some(schema) = Self::type_expr_to_schema_value(&check_type) {
673 self.emit_default_param_schema_check(param_index, param, &schema);
674 }
675 }
676 }
677 }
678 }
679
680 fn emit_default_param_schema_check(
681 &mut self,
682 param_index: usize,
683 param: &TypedParam,
684 schema: &VmValue,
685 ) {
686 self.chunk.emit(Op::GetArgc, self.line);
687 let threshold_idx = self
688 .chunk
689 .add_constant(Constant::Int((param_index + 1) as i64));
690 self.chunk.emit_u16(Op::Constant, threshold_idx, self.line);
691 self.chunk.emit(Op::GreaterEqual, self.line);
692 let supplied_jump = self.chunk.emit_jump(Op::JumpIfTrue, self.line);
693 self.chunk.emit(Op::Pop, self.line);
694 self.emit_schema_assert_call(param, schema);
695 let end_jump = self.chunk.emit_jump(Op::Jump, self.line);
696 self.chunk.patch_jump(supplied_jump);
697 self.chunk.emit(Op::Pop, self.line);
698 self.chunk.patch_jump(end_jump);
699 }
700
701 fn emit_schema_assert_call(&mut self, param: &TypedParam, schema: &VmValue) {
702 let fn_idx = self.string_constant("__assert_schema");
703 self.chunk.emit_u16(Op::Constant, fn_idx, self.line);
704 self.emit_get_binding(¶m.name);
705 let name_idx = self.string_constant(¶m.name);
706 self.chunk.emit_u16(Op::Constant, name_idx, self.line);
707 self.emit_vm_value_literal(schema);
708 self.chunk.emit_u8(Op::Call, 3, self.line);
709 self.chunk.emit(Op::Pop, self.line);
710 }
711
712 pub(crate) fn type_expr_to_schema_value(type_expr: &harn_parser::TypeExpr) -> Option<VmValue> {
713 match type_expr {
714 harn_parser::TypeExpr::Named(name) => match name.as_str() {
715 "any" | "unknown" => Some(VmValue::dict(BTreeMap::<String, VmValue>::new())),
716 "int" | "float" | "string" | "bool" | "list" | "dict" | "set" | "nil"
717 | "closure" | "bytes" => Some(VmValue::dict(BTreeMap::from([(
718 "type".to_string(),
719 VmValue::String(arcstr::ArcStr::from(name.as_str())),
720 )]))),
721 _ => None,
722 },
723 harn_parser::TypeExpr::Shape(fields) => {
724 let mut properties = BTreeMap::new();
725 let mut required = Vec::new();
726 for field in fields {
727 let mut field_schema = Self::type_expr_to_schema_value(&field.type_expr)?;
728 if field.optional {
729 field_schema = VmValue::dict(BTreeMap::from([(
730 "union".to_string(),
731 VmValue::List(std::sync::Arc::new(vec![
732 field_schema,
733 VmValue::dict(BTreeMap::from([(
734 "type".to_string(),
735 VmValue::String(arcstr::ArcStr::from("nil")),
736 )])),
737 ])),
738 )]));
739 }
740 properties.insert(field.name.clone(), field_schema);
741 if !field.optional {
742 required.push(VmValue::String(arcstr::ArcStr::from(field.name.as_str())));
743 }
744 }
745 let mut out = BTreeMap::new();
746 out.put_str("type", "dict");
747 out.insert("properties".to_string(), VmValue::dict(properties));
748 if !required.is_empty() {
749 out.insert(
750 "required".to_string(),
751 VmValue::List(std::sync::Arc::new(required)),
752 );
753 }
754 Some(VmValue::dict(out))
755 }
756 harn_parser::TypeExpr::OpenShape { .. } => None,
757 harn_parser::TypeExpr::List(inner) => {
758 let mut out = BTreeMap::new();
759 out.put_str("type", "list");
760 let item_schema = Self::type_expr_to_schema_value(inner)?;
761 out.insert("items".to_string(), item_schema);
762 Some(VmValue::dict(out))
763 }
764 harn_parser::TypeExpr::Tuple(_) => None,
770 harn_parser::TypeExpr::DictType(key, value) => {
771 let mut out = BTreeMap::new();
772 out.put_str("type", "dict");
773 if matches!(key.as_ref(), harn_parser::TypeExpr::Named(name) if name == "string") {
774 let value_schema = Self::type_expr_to_schema_value(value)?;
775 out.insert("additional_properties".to_string(), value_schema);
776 }
777 Some(VmValue::dict(out))
778 }
779 harn_parser::TypeExpr::Union(members) => {
780 if !members.is_empty()
785 && members
786 .iter()
787 .all(|m| matches!(m, harn_parser::TypeExpr::LitString(_)))
788 {
789 let values = members
790 .iter()
791 .map(|m| match m {
792 harn_parser::TypeExpr::LitString(s) => {
793 VmValue::String(arcstr::ArcStr::from(s.as_str()))
794 }
795 _ => unreachable!(),
796 })
797 .collect::<Vec<_>>();
798 return Some(VmValue::dict(BTreeMap::from([
799 (
800 "type".to_string(),
801 VmValue::String(arcstr::ArcStr::from("string")),
802 ),
803 (
804 "enum".to_string(),
805 VmValue::List(std::sync::Arc::new(values)),
806 ),
807 ])));
808 }
809 if !members.is_empty()
810 && members
811 .iter()
812 .all(|m| matches!(m, harn_parser::TypeExpr::LitInt(_)))
813 {
814 let values = members
815 .iter()
816 .map(|m| match m {
817 harn_parser::TypeExpr::LitInt(v) => VmValue::Int(*v),
818 _ => unreachable!(),
819 })
820 .collect::<Vec<_>>();
821 return Some(VmValue::dict(BTreeMap::from([
822 (
823 "type".to_string(),
824 VmValue::String(arcstr::ArcStr::from("int")),
825 ),
826 (
827 "enum".to_string(),
828 VmValue::List(std::sync::Arc::new(values)),
829 ),
830 ])));
831 }
832 let branches = members
833 .iter()
834 .map(Self::type_expr_to_schema_value)
835 .collect::<Option<Vec<_>>>()?;
836 if branches.is_empty() {
837 None
838 } else {
839 Some(VmValue::dict(BTreeMap::from([(
840 "union".to_string(),
841 VmValue::List(std::sync::Arc::new(branches)),
842 )])))
843 }
844 }
845 harn_parser::TypeExpr::Intersection(members) => {
846 let branches = members
850 .iter()
851 .map(Self::type_expr_to_schema_value)
852 .collect::<Option<Vec<_>>>()?;
853 if branches.is_empty() {
854 None
855 } else {
856 Some(VmValue::dict(BTreeMap::from([(
857 "all_of".to_string(),
858 VmValue::List(std::sync::Arc::new(branches)),
859 )])))
860 }
861 }
862 harn_parser::TypeExpr::FnType { .. } => Some(VmValue::dict(BTreeMap::from([(
863 "type".to_string(),
864 VmValue::String(arcstr::ArcStr::from("closure")),
865 )]))),
866 harn_parser::TypeExpr::Applied { .. } => None,
867 harn_parser::TypeExpr::Iter(_)
868 | harn_parser::TypeExpr::Generator(_)
869 | harn_parser::TypeExpr::Stream(_) => None,
870 harn_parser::TypeExpr::Never => None,
871 harn_parser::TypeExpr::LitString(s) => Some(VmValue::dict(BTreeMap::from([
872 (
873 "type".to_string(),
874 VmValue::String(arcstr::ArcStr::from("string")),
875 ),
876 (
877 "const".to_string(),
878 VmValue::String(arcstr::ArcStr::from(s.as_str())),
879 ),
880 ]))),
881 harn_parser::TypeExpr::LitInt(v) => Some(VmValue::dict(BTreeMap::from([
882 (
883 "type".to_string(),
884 VmValue::String(arcstr::ArcStr::from("int")),
885 ),
886 ("const".to_string(), VmValue::Int(*v)),
887 ]))),
888 harn_parser::TypeExpr::Owned(inner) => Self::type_expr_to_schema_value(inner),
889 }
890 }
891
892 pub(super) fn emit_vm_value_literal(&mut self, value: &VmValue) {
893 match value {
894 VmValue::String(text) => {
895 let idx = self.string_constant(text);
896 self.chunk.emit_u16(Op::Constant, idx, self.line);
897 }
898 VmValue::Int(number) => {
899 let idx = self.chunk.add_constant(Constant::Int(*number));
900 self.chunk.emit_u16(Op::Constant, idx, self.line);
901 }
902 VmValue::Float(number) => {
903 let idx = self.chunk.add_constant(Constant::Float(*number));
904 self.chunk.emit_u16(Op::Constant, idx, self.line);
905 }
906 VmValue::Bool(value) => {
907 let idx = self.chunk.add_constant(Constant::Bool(*value));
908 self.chunk.emit_u16(Op::Constant, idx, self.line);
909 }
910 VmValue::Nil => self.chunk.emit(Op::Nil, self.line),
911 VmValue::List(items) => {
912 for item in items.iter() {
913 self.emit_vm_value_literal(item);
914 }
915 self.chunk
916 .emit_u16(Op::BuildList, items.len() as u16, self.line);
917 }
918 VmValue::Dict(entries) => {
919 for (key, item) in entries.iter() {
920 let key_idx = self.string_constant(key);
921 self.chunk.emit_u16(Op::Constant, key_idx, self.line);
922 self.emit_vm_value_literal(item);
923 }
924 self.chunk
925 .emit_u16(Op::BuildDict, entries.len() as u16, self.line);
926 }
927 _ => {}
928 }
929 }
930
931 pub(super) fn emit_type_name_extra(&mut self, type_name_idx: u16) {
933 let hi = (type_name_idx >> 8) as u8;
934 let lo = type_name_idx as u8;
935 self.chunk.code.push(hi);
936 self.chunk.code.push(lo);
937 self.chunk.lines.push(self.line);
938 self.chunk.columns.push(self.column);
939 self.chunk.lines.push(self.line);
940 self.chunk.columns.push(self.column);
941 }
942
943 pub(super) fn compile_try_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
945 if body.is_empty() {
946 self.chunk.emit(Op::Nil, self.line);
947 } else {
948 self.compile_scoped_block(body)?;
949 }
950 Ok(())
951 }
952
953 pub(super) fn compile_catch_binding(
955 &mut self,
956 error_var: &Option<String>,
957 ) -> Result<(), CompileError> {
958 if let Some(var_name) = error_var {
959 self.emit_define_binding(var_name, false);
960 } else {
961 self.chunk.emit(Op::Pop, self.line);
962 }
963 Ok(())
964 }
965
966 pub(super) fn compile_finally_inline(
973 &mut self,
974 finally_body: &[SNode],
975 ) -> Result<(), CompileError> {
976 if !finally_body.is_empty() {
977 self.compile_scoped_block(finally_body)?;
978 self.chunk.emit(Op::Pop, self.line);
979 }
980 Ok(())
981 }
982
983 pub(super) fn has_pending_finally_until_barrier(&self) -> bool {
987 self.finally_bodies
988 .iter()
989 .rev()
990 .take_while(|entry| !matches!(entry, FinallyEntry::CatchBarrier))
991 .any(|entry| matches!(entry, FinallyEntry::Finally(_)))
992 }
993
994 pub(super) fn has_pending_finally(&self) -> bool {
996 self.finally_bodies
997 .iter()
998 .any(|e| matches!(e, FinallyEntry::Finally(_)))
999 }
1000
1001 pub(super) fn compile_plain_rethrow(&mut self) -> Result<(), CompileError> {
1010 self.temp_counter += 1;
1011 let temp_name = format!("__finally_err_{}__", self.temp_counter);
1012 self.emit_define_binding(&temp_name, true);
1013 self.emit_get_binding(&temp_name);
1014 self.chunk.emit(Op::Throw, self.line);
1015 Ok(())
1016 }
1017
1018 pub(super) fn declare_param_slots(&mut self, params: &[TypedParam]) {
1019 for param in params {
1020 self.define_local_slot(¶m.name, false);
1021 }
1022 }
1023
1024 fn mask_param_names(&mut self, params: &[TypedParam]) -> Vec<(String, super::LocalBinding)> {
1030 let mut removed = Vec::new();
1031 if let Some(scope) = self.local_scopes.last_mut() {
1032 for param in params {
1033 if let Some(binding) = scope.remove(¶m.name) {
1034 removed.push((param.name.clone(), binding));
1035 }
1036 }
1037 }
1038 removed
1039 }
1040
1041 fn restore_param_names(&mut self, removed: Vec<(String, super::LocalBinding)>) {
1043 if let Some(scope) = self.local_scopes.last_mut() {
1044 for (name, binding) in removed {
1045 scope.insert(name, binding);
1046 }
1047 }
1048 }
1049
1050 pub(super) fn seed_captured_idents(&mut self, body: &[SNode]) {
1055 let match_patterns = self.lexical_match_pattern_catalog();
1056 self.captured_bindings =
1057 harn_parser::lexical::captured_bindings_in_nested_callables(body, &match_patterns);
1058 }
1059
1060 fn seed_module_captured_idents(&mut self, body: &[SNode]) {
1061 let match_patterns = self.lexical_match_pattern_catalog();
1062 self.captured_bindings =
1063 harn_parser::lexical::captured_bindings_in_compiled_module(body, &match_patterns);
1064 }
1065
1066 pub(super) fn lexical_match_pattern_catalog(
1067 &self,
1068 ) -> harn_parser::lexical::MatchPatternCatalog {
1069 if self.imported_enum_candidates.is_empty() {
1070 return harn_parser::lexical::MatchPatternCatalog::new(
1071 &self.enum_names,
1072 &self.enum_variant_owners,
1073 );
1074 }
1075 let mut enum_names = self.enum_names.clone();
1076 enum_names.extend(self.imported_enum_candidates.iter().cloned());
1077 harn_parser::lexical::MatchPatternCatalog::new(&enum_names, &self.enum_variant_owners)
1078 }
1079
1080 pub(super) fn begin_scope(&mut self) {
1081 self.chunk.emit(Op::PushScope, self.line);
1082 self.scope_depth += 1;
1083 let enum_catalog = self.enum_catalog_snapshot();
1084 self.enum_catalog_scopes.push(enum_catalog);
1085 self.type_scopes.push(std::collections::HashMap::new());
1086 self.local_scopes.push(std::collections::HashMap::new());
1087 }
1088
1089 pub(super) fn end_scope(&mut self) {
1090 if self.scope_depth > 0 {
1091 self.chunk.emit(Op::PopScope, self.line);
1092 self.scope_depth -= 1;
1093 if let Some(snapshot) = self.enum_catalog_scopes.pop() {
1094 self.restore_enum_catalog(snapshot);
1095 }
1096 self.type_scopes.pop();
1097 self.local_scopes.pop();
1098 }
1099 }
1100
1101 pub(super) fn emit_scope_unwind_to(&mut self, target_depth: usize) {
1104 for _ in target_depth..self.scope_depth {
1105 self.chunk.emit(Op::PopScope, self.line);
1106 }
1107 }
1108
1109 pub(super) fn compile_scoped_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
1110 self.begin_scope();
1111 let finally_floor = self.finally_bodies.len();
1112 if stmts.is_empty() {
1113 self.chunk.emit(Op::Nil, self.line);
1114 } else {
1115 self.compile_block(stmts)?;
1116 }
1117 self.drain_finallys_to_floor(finally_floor)?;
1118 self.end_scope();
1119 Ok(())
1120 }
1121
1122 pub(super) fn compile_scoped_statements(
1123 &mut self,
1124 stmts: &[SNode],
1125 ) -> Result<(), CompileError> {
1126 self.begin_scope();
1127 self.record_monomorphic_var_bindings(stmts);
1128 let finally_floor = self.finally_bodies.len();
1129 for sn in stmts {
1130 self.compile_discarded_stmt(sn)?;
1131 }
1132 self.drain_finallys_to_floor(finally_floor)?;
1133 self.end_scope();
1134 Ok(())
1135 }
1136
1137 pub(super) fn drain_finallys_to_floor(&mut self, floor: usize) -> Result<(), CompileError> {
1142 while self.finally_bodies.len() > floor {
1143 let entry = self.finally_bodies.pop().expect("non-empty by guard");
1144 if let FinallyEntry::Finally(body) = entry {
1145 self.compile_finally_inline(&body)?;
1146 }
1147 }
1148 Ok(())
1149 }
1150
1151 pub(super) fn run_pending_finallys_for_transfer(
1164 &mut self,
1165 floor: usize,
1166 ) -> Result<(), CompileError> {
1167 if self.finally_bodies.len() <= floor {
1168 return Ok(());
1169 }
1170 let saved = self.finally_bodies[floor..].to_vec();
1171 let result = self.drain_finallys_to_floor(floor);
1172 self.finally_bodies.extend(saved);
1173 result
1174 }
1175
1176 pub(super) fn run_pending_finallys_until_barrier(&mut self) -> Result<(), CompileError> {
1181 let floor = self
1182 .finally_bodies
1183 .iter()
1184 .rposition(|e| matches!(e, FinallyEntry::CatchBarrier))
1185 .map(|i| i + 1)
1186 .unwrap_or(0);
1187 self.run_pending_finallys_for_transfer(floor)
1188 }
1189
1190 pub(super) fn maybe_register_owned_drop(
1195 &mut self,
1196 pattern: &harn_parser::BindingPattern,
1197 type_ann: Option<&TypeExpr>,
1198 span: harn_lexer::Span,
1199 ) {
1200 let Some(ty) = type_ann else {
1206 return;
1207 };
1208 if !matches!(ty, TypeExpr::Owned(_)) {
1209 return;
1210 }
1211 let harn_parser::BindingPattern::Identifier(name) = pattern else {
1212 return;
1213 };
1214 if harn_parser::is_discard_name(name) {
1215 return;
1216 }
1217 let call = harn_parser::spanned(
1218 Node::FunctionCall {
1219 name: "drop".to_string(),
1220 args: vec![harn_parser::spanned(Node::Identifier(name.clone()), span)],
1221 type_args: Vec::new(),
1222 },
1223 span,
1224 );
1225 self.finally_bodies.push(FinallyEntry::Finally(vec![call]));
1226 }
1227
1228 pub(super) fn compile_discarded_stmt(&mut self, sn: &SNode) -> Result<(), CompileError> {
1244 #[cfg(debug_assertions)]
1245 let probe = self.chunk.balance_probe();
1246 self.compile_node(sn)?;
1247 #[allow(unused_mut)]
1248 let mut produces = Self::produces_value(&sn.node);
1249 #[cfg(test)]
1253 if let Some(forced) = FORCE_DISCARDED_PRODUCES_VALUE.with(std::cell::Cell::get) {
1254 produces = forced;
1255 }
1256 #[cfg(debug_assertions)]
1257 if let Some(delta) = self.chunk.balance_delta_since(probe) {
1258 let expected = i32::from(produces);
1259 debug_assert_eq!(
1260 delta, expected,
1261 "operand-stack imbalance at line {}: produces_value={produces} but the \
1262 node's emitted bytecode netted {delta} (expected {expected}). A \
1263 `produces_value` arm is out of sync with this node's codegen — see #2622.\n\
1264 node: {:?}",
1265 self.line, sn.node,
1266 );
1267 }
1268 if produces {
1269 self.chunk.emit(Op::Pop, self.line);
1270 }
1271 Ok(())
1272 }
1273
1274 pub(super) fn compile_block(&mut self, stmts: &[SNode]) -> Result<(), CompileError> {
1275 self.record_monomorphic_var_bindings(stmts);
1276 for (i, snode) in stmts.iter().enumerate() {
1277 if i == stmts.len() - 1 {
1278 self.compile_node(snode)?;
1282 if !Self::produces_value(&snode.node) {
1283 self.chunk.emit(Op::Nil, self.line);
1284 }
1285 } else {
1286 self.compile_discarded_stmt(snode)?;
1287 }
1288 }
1289 Ok(())
1290 }
1291
1292 pub(super) fn compile_match_body(&mut self, body: &[SNode]) -> Result<(), CompileError> {
1294 self.begin_scope();
1295 let finally_floor = self.finally_bodies.len();
1296 if body.is_empty() {
1297 self.chunk.emit(Op::Nil, self.line);
1298 } else {
1299 self.compile_block(body)?;
1300 if !Self::produces_value(&body.last().unwrap().node) {
1301 self.chunk.emit(Op::Nil, self.line);
1302 }
1303 }
1304 self.drain_finallys_to_floor(finally_floor)?;
1305 self.end_scope();
1306 Ok(())
1307 }
1308
1309 pub(super) fn emit_compound_op(&mut self, op: &str) -> Result<(), CompileError> {
1311 match op {
1312 "+" => self.chunk.emit(Op::Add, self.line),
1313 "-" => self.chunk.emit(Op::Sub, self.line),
1314 "*" => self.chunk.emit(Op::Mul, self.line),
1315 "/" => self.chunk.emit(Op::Div, self.line),
1316 "%" => self.chunk.emit(Op::Mod, self.line),
1317 _ => {
1318 return Err(CompileError {
1319 message: format!("Unknown compound operator: {op}"),
1320 line: self.line,
1321 })
1322 }
1323 }
1324 Ok(())
1325 }
1326
1327 pub(super) fn compile_top_level_declarations(
1328 &mut self,
1329 program: &[SNode],
1330 ) -> Result<(), CompileError> {
1331 for sn in program {
1343 if !harn_parser::lexical::is_deferred_module_declaration(sn) {
1344 self.compile_discarded_stmt(sn)?;
1345 }
1346 }
1347 for sn in program {
1353 let inner_kind = match &sn.node {
1354 Node::AttributedDecl { inner, .. } => &inner.node,
1355 other => other,
1356 };
1357 match inner_kind {
1358 Node::EvalPackDecl {
1359 binding_name,
1360 pack_id,
1361 fields,
1362 body,
1363 summarize,
1364 ..
1365 } => {
1366 self.compile_eval_pack_decl(
1367 binding_name,
1368 pack_id,
1369 fields,
1370 body,
1371 summarize,
1372 false,
1373 )?;
1374 }
1375 Node::FnDecl { .. }
1376 | Node::ToolDecl { .. }
1377 | Node::SkillDecl { .. }
1378 | Node::ImplBlock { .. }
1379 | Node::StructDecl { .. }
1380 | Node::EnumDecl { .. }
1381 | Node::InterfaceDecl { .. } => {
1382 self.compile_node(sn)?;
1383 }
1384 Node::TypeDecl { .. } => {}
1385 _ => {}
1386 }
1387 }
1388 Ok(())
1389 }
1390
1391 pub fn compile_fn_body(
1404 &mut self,
1405 type_params: &[harn_parser::TypeParam],
1406 params: &[TypedParam],
1407 body: &[SNode],
1408 source_file: Option<String>,
1409 ) -> Result<CompiledFunction, CompileError> {
1410 let mut fn_compiler = self.nested_body();
1411 fn_compiler.enum_names = self.enum_names.clone();
1412 fn_compiler.enum_variant_owners = self.enum_variant_owners.clone();
1413 fn_compiler.imported_enum_candidates = self.imported_enum_candidates.clone();
1414 fn_compiler.imported_enum_candidates_authoritative =
1415 self.imported_enum_candidates_authoritative;
1416 fn_compiler.interface_methods = self.interface_methods.clone();
1417 fn_compiler.type_aliases = self.type_aliases.clone();
1418 fn_compiler.struct_layouts = self.struct_layouts.clone();
1419 fn_compiler.declare_param_slots(params);
1420 fn_compiler.record_param_types(params);
1421 fn_compiler.emit_default_preamble(params)?;
1422 fn_compiler.emit_type_checks(params);
1423 let is_gen = body_contains_yield(body);
1424 fn_compiler.seed_captured_idents(body);
1425 fn_compiler.compile_block(body)?;
1426 fn_compiler.chunk.emit(Op::Nil, 0);
1427 fn_compiler.chunk.emit(Op::Return, 0);
1428 fn_compiler.chunk.source_file = source_file;
1429 let param_slots = fn_compiler.compile_param_slots(params);
1430 let has_runtime_type_checks =
1431 CompiledFunction::has_runtime_type_checks_for_params(¶m_slots);
1432 super::ensure_chunk_addressable(&fn_compiler.chunk, "function body", self.line)?;
1433 Ok(CompiledFunction {
1434 name: String::new(),
1435 type_params: type_params.iter().map(|param| param.name.clone()).collect(),
1436 nominal_type_names: fn_compiler.nominal_type_names(),
1437 params: param_slots,
1438 default_start: TypedParam::default_start(params),
1439 chunk: Arc::new(fn_compiler.chunk),
1440 is_generator: is_gen,
1441 is_stream: false,
1442 has_rest_param: false,
1443 has_runtime_type_checks,
1444 })
1445 }
1446
1447 pub(super) fn produces_value(node: &Node) -> bool {
1449 harn_parser::node_produces_value(node)
1450 }
1451}
1452
1453impl Default for Compiler {
1454 fn default() -> Self {
1455 Self::new()
1456 }
1457}