1pub(super) mod debuginfo;
2pub(crate) mod error;
3mod product;
4
5use alloc::{
6 boxed::Box,
7 collections::{BTreeMap, BTreeSet},
8 string::ToString,
9 sync::Arc,
10 vec::Vec,
11};
12
13use debuginfo::DebugInfoSections;
14use miden_assembly_syntax::{
15 ExportedTypeUse, MAX_REPEAT_COUNT, Parse, SemanticAnalysisError,
16 ast::{
17 self, AttributeSet, Ident, InvocationTarget, InvokeKind, ItemIndex, ModuleKind,
18 SymbolResolution, Visibility, types::FunctionType,
19 },
20 debuginfo::{DefaultSourceManager, SourceManager, SourceSpan, Spanned},
21 diagnostics::{IntoDiagnostic, RelatedLabel, Report},
22 module::ItemInfo,
23};
24use miden_core::{
25 WORD_SIZE, Word,
26 mast::{MastNodeExt, MastNodeId},
27 operations::{AssemblyOp, Operation},
28 program::Kernel,
29 serde::Serializable,
30};
31use miden_mast_package::{
32 ConstantExport, Package, PackageDebugInfoError, PackageExport, PackageId, PackageModule,
33 PackageSubmodule, ProcedureExport, Section, SectionId, TypeExport,
34 debug_info::DebugSourceNodeId,
35};
36use miden_project::{Linkage, TargetType};
37
38use self::{error::AssemblerError, product::AssemblyProduct};
39use crate::{
40 GlobalItemIndex, ModuleIndex, Procedure, ProcedureContext,
41 ast::Path,
42 basic_block_builder::BasicBlockBuilder,
43 fmp::{fmp_end_frame_sequence, fmp_initialization_sequence, fmp_start_frame_sequence},
44 linker::{
45 Import, LinkLibrary, Linker, LinkerError, SymbolItem, SymbolResolutionContext,
46 SymbolResolver,
47 },
48 mast_forest_builder::{
49 MastForestBuilder, MastNodeRef, SourceDebugGraph, SourceNodeId, SourceNodeRef,
50 StaticLibrary,
51 },
52};
53
54pub(crate) const MAX_CONTROL_FLOW_NESTING: usize = 256;
59
60pub(crate) const MAX_PROC_LOCALS: u16 = (u16::MAX / WORD_SIZE as u16) * WORD_SIZE as u16;
67
68#[derive(Debug)]
69enum PendingPackageExport {
70 Procedure(PendingProcedureExport),
71 Constant(ConstantExport),
72 Type(TypeExport),
73}
74
75#[derive(Debug)]
76struct PendingProcedureExport {
77 node_ref: MastNodeRef,
78 source_ref: Option<SourceNodeRef>,
79 digest: Word,
80 path: Arc<Path>,
81 signature: Option<FunctionType>,
82 attributes: AttributeSet,
83}
84
85impl PendingPackageExport {
86 fn into_package_export(
87 self,
88 node_id_by_ref: &BTreeMap<MastNodeRef, MastNodeId>,
89 source_id_by_ref: &BTreeMap<SourceNodeRef, SourceNodeId>,
90 ) -> Result<PackageExport, Report> {
91 match self {
92 Self::Procedure(export) => export.into_package_export(node_id_by_ref, source_id_by_ref),
93 Self::Constant(export) => Ok(PackageExport::Constant(export)),
94 Self::Type(export) => Ok(PackageExport::Type(export)),
95 }
96 }
97}
98
99impl PendingProcedureExport {
100 fn into_package_export(
101 self,
102 node_id_by_ref: &BTreeMap<MastNodeRef, MastNodeId>,
103 source_id_by_ref: &BTreeMap<SourceNodeRef, SourceNodeId>,
104 ) -> Result<PackageExport, Report> {
105 let node = node_id_by_ref.get(&self.node_ref).copied().ok_or_else(|| {
106 Report::msg(format!("procedure export ref {} was not finalized", self.node_ref))
107 })?;
108 let source_node = self
109 .source_ref
110 .and_then(|source_ref| source_id_by_ref.get(&source_ref).copied())
111 .map(|source_id| DebugSourceNodeId::from(u32::from(source_id)));
112 Ok(PackageExport::Procedure(ProcedureExport {
113 digest: self.digest,
114 path: self.path,
115 node: Some(node),
116 source_node,
117 signature: self.signature,
118 attributes: self.attributes,
119 }))
120 }
121}
122
123#[derive(Clone)]
172pub struct Assembler {
173 source_manager: Arc<dyn SourceManager>,
175 linker: Box<Linker>,
177 pub(super) debug_info: DebugInfoSections,
179 warnings_as_errors: bool,
181 pub(super) emit_debug_info: bool,
183 pub(super) trim_paths: bool,
185}
186
187impl Default for Assembler {
188 fn default() -> Self {
189 let source_manager = Arc::new(DefaultSourceManager::default());
190 let linker = Box::new(Linker::new(source_manager.clone()));
191 Self {
192 source_manager,
193 linker,
194 debug_info: Default::default(),
195 warnings_as_errors: false,
196 emit_debug_info: true,
197 trim_paths: false,
198 }
199 }
200}
201
202impl Assembler {
205 pub fn new(source_manager: Arc<dyn SourceManager>) -> Self {
207 let linker = Box::new(Linker::new(source_manager.clone()));
208 Self {
209 source_manager,
210 linker,
211 debug_info: Default::default(),
212 warnings_as_errors: false,
213 emit_debug_info: true,
214 trim_paths: false,
215 }
216 }
217
218 pub fn with_kernel(
220 source_manager: Arc<dyn SourceManager>,
221 kernel: Arc<Package>,
222 ) -> Result<Self, Report> {
223 let linker = Box::new(Linker::with_kernel(source_manager.clone(), kernel)?);
224 Ok(Self {
225 source_manager,
226 linker,
227 ..Default::default()
228 })
229 }
230
231 pub fn with_warnings_as_errors(mut self, yes: bool) -> Self {
235 self.warnings_as_errors = yes;
236 self
237 }
238
239 #[cfg(feature = "std")]
240 pub(crate) fn with_emit_debug_info(mut self, yes: bool) -> Self {
241 self.emit_debug_info = yes;
242 self
243 }
244
245 #[cfg(feature = "std")]
246 pub(crate) fn with_trim_paths(mut self, yes: bool) -> Self {
247 self.trim_paths = yes;
248 self
249 }
250}
251
252impl Assembler {
255 #[inline]
259 pub fn compile_and_statically_link(&mut self, module: impl Parse) -> Result<&mut Self, Report> {
260 self.compile_and_statically_link_all([module])
261 }
262
263 pub fn compile_and_statically_link_all(
268 &mut self,
269 modules: impl IntoIterator<Item = impl Parse>,
270 ) -> Result<&mut Self, Report> {
271 let modules = modules
272 .into_iter()
273 .map(|module| module.parse(self.warnings_as_errors, self.source_manager.clone()))
274 .collect::<Result<Vec<_>, Report>>()?;
275
276 self.linker.link_modules(modules)?;
277
278 Ok(self)
279 }
280
281 #[cfg(feature = "std")]
330 pub fn compile_and_statically_link_from_root(
331 &mut self,
332 root: impl AsRef<std::path::Path>,
333 namespace: Option<&Path>,
334 ) -> Result<(), Report> {
335 use miden_assembly_syntax::parser;
336
337 let (root, modules) = parser::read_modules_from_root(
338 root,
339 namespace.map(Into::into),
340 None,
341 self.source_manager.clone(),
342 self.warnings_as_errors,
343 )?;
344 self.linker.link_modules(core::iter::once(root).chain(modules))?;
345 Ok(())
346 }
347
348 pub fn with_package(mut self, package: Arc<Package>, linkage: Linkage) -> Result<Self, Report> {
350 self.link_package(package, linkage)?;
351 Ok(self)
352 }
353
354 pub fn link_package(&mut self, package: Arc<Package>, linkage: Linkage) -> Result<(), Report> {
356 match package.kind {
357 TargetType::Kernel => {
358 if !self.kernel().is_empty() {
359 return Err(Report::msg(format!(
360 "duplicate kernels present in the dependency graph: '{}@{}' conflicts with another kernel we've already linked",
361 package.name, package.version
362 )));
363 }
364
365 self.linker.link_with_kernel(package)?;
366 Ok(())
367 },
368 TargetType::Executable => {
369 Err(Report::msg("cannot add executable packages to an assembler"))
370 },
371 _ => {
372 self.linker
373 .link_library(LinkLibrary::from_package(package).with_linkage(linkage))?;
374 Ok(())
375 },
376 }
377 }
378}
379
380impl Assembler {
383 pub fn warnings_as_errors(&self) -> bool {
385 self.warnings_as_errors
386 }
387
388 pub fn kernel(&self) -> &Kernel {
392 self.linker.kernel()
393 }
394
395 #[cfg(any(feature = "std", all(test, feature = "std")))]
396 pub(crate) fn source_manager(&self) -> Arc<dyn SourceManager> {
397 self.source_manager.clone()
398 }
399
400 #[cfg(any(test, feature = "testing"))]
401 #[doc(hidden)]
402 pub fn linker(&self) -> &Linker {
403 &self.linker
404 }
405}
406
407impl Assembler {
410 pub fn assemble_library(
416 self,
417 name: impl Into<PackageId>,
418 root: impl Parse,
419 support: impl IntoIterator<Item = impl Parse>,
420 ) -> Result<Box<Package>, Report> {
421 let root = root.parse(self.warnings_as_errors, self.source_manager.clone())?;
422 let support = support
423 .into_iter()
424 .map(|module| module.parse(self.warnings_as_errors, self.source_manager.clone()))
425 .collect::<Result<Vec<_>, Report>>()?;
426
427 self.assemble_library_modules(name.into(), root, support, TargetType::Library)?
428 .into_artifact()
429 }
430
431 #[cfg(feature = "std")]
436 pub fn assemble_library_from_root(
437 self,
438 root: impl AsRef<std::path::Path>,
439 namespace: Option<&Path>,
440 ) -> Result<Box<Package>, Report> {
441 use miden_assembly_syntax::parser;
442
443 let root = root.as_ref().to_path_buf();
444 let namespace = namespace.map(Into::into);
445 let (root, support) = parser::read_modules_from_root(
446 &root,
447 namespace,
448 Some(ModuleKind::Library),
449 self.source_manager.clone(),
450 self.warnings_as_errors,
451 )?;
452
453 let name = root.path().as_str().replace("::", "-");
455
456 self.assemble_library_modules(name.into(), root, support, TargetType::Library)?
457 .into_artifact()
458 }
459
460 pub fn assemble_kernel(
466 self,
467 name: impl Into<PackageId>,
468 root: Box<ast::Module>,
469 support: impl IntoIterator<Item = Box<ast::Module>>,
470 ) -> Result<Box<Package>, Report> {
471 self.assemble_library_modules(name.into(), root, support, TargetType::Kernel)?
472 .into_artifact()
473 }
474
475 #[cfg(feature = "std")]
489 pub fn assemble_kernel_from_root(
490 self,
491 name: impl Into<PackageId>,
492 sys_module_path: impl AsRef<std::path::Path>,
493 ) -> Result<Box<Package>, Report> {
494 let sys_module_path = sys_module_path.as_ref();
495 let namespace = Some(Path::KERNEL.into());
496 let (root, support) = miden_assembly_syntax::parser::read_modules_from_root(
497 sys_module_path,
498 namespace,
499 Some(ModuleKind::Kernel),
500 self.source_manager.clone(),
501 self.warnings_as_errors,
502 )?;
503
504 self.assemble_library_modules(name.into(), root, support, TargetType::Kernel)?
505 .into_artifact()
506 }
507
508 fn assemble_library_product(
510 mut self,
511 name: PackageId,
512 module_indices: &[ModuleIndex],
513 kind: TargetType,
514 ) -> Result<AssemblyProduct, Report> {
515 let staticlibs = self.static_libraries_for_builder()?;
516 let mut mast_forest_builder = MastForestBuilder::new_with_static_libraries(staticlibs)?;
517 let exports = {
518 let mut exports = BTreeMap::new();
519
520 for module_idx in module_indices.iter().copied() {
521 let (module_kind, module_path, num_symbols, imports) = {
522 let module = &self.linker[module_idx];
523
524 if let Some(advice_map) = module.advice_map() {
525 mast_forest_builder.merge_advice_map(advice_map)?;
526 }
527
528 (
529 module.kind(),
530 module.path().clone(),
531 module.symbols().len(),
532 module.imports().cloned().collect::<Vec<_>>(),
533 )
534 };
535
536 for index in 0..num_symbols {
537 let index = ItemIndex::new(index);
538 let gid = module_idx + index;
539
540 let path: Arc<Path> = {
541 let symbol = &self.linker[gid];
542 if !symbol.visibility().is_public() {
543 continue;
544 }
545 module_path
546 .join(symbol.name())
547 .canonicalize()
548 .into_diagnostic()?
549 .into_boxed_path()
550 .into()
551 };
552 let export = self.export_symbol(
553 gid,
554 module_kind,
555 path.clone(),
556 &mut mast_forest_builder,
557 )?;
558 if exports.insert(path.clone(), export).is_some() {
559 return Err(Report::new(AssemblerError::DuplicateExportPath { path }));
560 }
561 }
562
563 for import in imports.iter() {
564 if !import.visibility().is_public() {
565 continue;
566 }
567
568 let path: Arc<Path> = module_path
569 .join(import.local_name())
570 .canonicalize()
571 .into_diagnostic()?
572 .into_boxed_path()
573 .into();
574 let export = self.export_import(
575 module_idx,
576 module_kind,
577 path.clone(),
578 import,
579 &mut mast_forest_builder,
580 )?;
581 if exports.insert(path.clone(), export).is_some() {
582 return Err(Report::new(AssemblerError::DuplicateExportPath { path }));
583 }
584 }
585 }
586
587 exports
588 };
589
590 let (mast_forest, node_id_by_ref, source_graph, source_id_by_ref) =
591 mast_forest_builder.build()?.into_parts_with_source_graph();
592 let exports = exports
593 .into_iter()
594 .map(|(path, export)| {
595 export
596 .into_package_export(&node_id_by_ref, &source_id_by_ref)
597 .map(|export| (path, export))
598 })
599 .collect::<Result<BTreeMap<_, _>, _>>()?;
600
601 let modules = self.package_modules(module_indices);
602 self.finish_library_product(name, mast_forest, source_graph, exports, modules, kind)
603 }
604
605 fn package_modules(&self, module_indices: &[ModuleIndex]) -> Vec<PackageModule> {
606 let mut visited = BTreeSet::new();
607 let mut stack = module_indices.to_vec();
608 let mut modules = BTreeMap::new();
609
610 while let Some(module_idx) = stack.pop() {
611 if !visited.insert(module_idx) {
612 continue;
613 }
614
615 let module = &self.linker[module_idx];
616 let mut submodules = Vec::new();
617 for decl in module.submodules() {
618 if !decl.visibility.is_public() {
619 continue;
620 }
621
622 submodules.push(PackageSubmodule::new(decl.name.clone()));
623
624 let child_path = module.path().join(&decl.name);
625 if let Some(child_idx) = self.linker.find_module_index(child_path.as_path()) {
626 stack.push(child_idx);
627 }
628 }
629
630 modules.insert(
631 module.path().clone(),
632 PackageModule::new(module.path().clone(), submodules),
633 );
634 }
635
636 modules.into_values().collect()
637 }
638
639 fn export_symbol(
645 &mut self,
646 gid: GlobalItemIndex,
647 module_kind: ModuleKind,
648 symbol_path: Arc<Path>,
649 mast_forest_builder: &mut MastForestBuilder,
650 ) -> Result<PendingPackageExport, Report> {
651 log::trace!(target: "assembler::export_symbol", "exporting {} {symbol_path}", match self.linker[gid].item() {
652 SymbolItem::Compiled(ItemInfo::Procedure(_)) => "compiled procedure",
653 SymbolItem::Compiled(ItemInfo::Constant(_)) => "compiled constant",
654 SymbolItem::Compiled(ItemInfo::Type(_)) => "compiled type",
655 SymbolItem::Procedure(_) => "procedure",
656 SymbolItem::Constant(_) => "constant",
657 SymbolItem::Type(_) => "type",
658 });
659 let mut cache = crate::linker::ResolverCache::default();
660 let export = match self.linker[gid].item() {
661 SymbolItem::Compiled(ItemInfo::Procedure(item)) => {
662 let resolved = match mast_forest_builder.get_procedure(gid) {
663 Some(proc) => ResolvedProcedure {
664 node: proc.body_node_ref(),
665 signature: proc.signature(),
666 },
667 None => {
670 log::trace!(target: "assembler::export_symbol", "no procedure found in forest");
671 let node = self.ensure_valid_procedure_mast_root(
672 InvokeKind::ProcRef,
673 SourceSpan::UNKNOWN,
674 item.digest,
675 item.source_library_commitment(),
676 item.source_root_id(),
677 item.source_debug_root_id().map(DebugSourceNodeId::from),
678 mast_forest_builder,
679 )?;
680 ResolvedProcedure { node, signature: item.signature.clone() }
681 },
682 };
683 let digest = item.digest;
684 let ResolvedProcedure { node, signature } = resolved;
685 let attributes = item.attributes.clone();
686 let pctx = ProcedureContext::new(
687 gid,
688 false,
689 symbol_path.clone(),
690 Visibility::Public,
691 signature.clone(),
692 module_kind.is_kernel(),
693 self.source_manager.clone(),
694 );
695
696 let procedure = pctx.into_procedure(digest, node);
697 self.linker.register_procedure_root(gid, digest);
698 mast_forest_builder.insert_procedure(gid, procedure)?;
699 PendingPackageExport::Procedure(PendingProcedureExport {
700 digest,
701 path: symbol_path,
702 node_ref: node,
703 source_ref: mast_forest_builder.latest_source_ref_for_node_ref(node),
704 signature: signature.map(|sig| (*sig).clone()),
705 attributes,
706 })
707 },
708 SymbolItem::Compiled(ItemInfo::Constant(item)) => {
709 PendingPackageExport::Constant(ConstantExport {
710 path: symbol_path,
711 value: item.value.clone(),
712 })
713 },
714 SymbolItem::Compiled(ItemInfo::Type(item)) => {
715 PendingPackageExport::Type(TypeExport { path: symbol_path, ty: item.ty.clone() })
716 },
717 SymbolItem::Procedure(_) => {
718 self.compile_subgraph(SubgraphRoot::not_as_entrypoint(gid), mast_forest_builder)?;
719 let proc = mast_forest_builder
720 .get_procedure(gid)
721 .expect("compilation succeeded but root not found in cache");
722 let digest = proc.mast_root();
723 let signature = self.linker.resolve_signature(gid)?;
724 let attributes = self.linker.resolve_attributes(gid);
725 PendingPackageExport::Procedure(PendingProcedureExport {
726 digest,
727 path: symbol_path,
728 node_ref: proc.body_node_ref(),
729 source_ref: mast_forest_builder
730 .latest_source_ref_for_node_ref(proc.body_node_ref()),
731 signature: signature.map(Arc::unwrap_or_clone),
732 attributes,
733 })
734 },
735 SymbolItem::Constant(item) => {
736 let value = self.linker.const_eval(gid, &item.value, &mut cache)?;
738
739 PendingPackageExport::Constant(ConstantExport { path: symbol_path, value })
740 },
741 SymbolItem::Type(item) => {
742 let ty = self.linker.resolve_type(item.span(), gid)?;
743 PendingPackageExport::Type(TypeExport { path: symbol_path, ty })
744 },
745 };
746
747 Ok(export)
748 }
749
750 fn export_import(
751 &mut self,
752 module: ModuleIndex,
753 module_kind: ModuleKind,
754 symbol_path: Arc<Path>,
755 import: &Import,
756 mast_forest_builder: &mut MastForestBuilder,
757 ) -> Result<PendingPackageExport, Report> {
758 if let Some(resolved) = import.resolved() {
759 return self.export_symbol(resolved, module_kind, symbol_path, mast_forest_builder);
760 }
761
762 let target = import.target_path();
763 let context = SymbolResolutionContext {
764 span: target.span(),
765 module,
766 kind: Some(InvokeKind::ProcRef),
767 };
768 match self.linker.resolve_path(&context, target.inner())? {
769 SymbolResolution::Exact { gid, .. } => {
770 self.export_symbol(gid, module_kind, symbol_path, mast_forest_builder)
771 },
772 SymbolResolution::Module { .. }
773 | SymbolResolution::MastRoot(_)
774 | SymbolResolution::Local(_)
775 | SymbolResolution::External(_) => {
776 Err(self.unresolved_import_report("export", &symbol_path, import))
777 },
778 }
779 }
780
781 pub fn assemble_program(
790 self,
791 name: impl Into<PackageId>,
792 source: impl Parse,
793 ) -> Result<Box<Package>, Report> {
794 let program = source.parse(self.warnings_as_errors, self.source_manager.clone())?;
795 if !program.is_executable() {
796 return Err(Report::msg(
797 "unable to assemble program: source is not an executable module",
798 ));
799 }
800
801 self.assemble_executable_modules(name.into(), program, [])?.into_artifact()
802 }
803
804 pub(crate) fn assemble_library_modules(
805 mut self,
806 name: PackageId,
807 root: Box<ast::Module>,
808 support: impl IntoIterator<Item = Box<ast::Module>>,
809 kind: TargetType,
810 ) -> Result<AssemblyProduct, Report> {
811 let module_indices = match kind {
812 TargetType::Kernel => self.linker.link_kernel(root, support)?,
813 _ => self.linker.link([root], support)?,
814 };
815 self.verify_exported_signature_type_visibility(&module_indices)?;
816 self.assemble_library_product(name, &module_indices, kind)
817 }
818
819 fn verify_exported_signature_type_visibility(
820 &self,
821 module_indices: &[ModuleIndex],
822 ) -> Result<(), Report> {
823 let resolver = SymbolResolver::new(&self.linker);
824 for module_index in module_indices.iter().copied() {
825 let module = &self.linker[module_index];
826 for symbol in module.symbols() {
827 if !symbol.visibility().is_public() {
828 continue;
829 }
830
831 self.verify_exported_item(&resolver, module_index, symbol, None)?;
832 }
833
834 for import in module.imports() {
835 if !import.visibility().is_public()
836 || !matches!(import.kind(), ast::ImportKind::Item)
837 {
838 continue;
839 }
840
841 let Some(gid) = import.resolved() else {
842 continue;
843 };
844
845 self.verify_exported_item(
846 &resolver,
847 gid.module,
848 &self.linker[gid],
849 Some(import.span()),
850 )?;
851 }
852 }
853
854 Ok(())
855 }
856
857 fn verify_exported_item(
858 &self,
859 resolver: &SymbolResolver<'_>,
860 module_index: ModuleIndex,
861 symbol: &crate::linker::Symbol,
862 export_span: Option<SourceSpan>,
863 ) -> Result<(), Report> {
864 match symbol.item() {
865 SymbolItem::Procedure(proc) => {
866 let proc = proc.borrow();
867 self.verify_exported_signature(resolver, module_index, proc.signature())
868 },
869 SymbolItem::Type(type_decl) => {
870 if !symbol.visibility().is_public() {
871 return Err(Report::new(SemanticAnalysisError::PrivateTypeInExportedType {
872 span: export_span.unwrap_or_else(|| type_decl.name().span()),
873 defined: type_decl.name().span(),
874 }));
875 }
876
877 let mut visiting_types = BTreeSet::default();
878 self.verify_exported_type_decl(
879 resolver,
880 module_index,
881 type_decl,
882 &mut visiting_types,
883 ExportedTypeUse::TypeDeclaration,
884 )
885 },
886 SymbolItem::Constant(_)
887 | SymbolItem::Compiled(
888 ItemInfo::Procedure(_) | ItemInfo::Constant(_) | ItemInfo::Type(_),
889 ) => Ok(()),
890 }
891 }
892
893 fn verify_exported_signature(
894 &self,
895 resolver: &SymbolResolver<'_>,
896 current_module: ModuleIndex,
897 signature: Option<&ast::FunctionType>,
898 ) -> Result<(), Report> {
899 let Some(signature) = signature else {
900 return Ok(());
901 };
902
903 for ty in signature.args.iter().chain(signature.results.iter()) {
904 let mut visiting_types = BTreeSet::default();
905 self.verify_exported_type_expr(
906 resolver,
907 current_module,
908 ty,
909 &mut visiting_types,
910 ExportedTypeUse::ProcedureSignature,
911 )?;
912 }
913
914 Ok(())
915 }
916
917 fn verify_exported_type_decl(
918 &self,
919 resolver: &SymbolResolver<'_>,
920 current_module: ModuleIndex,
921 type_decl: &ast::TypeDecl,
922 visiting_types: &mut BTreeSet<GlobalItemIndex>,
923 usage: ExportedTypeUse,
924 ) -> Result<(), Report> {
925 match type_decl {
926 ast::TypeDecl::Alias(alias) => {
927 self.verify_exported_type_expr(
928 resolver,
929 current_module,
930 &alias.ty,
931 visiting_types,
932 usage,
933 )?;
934 },
935 ast::TypeDecl::Enum(ty) => {
936 for variant in ty.variants() {
937 if let Some(payload_ty) = variant.value_ty.as_ref() {
938 self.verify_exported_type_expr(
939 resolver,
940 current_module,
941 payload_ty,
942 visiting_types,
943 usage,
944 )?;
945 }
946 }
947 },
948 }
949
950 Ok(())
951 }
952
953 fn verify_exported_type_expr(
954 &self,
955 resolver: &SymbolResolver<'_>,
956 current_module: ModuleIndex,
957 ty: &ast::TypeExpr,
958 visiting_types: &mut BTreeSet<GlobalItemIndex>,
959 usage: ExportedTypeUse,
960 ) -> Result<(), Report> {
961 match ty {
962 ast::TypeExpr::Primitive(_) => Ok(()),
963 ast::TypeExpr::Ptr(ty) => self.verify_exported_type_expr(
964 resolver,
965 current_module,
966 &ty.pointee,
967 visiting_types,
968 usage,
969 ),
970 ast::TypeExpr::Array(ty) => self.verify_exported_type_expr(
971 resolver,
972 current_module,
973 &ty.elem,
974 visiting_types,
975 usage,
976 ),
977 ast::TypeExpr::Struct(ty) => {
978 for field in ty.fields.iter() {
979 self.verify_exported_type_expr(
980 resolver,
981 current_module,
982 &field.ty,
983 visiting_types,
984 usage,
985 )?;
986 }
987
988 Ok(())
989 },
990 ast::TypeExpr::Ref(path) => {
991 let context = SymbolResolutionContext {
992 span: path.span(),
993 module: current_module,
994 kind: None,
995 };
996 let resolution =
997 resolver.resolve_path(&context, path.as_deref()).map_err(Report::from)?;
998
999 let gid = match resolution {
1000 SymbolResolution::Exact { gid, .. } => gid,
1001 SymbolResolution::Local(item) => current_module + item.into_inner(),
1002 SymbolResolution::External(_)
1003 | SymbolResolution::MastRoot(_)
1004 | SymbolResolution::Module { .. } => return Ok(()),
1005 };
1006
1007 let symbol = &self.linker[gid];
1008 let SymbolItem::Type(type_decl) = symbol.item() else {
1009 return Ok(());
1010 };
1011
1012 if !symbol.visibility().is_public() {
1013 return Err(Report::new(
1014 usage.private_type_error(path.span(), type_decl.name().span()),
1015 ));
1016 }
1017
1018 if !visiting_types.insert(gid) {
1019 return Ok(());
1020 }
1021
1022 self.verify_exported_type_decl(
1023 resolver,
1024 gid.module,
1025 type_decl,
1026 visiting_types,
1027 usage,
1028 )?;
1029
1030 visiting_types.remove(&gid);
1031 Ok(())
1032 },
1033 }
1034 }
1035
1036 pub(crate) fn assemble_executable_modules(
1037 mut self,
1038 name: PackageId,
1039 program: Box<ast::Module>,
1040 support_modules: impl IntoIterator<Item = Box<ast::Module>>,
1041 ) -> Result<AssemblyProduct, Report> {
1042 let namespace = Arc::<Path>::from(program.path());
1044 let module_index = self.linker.link([program], support_modules)?[0];
1045
1046 let entrypoint = self.linker[module_index]
1049 .symbols()
1050 .position(|symbol| symbol.name().as_str() == Ident::MAIN)
1051 .map(|index| module_index + ItemIndex::new(index))
1052 .ok_or(SemanticAnalysisError::MissingEntrypoint)?;
1053
1054 let staticlibs = self.static_libraries_for_builder()?;
1056 let mut mast_forest_builder = MastForestBuilder::new_with_static_libraries(staticlibs)?;
1057
1058 if let Some(advice_map) = self.linker[module_index].advice_map() {
1059 mast_forest_builder.merge_advice_map(advice_map)?;
1060 }
1061
1062 self.compile_subgraph(SubgraphRoot::with_entrypoint(entrypoint), &mut mast_forest_builder)?;
1063 let entry_node_ref = mast_forest_builder
1064 .get_procedure(entrypoint)
1065 .expect("compilation succeeded but root not found in cache")
1066 .body_node_ref();
1067
1068 let (mast_forest, node_id_by_ref, source_graph, _) =
1069 mast_forest_builder.build()?.into_parts_with_source_graph();
1070 let entry_node_id = *node_id_by_ref.get(&entry_node_ref).ok_or_else(|| {
1071 Report::msg(format!("entrypoint ref {entry_node_ref} was not finalized"))
1072 })?;
1073
1074 self.finish_program_product(
1075 name,
1076 namespace,
1077 mast_forest,
1078 source_graph,
1079 entry_node_id,
1080 self.linker.kernel_package(),
1081 )
1082 }
1083
1084 fn finish_library_product(
1085 &self,
1086 name: PackageId,
1087 mast_forest: miden_core::mast::MastForest,
1088 source_graph: SourceDebugGraph,
1089 exports: BTreeMap<Arc<Path>, PackageExport>,
1090 modules: Vec<PackageModule>,
1091 kind: TargetType,
1092 ) -> Result<AssemblyProduct, Report> {
1093 let mast = Arc::new(mast_forest);
1094 let package = Box::new(
1095 Package::create_with_modules(
1096 name,
1097 miden_mast_package::Version::new(0, 0, 0),
1098 kind,
1099 mast,
1100 exports.into_values(),
1101 modules,
1102 None,
1103 )
1104 .map_err(Report::msg)?,
1105 );
1106 let debug_info = self.emit_debug_info.then(|| {
1107 #[cfg_attr(not(feature = "std"), expect(unused_mut))]
1108 let mut debug_info = self.debug_info.clone();
1109 #[cfg(feature = "std")]
1110 if let Some(trimmer) = self.source_path_trimmer() {
1111 debug_info.trim_paths(&trimmer);
1112 }
1113 debug_info
1114 });
1115
1116 let source_graph =
1117 self.emit_debug_info.then(|| self.apply_source_debug_options(source_graph));
1118
1119 Ok(AssemblyProduct::new(package, None, debug_info, source_graph))
1120 }
1121
1122 fn static_libraries_for_builder(&self) -> Result<Vec<StaticLibrary<'_>>, Report> {
1123 self.linker
1124 .static_libraries()
1125 .map(|lib| {
1126 let debug_info = match lib.package.debug_info() {
1127 Ok(debug_info) => debug_info,
1128 Err(PackageDebugInfoError::UntrustedSections) => None,
1129 Err(err) => {
1130 return Err(Report::msg(format!(
1131 "failed to decode debug info for statically linked package '{}': {err}",
1132 lib.package.name
1133 )));
1134 },
1135 };
1136 Ok(StaticLibrary::new(lib.mast().as_ref(), debug_info)
1137 .with_source_library_commitment(lib.commitment())
1138 .with_alternate_source_library_commitment(
1139 lib.package.interface_digest().into_diagnostic()?,
1140 ))
1141 })
1142 .collect()
1143 }
1144
1145 fn finish_program_product(
1146 &self,
1147 name: PackageId,
1148 namespace: Arc<Path>,
1149 mast_forest: miden_core::mast::MastForest,
1150 source_graph: SourceDebugGraph,
1151 entrypoint: MastNodeId,
1152 kernel: Option<Arc<Package>>,
1153 ) -> Result<AssemblyProduct, Report> {
1154 let mast = Arc::new(mast_forest);
1155 let entry: Arc<Path> = namespace.join(ast::ProcedureName::MAIN_PROC_NAME).into();
1156 let entry_digest = mast[entrypoint].digest();
1157 let entry_source_node = source_graph
1158 .unique_root_for_exec_node(entrypoint)
1159 .map(|source_id| DebugSourceNodeId::from(u32::from(source_id)));
1160 let package = Box::new(
1161 Package::create(
1162 name,
1163 miden_mast_package::Version::new(0, 0, 0),
1164 TargetType::Executable,
1165 mast,
1166 vec![PackageExport::Procedure(
1167 ProcedureExport::new(entry, Some(entrypoint), entry_digest, None)
1168 .with_source_node(entry_source_node),
1169 )],
1170 None,
1171 )
1172 .map_err(Report::msg)?,
1173 );
1174 let debug_info = self.emit_debug_info.then(|| {
1175 #[cfg_attr(not(feature = "std"), expect(unused_mut))]
1176 let mut debug_info = self.debug_info.clone();
1177 #[cfg(feature = "std")]
1178 if let Some(trimmer) = self.source_path_trimmer() {
1179 debug_info.trim_paths(&trimmer);
1180 }
1181 debug_info
1182 });
1183
1184 let source_graph =
1185 self.emit_debug_info.then(|| self.apply_source_debug_options(source_graph));
1186
1187 Ok(AssemblyProduct::new(package, kernel, debug_info, source_graph))
1188 }
1189
1190 fn apply_source_debug_options(&self, source_graph: SourceDebugGraph) -> SourceDebugGraph {
1191 if self.trim_paths {
1192 #[cfg(feature = "std")]
1193 if let Some(trimmer) = self.source_path_trimmer() {
1194 return source_graph.with_rewritten_source_locations(
1195 |location| trimmer.trim_location(location),
1196 |location| trimmer.trim_file_line_col(location),
1197 );
1198 }
1199 }
1200
1201 source_graph
1202 }
1203
1204 #[cfg(feature = "std")]
1205 fn source_path_trimmer(&self) -> Option<debuginfo::SourcePathTrimmer> {
1206 if !self.trim_paths {
1207 return None;
1208 }
1209
1210 std::env::current_dir().ok().map(debuginfo::SourcePathTrimmer::new)
1211 }
1212
1213 fn compile_subgraph(
1218 &mut self,
1219 root: SubgraphRoot,
1220 mast_forest_builder: &mut MastForestBuilder,
1221 ) -> Result<(), Report> {
1222 let mut worklist: Vec<GlobalItemIndex> = self
1223 .linker
1224 .topological_sort_from_root(root.proc_id)
1225 .map_err(|cycle| {
1226 let iter = cycle.into_node_ids();
1227 let mut nodes = Vec::with_capacity(iter.len());
1228 for node in iter {
1229 let module = self.linker[node.module].path();
1230 let proc = self.linker[node].name();
1231 nodes.push(format!("{}", module.join(proc)));
1232 }
1233 LinkerError::Cycle { nodes: nodes.into() }
1234 })?
1235 .into_iter()
1236 .filter(|&gid| matches!(self.linker[gid].item(), SymbolItem::Procedure(_)))
1237 .collect();
1238
1239 assert!(!worklist.is_empty());
1240
1241 self.process_graph_worklist(&mut worklist, &root, mast_forest_builder)
1242 }
1243
1244 fn process_graph_worklist(
1246 &mut self,
1247 worklist: &mut Vec<GlobalItemIndex>,
1248 root: &SubgraphRoot,
1249 mast_forest_builder: &mut MastForestBuilder,
1250 ) -> Result<(), Report> {
1251 while let Some(procedure_gid) = worklist.pop() {
1254 if let Some(proc) = mast_forest_builder.get_procedure(procedure_gid) {
1256 self.linker.register_procedure_root(procedure_gid, proc.mast_root());
1257 continue;
1258 }
1259 let (module_kind, module_path) = {
1261 let module = &self.linker[procedure_gid.module];
1262 (module.kind(), module.path().clone())
1263 };
1264 match self.linker[procedure_gid].item() {
1265 SymbolItem::Procedure(proc) => {
1266 let proc = proc.borrow();
1267 let num_locals = proc.num_locals();
1268 let path = Arc::<Path>::from(module_path.join(proc.name().as_str()));
1269 let signature = self.linker.resolve_signature(procedure_gid)?;
1270 let is_program_entrypoint =
1271 root.is_program_entrypoint && root.proc_id == procedure_gid;
1272
1273 let pctx = ProcedureContext::new(
1274 procedure_gid,
1275 is_program_entrypoint,
1276 path.clone(),
1277 proc.visibility(),
1278 signature.clone(),
1279 module_kind.is_kernel(),
1280 self.source_manager.clone(),
1281 )
1282 .with_span(proc.span())
1283 .with_num_locals(num_locals)?;
1284
1285 let procedure = self.compile_procedure(pctx, mast_forest_builder)?;
1287 self.debug_info
1295 .register_procedure_debug_info(&procedure, self.source_manager.as_ref())?;
1296
1297 drop(proc);
1299 self.linker.register_procedure_root(procedure_gid, procedure.mast_root());
1300 mast_forest_builder.insert_procedure(procedure_gid, procedure)?;
1301 },
1302 SymbolItem::Compiled(_) | SymbolItem::Constant(_) | SymbolItem::Type(_) => {
1303 },
1305 }
1306 }
1307
1308 Ok(())
1309 }
1310
1311 fn unresolved_import_report(
1312 &self,
1313 action: &'static str,
1314 symbol_path: &Path,
1315 import: &Import,
1316 ) -> Report {
1317 let target = import.target_path();
1318 let span = target.span();
1319
1320 RelatedLabel::error(format!(
1321 "unable to {action} import '{symbol_path}' targeting '{}'",
1322 target.inner()
1323 ))
1324 .with_labeled_span(span, "this import target does not resolve to a concrete item")
1325 .with_help("imports must resolve to a concrete item before they can be used")
1326 .with_source_file(self.source_manager.get(span.source_id()).ok())
1327 .into()
1328 }
1329
1330 fn compile_procedure(
1332 &self,
1333 mut proc_ctx: ProcedureContext,
1334 mast_forest_builder: &mut MastForestBuilder,
1335 ) -> Result<Procedure, Report> {
1336 let gid = proc_ctx.id();
1338
1339 let num_locals = proc_ctx.num_locals();
1340
1341 let proc = match self.linker[gid].item() {
1342 SymbolItem::Procedure(proc) => proc.borrow(),
1343 _ => panic!("expected item to be a procedure AST"),
1344 };
1345 let body_wrapper = if proc_ctx.is_program_entrypoint() {
1346 assert!(num_locals == 0, "program entrypoint cannot have locals");
1347
1348 Some(BodyWrapper {
1349 prologue: fmp_initialization_sequence(),
1350 epilogue: Vec::new(),
1351 })
1352 } else if num_locals > 0 {
1353 Some(BodyWrapper {
1354 prologue: fmp_start_frame_sequence(num_locals),
1355 epilogue: fmp_end_frame_sequence(num_locals),
1356 })
1357 } else {
1358 None
1359 };
1360
1361 let proc_body_ref =
1362 self.compile_body(proc.iter(), &mut proc_ctx, body_wrapper, mast_forest_builder, 0)?;
1363
1364 let proc_mast_root = mast_forest_builder
1365 .mast_root_for_ref(proc_body_ref)
1366 .expect("no MAST node for compiled procedure");
1367 Ok(proc_ctx.into_procedure(proc_mast_root, proc_body_ref))
1368 }
1369
1370 fn create_asm_op(
1372 &self,
1373 span: &SourceSpan,
1374 op_name: &str,
1375 proc_ctx: &ProcedureContext,
1376 ) -> AssemblyOp {
1377 let location = proc_ctx.source_manager().location(*span).ok();
1378 let context_name = proc_ctx.path().to_string();
1379 let num_cycles = 0;
1380 AssemblyOp::new(location, context_name, num_cycles, op_name.to_string())
1381 }
1382
1383 fn compile_body<'a, I>(
1384 &self,
1385 body: I,
1386 proc_ctx: &mut ProcedureContext,
1387 wrapper: Option<BodyWrapper>,
1388 mast_forest_builder: &mut MastForestBuilder,
1389 nesting_depth: usize,
1390 ) -> Result<MastNodeRef, Report>
1391 where
1392 I: Iterator<Item = &'a ast::Op>,
1393 {
1394 use ast::Op;
1395
1396 let mut body_node_refs: Vec<MastNodeRef> = Vec::new();
1397 let mut block_builder = BasicBlockBuilder::new(wrapper, mast_forest_builder);
1398
1399 for op in body {
1400 match op {
1401 Op::Inst(inst) => {
1402 if let Some(node_ref) =
1403 self.compile_instruction(inst, &mut block_builder, proc_ctx)?
1404 {
1405 if let Some(basic_block_id) = block_builder.make_basic_block()? {
1406 body_node_refs.push(basic_block_id);
1407 }
1408
1409 body_node_refs.push(node_ref);
1410 }
1411 },
1412
1413 Op::If { then_blk, else_blk, span } => {
1414 if let Some(basic_block_id) = block_builder.make_basic_block()? {
1415 body_node_refs.push(basic_block_id);
1416 }
1417
1418 let next_depth = nesting_depth + 1;
1419 if next_depth > MAX_CONTROL_FLOW_NESTING {
1420 return Err(Report::new(AssemblerError::ControlFlowNestingDepthExceeded {
1421 span: *span,
1422 source_file: proc_ctx.source_manager().get(span.source_id()).ok(),
1423 max_depth: MAX_CONTROL_FLOW_NESTING,
1424 }));
1425 }
1426
1427 let then_blk = self.compile_body(
1428 then_blk.iter(),
1429 proc_ctx,
1430 None,
1431 block_builder.mast_forest_builder_mut(),
1432 next_depth,
1433 )?;
1434 let else_blk = self.compile_body(
1435 else_blk.iter(),
1436 proc_ctx,
1437 None,
1438 block_builder.mast_forest_builder_mut(),
1439 next_depth,
1440 )?;
1441
1442 let asm_op = self.create_asm_op(span, "if.true", proc_ctx);
1443 let split_node_ref = block_builder
1444 .mast_forest_builder_mut()
1445 .ensure_split_node_ref([then_blk, else_blk], asm_op)?;
1446
1447 body_node_refs.push(split_node_ref);
1448 },
1449
1450 Op::Repeat { count, body, span } => {
1451 if let Some(basic_block_id) = block_builder.make_basic_block()? {
1452 body_node_refs.push(basic_block_id);
1453 }
1454
1455 let next_depth = nesting_depth + 1;
1456 if next_depth > MAX_CONTROL_FLOW_NESTING {
1457 return Err(Report::new(AssemblerError::ControlFlowNestingDepthExceeded {
1458 span: *span,
1459 source_file: proc_ctx.source_manager().get(span.source_id()).ok(),
1460 max_depth: MAX_CONTROL_FLOW_NESTING,
1461 }));
1462 }
1463
1464 let repeat_node_ref = self.compile_body(
1465 body.iter(),
1466 proc_ctx,
1467 None,
1468 block_builder.mast_forest_builder_mut(),
1469 next_depth,
1470 )?;
1471
1472 let iteration_count = (*count).expect_value();
1473 if iteration_count == 0 {
1474 return Err(RelatedLabel::error("invalid repeat count")
1475 .with_help("repeat count must be greater than 0")
1476 .with_labeled_span(count.span(), "repeat count must be at least 1")
1477 .with_source_file(
1478 proc_ctx.source_manager().get(proc_ctx.span().source_id()).ok(),
1479 )
1480 .into());
1481 }
1482 if iteration_count > MAX_REPEAT_COUNT {
1483 return Err(RelatedLabel::error("invalid repeat count")
1484 .with_help(format!(
1485 "repeat count must be less than or equal to {MAX_REPEAT_COUNT}",
1486 ))
1487 .with_labeled_span(
1488 count.span(),
1489 format!("repeat count exceeds {MAX_REPEAT_COUNT}"),
1490 )
1491 .with_source_file(
1492 proc_ctx.source_manager().get(proc_ctx.span().source_id()).ok(),
1493 )
1494 .into());
1495 }
1496
1497 for _ in 0..iteration_count {
1498 body_node_refs.push(repeat_node_ref);
1499 }
1500 },
1501
1502 Op::While { body, span } => {
1503 if let Some(basic_block_id) = block_builder.make_basic_block()? {
1504 body_node_refs.push(basic_block_id);
1505 }
1506
1507 let next_depth = nesting_depth + 1;
1508 if next_depth > MAX_CONTROL_FLOW_NESTING {
1509 return Err(Report::new(AssemblerError::ControlFlowNestingDepthExceeded {
1510 span: *span,
1511 source_file: proc_ctx.source_manager().get(span.source_id()).ok(),
1512 max_depth: MAX_CONTROL_FLOW_NESTING,
1513 }));
1514 }
1515
1516 let asm_op = self.create_asm_op(span, "while.true", proc_ctx);
1526
1527 let loop_body_node_ref = self.compile_body(
1528 body.iter(),
1529 proc_ctx,
1530 None,
1531 block_builder.mast_forest_builder_mut(),
1532 next_depth,
1533 )?;
1534 let loop_node_ref = block_builder
1535 .mast_forest_builder_mut()
1536 .ensure_loop_node_ref(loop_body_node_ref, asm_op.clone())?;
1537 let noop_block_ref = block_builder.mast_forest_builder_mut().ensure_block_ref(
1538 vec![Operation::Noop],
1539 vec![],
1540 vec![],
1541 )?;
1542
1543 let split_node_ref = block_builder
1544 .mast_forest_builder_mut()
1545 .ensure_split_node_ref([loop_node_ref, noop_block_ref], asm_op)?;
1546
1547 body_node_refs.push(split_node_ref);
1548 },
1549
1550 Op::DoWhile { body, condition, span } => {
1551 if let Some(basic_block_id) = block_builder.make_basic_block()? {
1552 body_node_refs.push(basic_block_id);
1553 }
1554
1555 let next_depth = nesting_depth + 1;
1556 if next_depth > MAX_CONTROL_FLOW_NESTING {
1557 return Err(Report::new(AssemblerError::ControlFlowNestingDepthExceeded {
1558 span: *span,
1559 source_file: proc_ctx.source_manager().get(span.source_id()).ok(),
1560 max_depth: MAX_CONTROL_FLOW_NESTING,
1561 }));
1562 }
1563
1564 let asm_op = self.create_asm_op(span, "do.while", proc_ctx);
1571
1572 let loop_body_node_ref = self.compile_body(
1573 body.iter().chain(condition.iter()),
1574 proc_ctx,
1575 None,
1576 block_builder.mast_forest_builder_mut(),
1577 next_depth,
1578 )?;
1579 let loop_node_ref = block_builder
1580 .mast_forest_builder_mut()
1581 .ensure_loop_node_ref(loop_body_node_ref, asm_op)?;
1582
1583 body_node_refs.push(loop_node_ref);
1584 },
1585 }
1586 }
1587
1588 if let Some(basic_block_id) = block_builder.try_into_basic_block()? {
1589 body_node_refs.push(basic_block_id);
1590 }
1591
1592 let procedure_body_ref = if body_node_refs.is_empty() {
1593 mast_forest_builder.ensure_block_ref(vec![Operation::Noop], vec![], vec![])?
1594 } else {
1595 let asm_op = self.create_asm_op(&proc_ctx.span(), "begin", proc_ctx);
1596 mast_forest_builder.join_node_refs(body_node_refs, Some(asm_op))?
1597 };
1598
1599 Ok(procedure_body_ref)
1600 }
1601
1602 pub(super) fn resolve_target(
1607 &self,
1608 kind: InvokeKind,
1609 target: &InvocationTarget,
1610 caller_module: ModuleIndex,
1611 mast_forest_builder: &mut MastForestBuilder,
1612 ) -> Result<ResolvedProcedure, Report> {
1613 let caller = SymbolResolutionContext {
1614 span: target.span(),
1615 module: caller_module,
1616 kind: Some(kind),
1617 };
1618 let resolved = self.linker.resolve_invoke_target(&caller, target)?;
1619 match resolved {
1620 SymbolResolution::MastRoot(mast_root) => {
1621 let node = self.ensure_valid_procedure_mast_root(
1622 kind,
1623 target.span(),
1624 mast_root.into_inner(),
1625 None,
1626 None,
1627 None,
1628 mast_forest_builder,
1629 )?;
1630 Ok(ResolvedProcedure { node, signature: None })
1631 },
1632 SymbolResolution::Exact { gid, .. } => {
1633 match mast_forest_builder.get_procedure(gid) {
1634 Some(proc) => Ok(ResolvedProcedure {
1635 node: proc.body_node_ref(),
1636 signature: proc.signature(),
1637 }),
1638 None => match self.linker[gid].item() {
1641 SymbolItem::Compiled(ItemInfo::Procedure(p)) => {
1642 let node = self.ensure_valid_procedure_mast_root(
1643 kind,
1644 target.span(),
1645 p.digest,
1646 p.source_library_commitment(),
1647 p.source_root_id(),
1648 p.source_debug_root_id().map(DebugSourceNodeId::from),
1649 mast_forest_builder,
1650 )?;
1651 Ok(ResolvedProcedure { node, signature: p.signature.clone() })
1652 },
1653 SymbolItem::Procedure(_) => panic!(
1654 "AST procedure {gid:?} exists in the linker, but not in the MastForestBuilder"
1655 ),
1656 SymbolItem::Compiled(_) | SymbolItem::Type(_) | SymbolItem::Constant(_) => {
1657 unreachable!("invoke resolver should reject non-procedure targets")
1658 },
1659 },
1660 }
1661 },
1662 SymbolResolution::Module { .. }
1663 | SymbolResolution::External(_)
1664 | SymbolResolution::Local(_) => unreachable!(),
1665 }
1666 }
1667
1668 fn ensure_valid_procedure_mast_root(
1673 &self,
1674 kind: InvokeKind,
1675 span: SourceSpan,
1676 mast_root: Word,
1677 source_library_commitment: Option<Word>,
1678 source_root_id: Option<MastNodeId>,
1679 source_debug_root_id: Option<DebugSourceNodeId>,
1680 mast_forest_builder: &mut MastForestBuilder,
1681 ) -> Result<MastNodeRef, Report> {
1682 let current_source_file = self.source_manager.get(span.source_id()).ok();
1684
1685 if matches!(kind, InvokeKind::SysCall) && self.linker.has_nonempty_kernel() {
1686 if !self.linker.kernel().contains_proc(mast_root) {
1690 let callee = mast_forest_builder
1691 .find_procedure_by_mast_root(&mast_root)
1692 .map(|proc| proc.path().clone())
1693 .unwrap_or_else(|| {
1694 let digest_path = format!("{mast_root}");
1695 Arc::<Path>::from(Path::new(&digest_path))
1696 });
1697 return Err(Report::new(LinkerError::InvalidSysCallTarget {
1698 span,
1699 source_file: current_source_file,
1700 callee,
1701 }));
1702 }
1703 }
1704
1705 if let (Some(source_library_commitment), Some(source_root_id)) =
1706 (source_library_commitment, source_root_id)
1707 && let Some(conflicting_root) = self.linker.conflicting_dynamic_procedure_export_root(
1708 source_library_commitment,
1709 mast_root,
1710 source_root_id,
1711 )
1712 {
1713 return Err(Report::new(LinkerError::AmbiguousDynamicProcedureRoot {
1714 span,
1715 source_file: current_source_file,
1716 mast_root,
1717 source_library_commitment,
1718 selected_root: source_root_id,
1719 conflicting_root,
1720 }));
1721 }
1722
1723 mast_forest_builder.ensure_external_link_with_source_ref(
1724 mast_root,
1725 source_library_commitment,
1726 source_root_id,
1727 source_debug_root_id,
1728 )
1729 }
1730}
1731
1732struct SubgraphRoot {
1740 proc_id: GlobalItemIndex,
1741 is_program_entrypoint: bool,
1742}
1743
1744impl SubgraphRoot {
1745 fn with_entrypoint(proc_id: GlobalItemIndex) -> Self {
1746 Self { proc_id, is_program_entrypoint: true }
1747 }
1748
1749 fn not_as_entrypoint(proc_id: GlobalItemIndex) -> Self {
1750 Self { proc_id, is_program_entrypoint: false }
1751 }
1752}
1753
1754pub(crate) struct BodyWrapper {
1757 pub prologue: Vec<Operation>,
1758 pub epilogue: Vec<Operation>,
1759}
1760
1761pub(super) struct ResolvedProcedure {
1762 pub node: MastNodeRef,
1763 pub signature: Option<Arc<FunctionType>>,
1764}