1use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
2use core::fmt;
3
4use miden_core::{
5 advice::AdviceMap,
6 serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8use miden_debug_types::{SourceFile, SourceManager, SourceSpan, Span, Spanned};
9use miden_utils_diagnostics::Report;
10#[cfg(feature = "arbitrary")]
11use proptest::prelude::*;
12use smallvec::SmallVec;
13
14use super::{
15 Alias, Constant, DocString, EnumType, Export, FunctionType, GlobalItemIndex, ItemIndex,
16 LocalSymbolResolver, Path, Procedure, ProcedureName, QualifiedProcedureName, SymbolResolution,
17 SymbolResolutionError, TypeAlias, TypeDecl, TypeResolver, Variant,
18};
19use crate::{
20 PathBuf,
21 ast::{self, Ident, types},
22 parser::ModuleParser,
23 sema::{LimitKind, SemanticAnalysisError},
24};
25
26#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
35#[cfg_attr(
36 all(feature = "arbitrary", test),
37 miden_test_serde_macros::serde_test(binary_serde(true), serde_test(false))
38)]
39#[repr(u8)]
40pub enum ModuleKind {
41 #[default]
48 Library = 0,
49 Executable = 1,
58 Kernel = 2,
67}
68
69impl ModuleKind {
70 pub fn is_executable(&self) -> bool {
71 matches!(self, Self::Executable)
72 }
73
74 pub fn is_kernel(&self) -> bool {
75 matches!(self, Self::Kernel)
76 }
77
78 pub fn is_library(&self) -> bool {
79 matches!(self, Self::Library)
80 }
81}
82
83impl fmt::Display for ModuleKind {
84 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 match self {
86 Self::Library => f.write_str("library"),
87 Self::Executable => f.write_str("executable"),
88 Self::Kernel => f.write_str("kernel"),
89 }
90 }
91}
92
93#[cfg(feature = "arbitrary")]
94impl Arbitrary for ModuleKind {
95 type Parameters = ();
96 type Strategy = BoxedStrategy<Self>;
97
98 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
99 any::<u8>()
100 .prop_map(|tag| match tag % 3 {
101 0 => Self::Library,
102 1 => Self::Executable,
103 _ => Self::Kernel,
104 })
105 .boxed()
106 }
107}
108
109impl Serializable for ModuleKind {
110 fn write_into<W: ByteWriter>(&self, target: &mut W) {
111 target.write_u8(*self as u8)
112 }
113}
114
115impl Deserializable for ModuleKind {
116 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
117 match source.read_u8()? {
118 0 => Ok(Self::Library),
119 1 => Ok(Self::Executable),
120 2 => Ok(Self::Kernel),
121 n => Err(DeserializationError::InvalidValue(format!("invalid module kind tag: {n}"))),
122 }
123 }
124}
125
126#[derive(Clone)]
134pub struct Module {
135 span: SourceSpan,
137 docs: Option<DocString>,
143 path: PathBuf,
145 kind: ModuleKind,
147 pub(crate) items: Vec<Export>,
149 pub(crate) advice_map: AdviceMap,
151}
152
153impl Module {
155 pub const FILE_EXTENSION: &'static str = "masm";
157
158 pub const ROOT: &'static str = "mod";
160
161 pub const ROOT_FILENAME: &'static str = "mod.masm";
163}
164
165impl Module {
167 pub fn new(kind: ModuleKind, path: impl AsRef<Path>) -> Self {
170 let path = path.as_ref().to_absolute().into_owned();
171 Self {
172 span: Default::default(),
173 docs: None,
174 path,
175 kind,
176 items: Default::default(),
177 advice_map: Default::default(),
178 }
179 }
180
181 pub fn new_kernel() -> Self {
183 Self::new(ModuleKind::Kernel, Path::kernel_path())
184 }
185
186 pub fn new_executable() -> Self {
188 Self::new(ModuleKind::Executable, Path::exec_path())
189 }
190
191 pub fn with_span(mut self, span: SourceSpan) -> Self {
194 self.span = span;
195 self
196 }
197
198 pub fn set_path(&mut self, path: impl AsRef<Path>) {
200 self.path = path.as_ref().to_path_buf();
201 }
202
203 pub fn set_parent(&mut self, ns: impl AsRef<Path>) {
208 self.path.set_parent(ns.as_ref());
209 }
210
211 pub fn set_docs(&mut self, docs: Option<Span<String>>) {
213 self.docs = docs.map(DocString::new);
214 }
215
216 pub fn set_span(&mut self, span: SourceSpan) {
218 self.span = span;
219 }
220
221 fn ensure_item_capacity(&self, span: SourceSpan) -> Result<(), SemanticAnalysisError> {
222 if self.items.len() >= ItemIndex::MAX_ITEMS {
223 return Err(SemanticAnalysisError::LimitExceeded { span, kind: LimitKind::Items });
224 }
225
226 Ok(())
227 }
228
229 pub(crate) fn push_export(&mut self, item: Export) -> Result<(), SemanticAnalysisError> {
230 self.ensure_item_capacity(item.span())?;
231 self.items.push(item);
232 Ok(())
233 }
234
235 pub fn define_constant(&mut self, constant: Constant) -> Result<(), SemanticAnalysisError> {
237 if let Some(prev) = self.items.iter().find(|item| item.name() == &constant.name) {
238 return Err(SemanticAnalysisError::SymbolConflict {
239 span: constant.span,
240 prev_span: prev.span(),
241 });
242 }
243 self.push_export(Export::Constant(constant))?;
244 Ok(())
245 }
246
247 pub fn define_type(&mut self, ty: TypeAlias) -> Result<(), SemanticAnalysisError> {
249 if let Some(prev) = self.items.iter().find(|item| item.name() == ty.name()) {
250 return Err(SemanticAnalysisError::SymbolConflict {
251 span: ty.span(),
252 prev_span: prev.span(),
253 });
254 }
255 self.push_export(Export::Type(ty.into()))?;
256 Ok(())
257 }
258
259 pub fn define_enum(&mut self, ty: EnumType) -> Result<(), SemanticAnalysisError> {
269 let repr = ty.ty().clone();
270
271 if !repr.is_integer() {
272 return Err(SemanticAnalysisError::InvalidEnumRepr { span: ty.span() });
273 }
274
275 if !ty.is_c_like() {
277 self.items.push(Export::Type(ty.into()));
278 return Ok(());
279 }
280
281 let export = ty.clone();
282
283 let (alias, variants) = ty.into_parts();
284
285 if let Some(prev) = self.items.iter().find(|t| t.name() == &alias.name) {
286 return Err(SemanticAnalysisError::SymbolConflict {
287 span: alias.span(),
288 prev_span: prev.span(),
289 });
290 }
291
292 let mut values = SmallVec::<[Span<u64>; 8]>::new_const();
293
294 for variant in variants {
295 let value = match &variant.discriminant {
297 ast::ConstantExpr::Int(value) => (*value).map(|v| v.as_int()),
298 expr => {
299 return Err(SemanticAnalysisError::InvalidEnumDiscriminant {
300 span: expr.span(),
301 repr,
302 });
303 },
304 };
305 if let Some(prev) = values.iter().find(|v| *v == &value) {
306 return Err(SemanticAnalysisError::EnumDiscriminantConflict {
307 span: value.span(),
308 prev: prev.span(),
309 });
310 } else {
311 values.push(value);
312 }
313
314 variant.assert_instance_of(&repr)?;
316
317 let Variant {
318 span,
319 docs,
320 name,
321 value_ty: _,
322 discriminant,
323 } = variant;
324
325 self.define_constant(Constant {
326 span,
327 docs,
328 visibility: alias.visibility(),
329 name,
330 value: discriminant,
331 })?;
332 }
333
334 self.push_export(Export::Type(export.into()))?;
335
336 Ok(())
337 }
338
339 pub fn define_procedure(
342 &mut self,
343 procedure: Procedure,
344 _source_manager: Arc<dyn SourceManager>,
345 ) -> Result<(), SemanticAnalysisError> {
346 if let Some(prev) =
347 self.items.iter().find(|item| item.name().as_str() == procedure.name().as_str())
348 {
349 return Err(SemanticAnalysisError::SymbolConflict {
350 span: procedure.span(),
351 prev_span: prev.name().span(),
352 });
353 }
354 self.push_export(Export::Procedure(procedure))?;
355 Ok(())
356 }
357
358 pub fn define_alias(
361 &mut self,
362 item: Alias,
363 _source_manager: Arc<dyn SourceManager>,
364 ) -> Result<(), SemanticAnalysisError> {
365 if self.is_kernel() && item.visibility().is_public() {
366 return Err(SemanticAnalysisError::ReexportFromKernel { span: item.span() });
367 }
368 if let Some(prev) = self
369 .items
370 .iter()
371 .find(|existing| existing.name().as_str() == item.name().as_str())
372 {
373 return Err(SemanticAnalysisError::SymbolConflict {
374 span: item.name().span(),
375 prev_span: prev.name().span(),
376 });
377 }
378 self.push_export(Export::Alias(item))?;
379 Ok(())
380 }
381}
382
383impl Module {
385 pub fn parse(
387 name: impl AsRef<Path>,
388 kind: ModuleKind,
389 source_file: Arc<SourceFile>,
390 source_manager: Arc<dyn SourceManager>,
391 ) -> Result<Box<Self>, Report> {
392 let name = name.as_ref();
393 let mut parser = Self::parser(kind);
394 parser.parse(name, source_file, source_manager)
395 }
396
397 pub fn parser(kind: ModuleKind) -> ModuleParser {
399 ModuleParser::new(kind)
400 }
401}
402
403impl Module {
405 pub fn name(&self) -> &str {
408 self.path.last().expect("non-empty module path")
409 }
410
411 pub fn path(&self) -> &Path {
413 &self.path
414 }
415
416 pub fn parent(&self) -> Option<&Path> {
418 self.path.parent()
419 }
420
421 pub fn is_in_namespace(&self, namespace: &Path) -> bool {
423 self.path.starts_with(namespace)
424 }
425
426 pub fn docs(&self) -> Option<Span<&str>> {
429 self.docs.as_ref().map(|spanned| spanned.as_spanned_str())
430 }
431
432 pub fn kind(&self) -> ModuleKind {
436 self.kind
437 }
438
439 pub fn set_kind(&mut self, kind: ModuleKind) {
443 self.kind = kind;
444 }
445
446 #[inline(always)]
448 pub fn is_executable(&self) -> bool {
449 self.kind.is_executable()
450 }
451
452 #[inline(always)]
454 pub fn is_kernel(&self) -> bool {
455 self.kind.is_kernel() && self.path.is_kernel_path()
456 }
457
458 #[inline(always)]
460 pub fn is_in_kernel(&self) -> bool {
461 self.kind.is_kernel()
462 }
463
464 pub fn has_entrypoint(&self) -> bool {
467 self.index_of(Export::is_main).is_some()
468 }
469
470 pub fn advice_map(&self) -> &AdviceMap {
472 &self.advice_map
473 }
474
475 pub fn constants(&self) -> impl Iterator<Item = &Constant> + '_ {
477 self.items.iter().filter_map(|item| match item {
478 Export::Constant(item) => Some(item),
479 _ => None,
480 })
481 }
482
483 pub fn constants_mut(&mut self) -> impl Iterator<Item = &mut Constant> + '_ {
485 self.items.iter_mut().filter_map(|item| match item {
486 Export::Constant(item) => Some(item),
487 _ => None,
488 })
489 }
490
491 pub fn types(&self) -> impl Iterator<Item = &TypeDecl> + '_ {
493 self.items.iter().filter_map(|item| match item {
494 Export::Type(item) => Some(item),
495 _ => None,
496 })
497 }
498
499 pub fn types_mut(&mut self) -> impl Iterator<Item = &mut TypeDecl> + '_ {
501 self.items.iter_mut().filter_map(|item| match item {
502 Export::Type(item) => Some(item),
503 _ => None,
504 })
505 }
506
507 pub fn procedures(&self) -> impl Iterator<Item = &Procedure> + '_ {
509 self.items.iter().filter_map(|item| match item {
510 Export::Procedure(item) => Some(item),
511 _ => None,
512 })
513 }
514
515 pub fn procedures_mut(&mut self) -> impl Iterator<Item = &mut Procedure> + '_ {
517 self.items.iter_mut().filter_map(|item| match item {
518 Export::Procedure(item) => Some(item),
519 _ => None,
520 })
521 }
522
523 pub fn aliases(&self) -> impl Iterator<Item = &Alias> + '_ {
525 self.items.iter().filter_map(|item| match item {
526 Export::Alias(item) => Some(item),
527 _ => None,
528 })
529 }
530
531 pub fn aliases_mut(&mut self) -> impl Iterator<Item = &mut Alias> + '_ {
533 self.items.iter_mut().filter_map(|item| match item {
534 Export::Alias(item) => Some(item),
535 _ => None,
536 })
537 }
538
539 pub fn items(&self) -> &[Export] {
541 &self.items
542 }
543
544 pub fn items_mut(&mut self) -> &mut Vec<Export> {
546 &mut self.items
547 }
548
549 pub fn exported(&self) -> impl Iterator<Item = (ItemIndex, QualifiedProcedureName)> + '_ {
553 self.items.iter().enumerate().filter_map(|(idx, item)| {
554 if !item.visibility().is_public() {
556 return None;
557 }
558
559 let idx = ItemIndex::new(idx);
560 let name = ProcedureName::from_raw_parts(item.name().clone());
561 let fqn = QualifiedProcedureName::new(self.path.clone(), name);
562
563 Some((idx, fqn))
564 })
565 }
566
567 pub fn procedure_signature(&self, id: ItemIndex) -> Option<&FunctionType> {
569 self.items[id.as_usize()].signature()
570 }
571
572 pub fn get(&self, index: ItemIndex) -> Option<&Export> {
577 self.items.get(index.as_usize())
578 }
579
580 pub fn index_of<F>(&self, predicate: F) -> Option<ItemIndex>
583 where
584 F: FnMut(&Export) -> bool,
585 {
586 self.items.iter().position(predicate).map(ItemIndex::new)
587 }
588
589 pub fn index_of_name(&self, name: &Ident) -> Option<ItemIndex> {
594 self.index_of(|item| item.name() == name && item.visibility().is_public())
595 }
596
597 pub fn resolve(
599 &self,
600 name: Span<&str>,
601 source_manager: Arc<dyn SourceManager>,
602 ) -> Result<SymbolResolution, SymbolResolutionError> {
603 let resolver = self.resolver(source_manager)?;
604 resolver.resolve(name)
605 }
606
607 pub fn resolve_path(
609 &self,
610 path: Span<&Path>,
611 source_manager: Arc<dyn SourceManager>,
612 ) -> Result<SymbolResolution, SymbolResolutionError> {
613 let resolver = self.resolver(source_manager)?;
614 resolver.resolve_path(path)
615 }
616
617 #[inline]
619 pub fn resolver(
620 &self,
621 source_manager: Arc<dyn SourceManager>,
622 ) -> Result<LocalSymbolResolver, SymbolResolutionError> {
623 LocalSymbolResolver::new(self, source_manager)
624 }
625
626 pub fn get_import(&self, module_name: &str) -> Option<&Alias> {
628 self.items.iter().find_map(|item| match item {
629 Export::Alias(item) if item.name().as_str() == module_name => Some(item),
630 _ => None,
631 })
632 }
633
634 pub fn get_import_mut(&mut self, module_name: &str) -> Option<&mut Alias> {
636 self.items.iter_mut().find_map(|item| match item {
637 Export::Alias(item) if item.name().as_str() == module_name => Some(item),
638 _ => None,
639 })
640 }
641
642 pub fn resolve_type(
644 &self,
645 ty: &ast::TypeExpr,
646 source_manager: Arc<dyn SourceManager>,
647 ) -> Result<Option<types::Type>, SymbolResolutionError> {
648 let mut type_resolver = self.type_resolver(source_manager)?;
649 type_resolver.resolve(ty)
650 }
651
652 pub fn type_resolver(
654 &self,
655 source_manager: Arc<dyn SourceManager>,
656 ) -> Result<impl TypeResolver<SymbolResolutionError> + '_, SymbolResolutionError> {
657 ModuleTypeResolver::new(self, source_manager)
658 }
659}
660
661impl core::ops::Index<ItemIndex> for Module {
662 type Output = Export;
663
664 #[inline]
665 fn index(&self, index: ItemIndex) -> &Self::Output {
666 &self.items[index.as_usize()]
667 }
668}
669
670impl core::ops::IndexMut<ItemIndex> for Module {
671 #[inline]
672 fn index_mut(&mut self, index: ItemIndex) -> &mut Self::Output {
673 &mut self.items[index.as_usize()]
674 }
675}
676
677impl Spanned for Module {
678 fn span(&self) -> SourceSpan {
679 self.span
680 }
681}
682
683impl Eq for Module {}
684
685impl PartialEq for Module {
686 fn eq(&self, other: &Self) -> bool {
687 self.kind == other.kind
688 && self.path == other.path
689 && self.docs == other.docs
690 && self.items == other.items
691 }
692}
693
694impl fmt::Debug for Module {
696 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
697 f.debug_struct("Module")
698 .field("docs", &self.docs)
699 .field("path", &self.path)
700 .field("kind", &self.kind)
701 .field("items", &self.items)
702 .finish()
703 }
704}
705
706impl fmt::Display for Module {
710 #[inline]
715 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
716 use crate::prettier::PrettyPrint;
717
718 self.pretty_print(f)
719 }
720}
721
722impl crate::prettier::PrettyPrint for Module {
724 fn render(&self) -> crate::prettier::Document {
725 use crate::prettier::*;
726
727 let mut doc = self
728 .docs
729 .as_ref()
730 .map(|docstring| docstring.render() + nl())
731 .unwrap_or(Document::Empty);
732
733 for (item_index, item) in self.items.iter().enumerate() {
734 if item_index > 0 {
735 doc += nl();
736 }
737 doc += item.render();
738 }
739
740 doc
741 }
742}
743
744struct ModuleTypeResolver<'a> {
745 module: &'a Module,
746 resolver: LocalSymbolResolver,
747}
748
749impl<'a> ModuleTypeResolver<'a> {
750 pub fn new(
751 module: &'a Module,
752 source_manager: Arc<dyn SourceManager>,
753 ) -> Result<Self, SymbolResolutionError> {
754 let resolver = module.resolver(source_manager)?;
755 Ok(Self { module, resolver })
756 }
757}
758
759impl TypeResolver<SymbolResolutionError> for ModuleTypeResolver<'_> {
760 fn source_manager(&self) -> Arc<dyn SourceManager> {
761 self.resolver.source_manager()
762 }
763 fn get_type(
764 &mut self,
765 context: SourceSpan,
766 _gid: GlobalItemIndex,
767 ) -> Result<types::Type, SymbolResolutionError> {
768 Err(SymbolResolutionError::undefined(context, &self.resolver.source_manager()))
769 }
770 fn get_local_type(
771 &mut self,
772 context: SourceSpan,
773 id: ItemIndex,
774 ) -> Result<Option<types::Type>, SymbolResolutionError> {
775 match &self.module[id] {
776 Export::Type(ty) => match ty {
777 TypeDecl::Alias(ty) => self.resolve(&ty.ty),
778 TypeDecl::Enum(ty) => Ok(Some(ty.ty().clone())),
779 },
780 item => Err(self.resolve_local_failed(SymbolResolutionError::invalid_symbol_type(
781 context,
782 "type",
783 item.span(),
784 &self.resolver.source_manager(),
785 ))),
786 }
787 }
788 #[inline(always)]
789 fn resolve_local_failed(&self, err: SymbolResolutionError) -> SymbolResolutionError {
790 err
791 }
792 fn resolve_type_ref(
793 &mut self,
794 path: Span<&Path>,
795 ) -> Result<SymbolResolution, SymbolResolutionError> {
796 self.resolver.resolve_path(path)
797 }
798}