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;
10use smallvec::SmallVec;
11
12use super::{
13 Alias, Constant, DocString, EnumType, Export, FunctionType, GlobalItemIndex, ItemIndex,
14 LocalSymbolResolver, Path, Procedure, ProcedureName, QualifiedProcedureName, SymbolResolution,
15 SymbolResolutionError, TypeAlias, TypeDecl, TypeResolver, Variant,
16};
17use crate::{
18 PathBuf,
19 ast::{self, Ident, types},
20 parser::ModuleParser,
21 sema::SemanticAnalysisError,
22};
23
24#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
33#[repr(u8)]
34pub enum ModuleKind {
35 #[default]
42 Library = 0,
43 Executable = 1,
52 Kernel = 2,
61}
62
63impl ModuleKind {
64 pub fn is_executable(&self) -> bool {
65 matches!(self, Self::Executable)
66 }
67
68 pub fn is_kernel(&self) -> bool {
69 matches!(self, Self::Kernel)
70 }
71
72 pub fn is_library(&self) -> bool {
73 matches!(self, Self::Library)
74 }
75}
76
77impl fmt::Display for ModuleKind {
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79 match self {
80 Self::Library => f.write_str("library"),
81 Self::Executable => f.write_str("executable"),
82 Self::Kernel => f.write_str("kernel"),
83 }
84 }
85}
86
87impl Serializable for ModuleKind {
88 fn write_into<W: ByteWriter>(&self, target: &mut W) {
89 target.write_u8(*self as u8)
90 }
91}
92
93impl Deserializable for ModuleKind {
94 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
95 match source.read_u8()? {
96 0 => Ok(Self::Library),
97 1 => Ok(Self::Executable),
98 2 => Ok(Self::Kernel),
99 n => Err(DeserializationError::InvalidValue(format!("invalid module kind tag: {n}"))),
100 }
101 }
102}
103
104#[derive(Clone)]
112pub struct Module {
113 span: SourceSpan,
115 docs: Option<DocString>,
121 path: PathBuf,
123 kind: ModuleKind,
125 pub(crate) items: Vec<Export>,
127 pub(crate) advice_map: AdviceMap,
129}
130
131impl Module {
133 pub const FILE_EXTENSION: &'static str = "masm";
135
136 pub const ROOT: &'static str = "mod";
138
139 pub const ROOT_FILENAME: &'static str = "mod.masm";
141}
142
143impl Module {
145 pub fn new(kind: ModuleKind, path: impl AsRef<Path>) -> Self {
148 let path = path.as_ref().to_absolute().into_owned();
149 Self {
150 span: Default::default(),
151 docs: None,
152 path,
153 kind,
154 items: Default::default(),
155 advice_map: Default::default(),
156 }
157 }
158
159 pub fn new_kernel() -> Self {
161 Self::new(ModuleKind::Kernel, Path::kernel_path())
162 }
163
164 pub fn new_executable() -> Self {
166 Self::new(ModuleKind::Executable, Path::exec_path())
167 }
168
169 pub fn with_span(mut self, span: SourceSpan) -> Self {
172 self.span = span;
173 self
174 }
175
176 pub fn set_path(&mut self, path: impl AsRef<Path>) {
178 self.path = path.as_ref().to_path_buf();
179 }
180
181 pub fn set_parent(&mut self, ns: impl AsRef<Path>) {
186 self.path.set_parent(ns.as_ref());
187 }
188
189 pub fn set_docs(&mut self, docs: Option<Span<String>>) {
191 self.docs = docs.map(DocString::new);
192 }
193
194 pub fn set_span(&mut self, span: SourceSpan) {
196 self.span = span;
197 }
198
199 pub fn define_constant(&mut self, constant: Constant) -> Result<(), SemanticAnalysisError> {
201 for item in self.items.iter() {
202 if let Export::Constant(c) = item
203 && c.name == constant.name
204 {
205 return Err(SemanticAnalysisError::SymbolConflict {
206 span: constant.span,
207 prev_span: c.span,
208 });
209 }
210 }
211 self.items.push(Export::Constant(constant));
212 Ok(())
213 }
214
215 pub fn define_type(&mut self, ty: TypeAlias) -> Result<(), SemanticAnalysisError> {
217 for item in self.items.iter() {
218 if let Export::Type(t) = item
219 && t.name() == ty.name()
220 {
221 return Err(SemanticAnalysisError::SymbolConflict {
222 span: ty.span(),
223 prev_span: t.span(),
224 });
225 }
226 }
227 self.items.push(Export::Type(ty.into()));
228 Ok(())
229 }
230
231 pub fn define_enum(&mut self, ty: EnumType) -> Result<(), SemanticAnalysisError> {
241 let repr = ty.ty().clone();
242
243 if !repr.is_integer() {
244 return Err(SemanticAnalysisError::InvalidEnumRepr { span: ty.span() });
245 }
246
247 if ty.is_c_like() {
249 self.items.push(Export::Type(ty.into()));
250 return Ok(());
251 }
252
253 let export = ty.clone();
254
255 let (alias, variants) = ty.into_parts();
256
257 if let Some(prev) = self.items.iter().find(|t| t.name() == &alias.name) {
258 return Err(SemanticAnalysisError::SymbolConflict {
259 span: alias.span(),
260 prev_span: prev.span(),
261 });
262 }
263
264 let mut values = SmallVec::<[Span<u64>; 8]>::new_const();
265
266 for variant in variants {
267 let value = match &variant.discriminant {
269 ast::ConstantExpr::Int(value) => (*value).map(|v| v.as_int()),
270 expr => {
271 return Err(SemanticAnalysisError::InvalidEnumDiscriminant {
272 span: expr.span(),
273 repr,
274 });
275 },
276 };
277 if let Some(prev) = values.iter().find(|v| *v == &value) {
278 return Err(SemanticAnalysisError::EnumDiscriminantConflict {
279 span: value.span(),
280 prev: prev.span(),
281 });
282 } else {
283 values.push(value);
284 }
285
286 variant.assert_instance_of(&repr)?;
288
289 let Variant {
290 span,
291 docs,
292 name,
293 value_ty: _,
294 discriminant,
295 } = variant;
296
297 self.define_constant(Constant {
298 span,
299 docs,
300 visibility: alias.visibility(),
301 name,
302 value: discriminant,
303 })?;
304 }
305
306 self.items.push(Export::Type(export.into()));
307
308 Ok(())
309 }
310
311 pub fn define_procedure(
314 &mut self,
315 procedure: Procedure,
316 source_manager: Arc<dyn SourceManager>,
317 ) -> Result<(), SemanticAnalysisError> {
318 let name = procedure.name();
319 let name = Span::new(name.span(), name.as_str());
320 if let Ok(prev) = self.resolve(name, source_manager) {
321 let prev_span = prev.span();
322 Err(SemanticAnalysisError::SymbolConflict { span: procedure.span(), prev_span })
323 } else {
324 self.items.push(Export::Procedure(procedure));
325 Ok(())
326 }
327 }
328
329 pub fn define_alias(
332 &mut self,
333 item: Alias,
334 source_manager: Arc<dyn SourceManager>,
335 ) -> Result<(), SemanticAnalysisError> {
336 if self.is_kernel() && item.visibility().is_public() {
337 return Err(SemanticAnalysisError::ReexportFromKernel { span: item.span() });
338 }
339 let name = item.name();
340 let name = Span::new(name.span(), name.as_str());
341 if let Ok(prev) = self.resolve(name, source_manager) {
342 let prev_span = prev.span();
343 Err(SemanticAnalysisError::SymbolConflict { span: item.span(), prev_span })
344 } else {
345 self.items.push(Export::Alias(item));
346 Ok(())
347 }
348 }
349}
350
351impl Module {
353 pub fn parse(
355 name: impl AsRef<Path>,
356 kind: ModuleKind,
357 source_file: Arc<SourceFile>,
358 source_manager: Arc<dyn SourceManager>,
359 ) -> Result<Box<Self>, Report> {
360 let name = name.as_ref();
361 let mut parser = Self::parser(kind);
362 parser.parse(name, source_file, source_manager)
363 }
364
365 pub fn parser(kind: ModuleKind) -> ModuleParser {
367 ModuleParser::new(kind)
368 }
369}
370
371impl Module {
373 pub fn name(&self) -> &str {
376 self.path.last().expect("non-empty module path")
377 }
378
379 pub fn path(&self) -> &Path {
381 &self.path
382 }
383
384 pub fn parent(&self) -> Option<&Path> {
386 self.path.parent()
387 }
388
389 pub fn is_in_namespace(&self, namespace: &Path) -> bool {
391 self.path.starts_with(namespace)
392 }
393
394 pub fn docs(&self) -> Option<Span<&str>> {
397 self.docs.as_ref().map(|spanned| spanned.as_spanned_str())
398 }
399
400 pub fn kind(&self) -> ModuleKind {
404 self.kind
405 }
406
407 pub fn set_kind(&mut self, kind: ModuleKind) {
411 self.kind = kind;
412 }
413
414 #[inline(always)]
416 pub fn is_executable(&self) -> bool {
417 self.kind.is_executable()
418 }
419
420 #[inline(always)]
422 pub fn is_kernel(&self) -> bool {
423 self.kind.is_kernel() && self.path.is_kernel_path()
424 }
425
426 #[inline(always)]
428 pub fn is_in_kernel(&self) -> bool {
429 self.kind.is_kernel()
430 }
431
432 pub fn has_entrypoint(&self) -> bool {
435 self.index_of(|p| p.is_main()).is_some()
436 }
437
438 pub fn advice_map(&self) -> &AdviceMap {
440 &self.advice_map
441 }
442
443 pub fn constants(&self) -> impl Iterator<Item = &Constant> + '_ {
445 self.items.iter().filter_map(|item| match item {
446 Export::Constant(item) => Some(item),
447 _ => None,
448 })
449 }
450
451 pub fn constants_mut(&mut self) -> impl Iterator<Item = &mut Constant> + '_ {
453 self.items.iter_mut().filter_map(|item| match item {
454 Export::Constant(item) => Some(item),
455 _ => None,
456 })
457 }
458
459 pub fn types(&self) -> impl Iterator<Item = &TypeDecl> + '_ {
461 self.items.iter().filter_map(|item| match item {
462 Export::Type(item) => Some(item),
463 _ => None,
464 })
465 }
466
467 pub fn types_mut(&mut self) -> impl Iterator<Item = &mut TypeDecl> + '_ {
469 self.items.iter_mut().filter_map(|item| match item {
470 Export::Type(item) => Some(item),
471 _ => None,
472 })
473 }
474
475 pub fn procedures(&self) -> impl Iterator<Item = &Procedure> + '_ {
477 self.items.iter().filter_map(|item| match item {
478 Export::Procedure(item) => Some(item),
479 _ => None,
480 })
481 }
482
483 pub fn procedures_mut(&mut self) -> impl Iterator<Item = &mut Procedure> + '_ {
485 self.items.iter_mut().filter_map(|item| match item {
486 Export::Procedure(item) => Some(item),
487 _ => None,
488 })
489 }
490
491 pub fn aliases(&self) -> impl Iterator<Item = &Alias> + '_ {
493 self.items.iter().filter_map(|item| match item {
494 Export::Alias(item) => Some(item),
495 _ => None,
496 })
497 }
498
499 pub fn aliases_mut(&mut self) -> impl Iterator<Item = &mut Alias> + '_ {
501 self.items.iter_mut().filter_map(|item| match item {
502 Export::Alias(item) => Some(item),
503 _ => None,
504 })
505 }
506
507 pub fn items(&self) -> &[Export] {
509 &self.items
510 }
511
512 pub fn items_mut(&mut self) -> &mut Vec<Export> {
514 &mut self.items
515 }
516
517 pub fn exported(&self) -> impl Iterator<Item = (ItemIndex, QualifiedProcedureName)> + '_ {
521 self.items.iter().enumerate().filter_map(|(idx, item)| {
522 if !item.visibility().is_public() {
524 return None;
525 }
526
527 let idx = ItemIndex::new(idx);
528 let name = ProcedureName::from_raw_parts(item.name().clone());
529 let fqn = QualifiedProcedureName::new(self.path.clone(), name);
530
531 Some((idx, fqn))
532 })
533 }
534
535 pub fn procedure_signature(&self, id: ItemIndex) -> Option<&FunctionType> {
537 self.items[id.as_usize()].signature()
538 }
539
540 pub fn get(&self, index: ItemIndex) -> Option<&Export> {
545 self.items.get(index.as_usize())
546 }
547
548 pub fn index_of<F>(&self, predicate: F) -> Option<ItemIndex>
551 where
552 F: FnMut(&Export) -> bool,
553 {
554 self.items.iter().position(predicate).map(ItemIndex::new)
555 }
556
557 pub fn index_of_name(&self, name: &Ident) -> Option<ItemIndex> {
562 self.index_of(|item| item.name() == name && item.visibility().is_public())
563 }
564
565 pub fn resolve(
567 &self,
568 name: Span<&str>,
569 source_manager: Arc<dyn SourceManager>,
570 ) -> Result<SymbolResolution, SymbolResolutionError> {
571 let resolver = self.resolver(source_manager);
572 resolver.resolve(name)
573 }
574
575 pub fn resolve_path(
577 &self,
578 path: Span<&Path>,
579 source_manager: Arc<dyn SourceManager>,
580 ) -> Result<SymbolResolution, SymbolResolutionError> {
581 let resolver = self.resolver(source_manager);
582 resolver.resolve_path(path)
583 }
584
585 #[inline]
587 pub fn resolver(&self, source_manager: Arc<dyn SourceManager>) -> LocalSymbolResolver {
588 LocalSymbolResolver::new(self, source_manager)
589 }
590
591 pub fn get_import(&self, module_name: &str) -> Option<&Alias> {
593 self.items.iter().find_map(|item| match item {
594 Export::Alias(item) if item.name().as_str() == module_name => Some(item),
595 _ => None,
596 })
597 }
598
599 pub fn get_import_mut(&mut self, module_name: &str) -> Option<&mut Alias> {
601 self.items.iter_mut().find_map(|item| match item {
602 Export::Alias(item) if item.name().as_str() == module_name => Some(item),
603 _ => None,
604 })
605 }
606
607 pub fn resolve_type(
609 &self,
610 ty: &ast::TypeExpr,
611 source_manager: Arc<dyn SourceManager>,
612 ) -> Result<Option<types::Type>, SymbolResolutionError> {
613 let mut type_resolver = ModuleTypeResolver::new(self, source_manager);
614 type_resolver.resolve(ty)
615 }
616
617 pub fn type_resolver(
619 &self,
620 source_manager: Arc<dyn SourceManager>,
621 ) -> impl TypeResolver<SymbolResolutionError> + '_ {
622 ModuleTypeResolver::new(self, source_manager)
623 }
624}
625
626impl core::ops::Index<ItemIndex> for Module {
627 type Output = Export;
628
629 #[inline]
630 fn index(&self, index: ItemIndex) -> &Self::Output {
631 &self.items[index.as_usize()]
632 }
633}
634
635impl core::ops::IndexMut<ItemIndex> for Module {
636 #[inline]
637 fn index_mut(&mut self, index: ItemIndex) -> &mut Self::Output {
638 &mut self.items[index.as_usize()]
639 }
640}
641
642impl Spanned for Module {
643 fn span(&self) -> SourceSpan {
644 self.span
645 }
646}
647
648impl Eq for Module {}
649
650impl PartialEq for Module {
651 fn eq(&self, other: &Self) -> bool {
652 self.kind == other.kind
653 && self.path == other.path
654 && self.docs == other.docs
655 && self.items == other.items
656 }
657}
658
659impl fmt::Debug for Module {
661 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
662 f.debug_struct("Module")
663 .field("docs", &self.docs)
664 .field("path", &self.path)
665 .field("kind", &self.kind)
666 .field("items", &self.items)
667 .finish()
668 }
669}
670
671impl fmt::Display for Module {
675 #[inline]
680 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
681 use crate::prettier::PrettyPrint;
682
683 self.pretty_print(f)
684 }
685}
686
687impl crate::prettier::PrettyPrint for Module {
689 fn render(&self) -> crate::prettier::Document {
690 use crate::prettier::*;
691
692 let mut doc = self
693 .docs
694 .as_ref()
695 .map(|docstring| docstring.render() + nl())
696 .unwrap_or(Document::Empty);
697
698 for (item_index, item) in self.items.iter().enumerate() {
699 if item_index > 0 {
700 doc += nl();
701 }
702 doc += item.render();
703 }
704
705 doc
706 }
707}
708
709struct ModuleTypeResolver<'a> {
710 module: &'a Module,
711 resolver: LocalSymbolResolver,
712}
713
714impl<'a> ModuleTypeResolver<'a> {
715 pub fn new(module: &'a Module, source_manager: Arc<dyn SourceManager>) -> Self {
716 let resolver = module.resolver(source_manager);
717 Self { module, resolver }
718 }
719}
720
721impl TypeResolver<SymbolResolutionError> for ModuleTypeResolver<'_> {
722 fn source_manager(&self) -> Arc<dyn SourceManager> {
723 self.resolver.source_manager()
724 }
725 fn get_type(
726 &mut self,
727 context: SourceSpan,
728 _gid: GlobalItemIndex,
729 ) -> Result<ast::types::Type, SymbolResolutionError> {
730 Err(SymbolResolutionError::undefined(context, &self.resolver.source_manager()))
731 }
732 fn get_local_type(
733 &mut self,
734 context: SourceSpan,
735 id: ItemIndex,
736 ) -> Result<Option<ast::types::Type>, SymbolResolutionError> {
737 match &self.module[id] {
738 super::Export::Type(ty) => match ty {
739 TypeDecl::Alias(ty) => self.resolve(&ty.ty),
740 TypeDecl::Enum(ty) => Ok(Some(ty.ty().clone())),
741 },
742 item => Err(self.resolve_local_failed(SymbolResolutionError::invalid_symbol_type(
743 context,
744 "type",
745 item.span(),
746 &self.resolver.source_manager(),
747 ))),
748 }
749 }
750 #[inline(always)]
751 fn resolve_local_failed(&self, err: SymbolResolutionError) -> SymbolResolutionError {
752 err
753 }
754 fn resolve_type_ref(
755 &mut self,
756 path: Span<&Path>,
757 ) -> Result<SymbolResolution, SymbolResolutionError> {
758 self.resolver.resolve_path(path)
759 }
760}