1use std::collections::{HashMap, HashSet};
13
14use thiserror::Error;
15
16use crate::dag_id::DagId;
17use crate::desugar::desugared_ast as ast;
18use crate::syntax::ast::{IdentPath, ImportItem, ImportItemNamespace, ImportKind, ModulePath};
19use crate::syntax::names::{
20 ConstructorName, DeclName, DimName, IndexName, IndexVariantName, ModuleAliasName, NameAtom,
21 NameDef, NameNamespace, NamePath, ResolvedIndexVariant, ResolvedName, StructTypeName, UnitName,
22 namespace,
23};
24use crate::syntax::non_empty::NonEmpty;
25use crate::syntax::phase::never;
26use crate::syntax::span::{Span, Spanned};
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub enum SymbolVisibility {
31 Private,
33 Public,
35 PublicBind,
37}
38
39impl SymbolVisibility {
40 #[must_use]
42 pub const fn is_public(self) -> bool {
43 matches!(self, Self::Public | Self::PublicBind)
44 }
45
46 #[must_use]
48 pub const fn is_bindable(self) -> bool {
49 matches!(self, Self::PublicBind)
50 }
51}
52
53impl From<ast::Visibility> for SymbolVisibility {
54 fn from(visibility: ast::Visibility) -> Self {
55 match visibility {
56 ast::Visibility::Private => Self::Private,
57 ast::Visibility::Public => Self::Public,
58 }
59 }
60}
61
62impl From<ast::BindableVisibility> for SymbolVisibility {
63 fn from(visibility: ast::BindableVisibility) -> Self {
64 match visibility {
65 ast::BindableVisibility::Private => Self::Private,
66 ast::BindableVisibility::Public => Self::Public,
67 ast::BindableVisibility::PublicBind => Self::PublicBind,
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
74pub enum DeclSymbolKind {
75 Const,
76 Param,
77 Node,
78 Assert,
79 Plot,
80 Figure,
81 Layer,
82 Dag,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86enum ExclusiveNameKind {
87 Value,
88 Dimension,
89 StructType,
90 Index,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94struct ExclusiveNameBinding {
95 kind: ExclusiveNameKind,
96 span: Span,
97}
98
99impl DeclSymbolKind {
100 #[must_use]
103 pub const fn is_const(self) -> bool {
104 matches!(self, Self::Const)
105 }
106}
107
108impl std::fmt::Display for DeclSymbolKind {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 let label = match self {
111 Self::Const => "const",
112 Self::Param => "param",
113 Self::Node => "node",
114 Self::Assert => "assert",
115 Self::Plot => "plot",
116 Self::Figure => "figure",
117 Self::Layer => "layer",
118 Self::Dag => "dag",
119 };
120 f.write_str(label)
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
126pub enum ModuleAccess {
127 PublicOnly,
129}
130
131impl ModuleAccess {
132 const fn requires_public(self) -> bool {
133 matches!(self, Self::PublicOnly)
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
139pub struct ModuleSymbol<Ns: NameNamespace> {
140 resolved: ResolvedName<Ns>,
141 visibility: SymbolVisibility,
142 span: Span,
143}
144
145impl<Ns: NameNamespace> ModuleSymbol<Ns> {
146 fn new(owner: &DagId, name: NameDef<Ns>, visibility: SymbolVisibility, span: Span) -> Self {
147 Self {
148 resolved: ResolvedName::from_def(owner.clone(), name),
149 visibility,
150 span,
151 }
152 }
153
154 #[must_use]
156 pub const fn resolved(&self) -> &ResolvedName<Ns> {
157 &self.resolved
158 }
159
160 #[must_use]
162 pub const fn visibility(&self) -> SymbolVisibility {
163 self.visibility
164 }
165
166 #[must_use]
168 pub const fn span(&self) -> Span {
169 self.span
170 }
171}
172
173trait ModuleSymbolLookup<Ns: NameNamespace> {
174 fn resolved(&self) -> &ResolvedName<Ns>;
175 fn visibility(&self) -> SymbolVisibility;
176 fn span(&self) -> Span;
177}
178
179impl<Ns: NameNamespace> ModuleSymbolLookup<Ns> for ModuleSymbol<Ns> {
180 fn resolved(&self) -> &ResolvedName<Ns> {
181 self.resolved()
182 }
183
184 fn visibility(&self) -> SymbolVisibility {
185 self.visibility()
186 }
187
188 fn span(&self) -> Span {
189 self.span()
190 }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct ModuleDeclSymbol {
196 symbol: ModuleSymbol<namespace::Decl>,
197 kind: DeclSymbolKind,
198}
199
200impl ModuleDeclSymbol {
201 fn new(
202 owner: &DagId,
203 name: DeclName,
204 visibility: SymbolVisibility,
205 span: Span,
206 kind: DeclSymbolKind,
207 ) -> Self {
208 Self {
209 symbol: ModuleSymbol::new(owner, name, visibility, span),
210 kind,
211 }
212 }
213
214 #[must_use]
216 pub const fn resolved(&self) -> &ResolvedName<namespace::Decl> {
217 self.symbol.resolved()
218 }
219
220 #[must_use]
222 pub const fn visibility(&self) -> SymbolVisibility {
223 self.symbol.visibility()
224 }
225
226 #[must_use]
228 pub const fn span(&self) -> Span {
229 self.symbol.span()
230 }
231
232 #[must_use]
234 pub const fn kind(&self) -> DeclSymbolKind {
235 self.kind
236 }
237}
238
239impl ModuleSymbolLookup<namespace::Decl> for ModuleDeclSymbol {
240 fn resolved(&self) -> &ResolvedName<namespace::Decl> {
241 self.resolved()
242 }
243
244 fn visibility(&self) -> SymbolVisibility {
245 self.visibility()
246 }
247
248 fn span(&self) -> Span {
249 self.span()
250 }
251}
252
253#[derive(Debug, Clone, PartialEq, Eq)]
255pub struct ModuleIndexSymbol {
256 symbol: ModuleSymbol<namespace::Index>,
257 variants: HashMap<IndexVariantName, Span>,
258}
259
260impl ModuleIndexSymbol {
261 #[must_use]
263 pub const fn resolved(&self) -> &ResolvedName<namespace::Index> {
264 self.symbol.resolved()
265 }
266
267 #[must_use]
269 pub const fn visibility(&self) -> SymbolVisibility {
270 self.symbol.visibility()
271 }
272
273 #[must_use]
275 pub const fn span(&self) -> Span {
276 self.symbol.span()
277 }
278
279 #[must_use]
281 pub const fn variants(&self) -> &HashMap<IndexVariantName, Span> {
282 &self.variants
283 }
284}
285
286impl ModuleSymbolLookup<namespace::Index> for ModuleIndexSymbol {
287 fn resolved(&self) -> &ResolvedName<namespace::Index> {
288 self.resolved()
289 }
290
291 fn visibility(&self) -> SymbolVisibility {
292 self.visibility()
293 }
294
295 fn span(&self) -> Span {
296 self.span()
297 }
298}
299
300#[derive(Debug, Clone, PartialEq, Eq)]
302pub struct ModuleSymbols {
303 owner: DagId,
304 decls: HashMap<DeclName, ModuleDeclSymbol>,
305 dimensions: HashMap<DimName, ModuleSymbol<namespace::Dim>>,
306 units: HashMap<UnitName, ModuleSymbol<namespace::Unit>>,
307 struct_types: HashMap<StructTypeName, ModuleSymbol<namespace::StructType>>,
308 indexes: HashMap<IndexName, ModuleIndexSymbol>,
309 constructors: HashMap<ConstructorName, ModuleSymbol<namespace::Constructor>>,
310}
311
312impl ModuleSymbols {
313 pub fn from_declarations(
323 owner: DagId,
324 declarations: &[ast::Declaration],
325 ) -> Result<Self, ModuleResolveError> {
326 let mut symbols = Self {
327 owner,
328 decls: HashMap::new(),
329 dimensions: HashMap::new(),
330 units: HashMap::new(),
331 struct_types: HashMap::new(),
332 indexes: HashMap::new(),
333 constructors: HashMap::new(),
334 };
335
336 symbols.collect_declarations(declarations)?;
337 Ok(symbols)
338 }
339
340 #[must_use]
342 pub const fn owner(&self) -> &DagId {
343 &self.owner
344 }
345
346 #[must_use]
348 pub const fn decls(&self) -> &HashMap<DeclName, ModuleDeclSymbol> {
349 &self.decls
350 }
351
352 #[must_use]
354 pub const fn dimensions(&self) -> &HashMap<DimName, ModuleSymbol<namespace::Dim>> {
355 &self.dimensions
356 }
357
358 #[must_use]
360 pub const fn units(&self) -> &HashMap<UnitName, ModuleSymbol<namespace::Unit>> {
361 &self.units
362 }
363
364 #[must_use]
366 pub const fn struct_types(
367 &self,
368 ) -> &HashMap<StructTypeName, ModuleSymbol<namespace::StructType>> {
369 &self.struct_types
370 }
371
372 #[must_use]
374 pub const fn indexes(&self) -> &HashMap<IndexName, ModuleIndexSymbol> {
375 &self.indexes
376 }
377
378 #[must_use]
380 pub const fn constructors(
381 &self,
382 ) -> &HashMap<ConstructorName, ModuleSymbol<namespace::Constructor>> {
383 &self.constructors
384 }
385
386 fn collect_declarations(
387 &mut self,
388 declarations: &[ast::Declaration],
389 ) -> Result<(), ModuleResolveError> {
390 let mut exclusive_names = HashMap::new();
391 for decl in declarations {
392 match &decl.kind {
393 ast::DeclKind::Param(p) => self.insert_value_decl(
394 &mut exclusive_names,
395 &p.name,
396 SymbolVisibility::PublicBind,
397 DeclSymbolKind::Param,
398 )?,
399 ast::DeclKind::Node(n) => self.insert_value_decl(
400 &mut exclusive_names,
401 &n.name,
402 SymbolVisibility::from(n.visibility),
403 DeclSymbolKind::Node,
404 )?,
405 ast::DeclKind::ConstNode(c) => self.insert_value_decl(
406 &mut exclusive_names,
407 &c.name,
408 SymbolVisibility::from(c.visibility),
409 DeclSymbolKind::Const,
410 )?,
411 ast::DeclKind::Assert(a) => self.insert_value_decl(
412 &mut exclusive_names,
413 &a.name,
414 SymbolVisibility::from(a.visibility),
415 DeclSymbolKind::Assert,
416 )?,
417 ast::DeclKind::Plot(p) => self.insert_value_decl(
418 &mut exclusive_names,
419 &p.name,
420 SymbolVisibility::from(p.visibility),
421 DeclSymbolKind::Plot,
422 )?,
423 ast::DeclKind::Figure(f) => self.insert_value_decl(
424 &mut exclusive_names,
425 &f.name,
426 SymbolVisibility::from(f.visibility),
427 DeclSymbolKind::Figure,
428 )?,
429 ast::DeclKind::Layer(l) => self.insert_value_decl(
430 &mut exclusive_names,
431 &l.name,
432 SymbolVisibility::from(l.visibility),
433 DeclSymbolKind::Layer,
434 )?,
435 ast::DeclKind::Dag(d) => self.insert_value_decl(
436 &mut exclusive_names,
437 &d.name,
438 SymbolVisibility::from(d.visibility),
439 DeclSymbolKind::Dag,
440 )?,
441 ast::DeclKind::BaseDimension(d) => self.insert_dimension_decl(
442 &mut exclusive_names,
443 &d.name,
444 SymbolVisibility::from(d.visibility),
445 )?,
446 ast::DeclKind::Dimension(d) => self.insert_dimension_decl(
447 &mut exclusive_names,
448 &d.name,
449 SymbolVisibility::from(d.visibility),
450 )?,
451 ast::DeclKind::Unit(u) => self.insert_unit(
452 &u.name,
453 SymbolVisibility::from(u.visibility),
454 namespace::Unit::DISPLAY_NAME,
455 )?,
456 ast::DeclKind::Type(t) => self.insert_type_decl(&mut exclusive_names, t)?,
457 ast::DeclKind::Index(i) => self.insert_index_decl(&mut exclusive_names, i)?,
458 ast::DeclKind::Import(_) | ast::DeclKind::Include(_) => {}
459 #[expect(
460 clippy::uninhabited_references,
461 reason = "post-desugar Sugar payload is uninhabited by phase invariant"
462 )]
463 ast::DeclKind::Sugar(s) => never(*s),
464 }
465 }
466 Ok(())
467 }
468
469 fn insert_value_decl(
470 &mut self,
471 exclusive_names: &mut HashMap<NameAtom, ExclusiveNameBinding>,
472 name: &Spanned<DeclName>,
473 visibility: SymbolVisibility,
474 kind: DeclSymbolKind,
475 ) -> Result<(), ModuleResolveError> {
476 self.insert_exclusive_name(
477 exclusive_names,
478 name.value.atom(),
479 ExclusiveNameKind::Value,
480 name.span,
481 )?;
482 self.insert_decl(name, visibility, namespace::Decl::DISPLAY_NAME, kind)
483 }
484
485 fn insert_dimension_decl(
486 &mut self,
487 exclusive_names: &mut HashMap<NameAtom, ExclusiveNameBinding>,
488 name: &Spanned<DimName>,
489 visibility: SymbolVisibility,
490 ) -> Result<(), ModuleResolveError> {
491 self.insert_exclusive_name(
492 exclusive_names,
493 name.value.atom(),
494 ExclusiveNameKind::Dimension,
495 name.span,
496 )?;
497 self.insert_dimension(name, visibility, namespace::Dim::DISPLAY_NAME)
498 }
499
500 fn insert_type_decl(
501 &mut self,
502 exclusive_names: &mut HashMap<NameAtom, ExclusiveNameBinding>,
503 type_decl: &ast::TypeDecl,
504 ) -> Result<(), ModuleResolveError> {
505 let visibility = SymbolVisibility::from(type_decl.visibility);
506 self.insert_exclusive_name(
507 exclusive_names,
508 type_decl.name.value.atom(),
509 ExclusiveNameKind::StructType,
510 type_decl.name.span,
511 )?;
512 self.insert_struct_type(
513 &type_decl.name,
514 visibility,
515 namespace::StructType::DISPLAY_NAME,
516 )?;
517 if let ast::TypeDeclBody::Constructors(members) = &type_decl.body {
518 for member in members {
519 self.insert_constructor(
520 &member.name,
521 visibility,
522 namespace::Constructor::DISPLAY_NAME,
523 )?;
524 }
525 }
526 Ok(())
527 }
528
529 fn insert_index_decl(
530 &mut self,
531 exclusive_names: &mut HashMap<NameAtom, ExclusiveNameBinding>,
532 index: &ast::IndexDecl,
533 ) -> Result<(), ModuleResolveError> {
534 self.insert_exclusive_name(
535 exclusive_names,
536 index.name.value.atom(),
537 ExclusiveNameKind::Index,
538 index.name.span,
539 )?;
540 self.insert_index(index)
541 }
542
543 fn insert_exclusive_name(
544 &self,
545 occupied: &mut HashMap<NameAtom, ExclusiveNameBinding>,
546 atom: &NameAtom,
547 kind: ExclusiveNameKind,
548 span: Span,
549 ) -> Result<(), ModuleResolveError> {
550 match occupied.get(atom) {
551 Some(first) if first.kind != kind => Err(ModuleResolveError::DuplicateSymbol {
552 owner: self.owner.clone(),
553 namespace: "name",
554 name: atom.to_string(),
555 first: first.span,
556 duplicate: span,
557 }),
558 _ => {
559 occupied
560 .entry(atom.clone())
561 .or_insert(ExclusiveNameBinding { kind, span });
562 Ok(())
563 }
564 }
565 }
566
567 fn insert_decl(
568 &mut self,
569 name: &Spanned<DeclName>,
570 visibility: SymbolVisibility,
571 namespace_name: &'static str,
572 kind: DeclSymbolKind,
573 ) -> Result<(), ModuleResolveError> {
574 insert_decl_symbol(
575 &self.owner,
576 &mut self.decls,
577 name,
578 visibility,
579 namespace_name,
580 kind,
581 )
582 }
583
584 fn insert_dimension(
585 &mut self,
586 name: &Spanned<DimName>,
587 visibility: SymbolVisibility,
588 namespace_name: &'static str,
589 ) -> Result<(), ModuleResolveError> {
590 insert_symbol(
591 &self.owner,
592 &mut self.dimensions,
593 name,
594 visibility,
595 namespace_name,
596 )
597 }
598
599 fn insert_unit(
600 &mut self,
601 name: &Spanned<UnitName>,
602 visibility: SymbolVisibility,
603 namespace_name: &'static str,
604 ) -> Result<(), ModuleResolveError> {
605 insert_symbol(
606 &self.owner,
607 &mut self.units,
608 name,
609 visibility,
610 namespace_name,
611 )
612 }
613
614 fn insert_struct_type(
615 &mut self,
616 name: &Spanned<StructTypeName>,
617 visibility: SymbolVisibility,
618 namespace_name: &'static str,
619 ) -> Result<(), ModuleResolveError> {
620 insert_symbol(
621 &self.owner,
622 &mut self.struct_types,
623 name,
624 visibility,
625 namespace_name,
626 )
627 }
628
629 fn insert_constructor(
630 &mut self,
631 name: &Spanned<ConstructorName>,
632 visibility: SymbolVisibility,
633 namespace_name: &'static str,
634 ) -> Result<(), ModuleResolveError> {
635 insert_symbol(
636 &self.owner,
637 &mut self.constructors,
638 name,
639 visibility,
640 namespace_name,
641 )
642 }
643
644 fn insert_index(&mut self, index: &ast::IndexDecl) -> Result<(), ModuleResolveError> {
645 if let Some(first) = self.indexes.get(index.name.value.as_str()) {
646 return Err(ModuleResolveError::DuplicateSymbol {
647 owner: self.owner.clone(),
648 namespace: namespace::Index::DISPLAY_NAME,
649 name: index.name.value.to_string(),
650 first: first.span(),
651 duplicate: index.name.span,
652 });
653 }
654
655 let mut variants = HashMap::new();
656 if let ast::IndexDeclKind::Named { variants: declared } = &index.kind {
657 for variant in declared {
658 if let Some(first) = variants.insert(variant.value.clone(), variant.span) {
659 return Err(ModuleResolveError::DuplicateSymbol {
660 owner: self.owner.clone(),
661 namespace: namespace::IndexVariant::DISPLAY_NAME,
662 name: variant.value.qualified_by(&index.name.value).to_string(),
663 first,
664 duplicate: variant.span,
665 });
666 }
667 }
668 }
669
670 self.indexes.insert(
671 index.name.value.clone(),
672 ModuleIndexSymbol {
673 symbol: ModuleSymbol::new(
674 &self.owner,
675 index.name.value.clone(),
676 SymbolVisibility::from(index.visibility),
677 index.name.span,
678 ),
679 variants,
680 },
681 );
682 Ok(())
683 }
684}
685
686fn insert_symbol<Ns: NameNamespace>(
687 owner: &DagId,
688 map: &mut HashMap<NameDef<Ns>, ModuleSymbol<Ns>>,
689 name: &Spanned<NameDef<Ns>>,
690 visibility: SymbolVisibility,
691 namespace_name: &'static str,
692) -> Result<(), ModuleResolveError> {
693 if let Some(first) = map.get(name.value.as_str()) {
694 return Err(ModuleResolveError::DuplicateSymbol {
695 owner: owner.clone(),
696 namespace: namespace_name,
697 name: name.value.to_string(),
698 first: first.span(),
699 duplicate: name.span,
700 });
701 }
702 map.insert(
703 name.value.clone(),
704 ModuleSymbol::new(owner, name.value.clone(), visibility, name.span),
705 );
706 Ok(())
707}
708
709fn insert_decl_symbol(
710 owner: &DagId,
711 map: &mut HashMap<DeclName, ModuleDeclSymbol>,
712 name: &Spanned<DeclName>,
713 visibility: SymbolVisibility,
714 namespace_name: &'static str,
715 kind: DeclSymbolKind,
716) -> Result<(), ModuleResolveError> {
717 if let Some(first) = map.get(name.value.as_str()) {
718 return Err(ModuleResolveError::DuplicateSymbol {
719 owner: owner.clone(),
720 namespace: namespace_name,
721 name: name.value.to_string(),
722 first: first.span(),
723 duplicate: name.span,
724 });
725 }
726 map.insert(
727 name.value.clone(),
728 ModuleDeclSymbol::new(owner, name.value.clone(), visibility, name.span, kind),
729 );
730 Ok(())
731}
732
733#[derive(Debug, Clone, PartialEq, Eq)]
735pub struct ModuleAliasTarget {
736 target: DagId,
737 span: Span,
738 access: ModuleAccess,
739}
740
741impl ModuleAliasTarget {
742 #[must_use]
744 pub const fn target(&self) -> &DagId {
745 &self.target
746 }
747
748 #[must_use]
750 pub const fn span(&self) -> Span {
751 self.span
752 }
753
754 #[must_use]
756 pub const fn access(&self) -> ModuleAccess {
757 self.access
758 }
759}
760
761#[derive(Debug, Clone, PartialEq, Eq)]
763pub struct ImportedSymbol<Ns: NameNamespace> {
764 resolved: ResolvedName<Ns>,
765 span: Span,
766 visibility: SymbolVisibility,
767}
768
769impl<Ns: NameNamespace> ImportedSymbol<Ns> {
770 const fn new(resolved: ResolvedName<Ns>, span: Span, visibility: SymbolVisibility) -> Self {
771 Self {
772 resolved,
773 span,
774 visibility,
775 }
776 }
777
778 #[must_use]
780 pub const fn resolved(&self) -> &ResolvedName<Ns> {
781 &self.resolved
782 }
783
784 #[must_use]
786 pub const fn span(&self) -> Span {
787 self.span
788 }
789
790 #[must_use]
792 pub const fn visibility(&self) -> SymbolVisibility {
793 self.visibility
794 }
795}
796
797impl<Ns: NameNamespace> ModuleSymbolLookup<Ns> for ImportedSymbol<Ns> {
798 fn resolved(&self) -> &ResolvedName<Ns> {
799 self.resolved()
800 }
801
802 fn visibility(&self) -> SymbolVisibility {
803 self.visibility()
804 }
805
806 fn span(&self) -> Span {
807 self.span()
808 }
809}
810
811#[derive(Debug, Default, Clone, PartialEq, Eq)]
813pub struct ModuleScope {
814 module_aliases: HashMap<ModuleAliasName, ModuleAliasTarget>,
815 selected_decls: HashMap<DeclName, ImportedSymbol<namespace::Decl>>,
816 selected_dimensions: HashMap<DimName, ImportedSymbol<namespace::Dim>>,
817 selected_units: HashMap<UnitName, ImportedSymbol<namespace::Unit>>,
818 selected_struct_types: HashMap<StructTypeName, ImportedSymbol<namespace::StructType>>,
819 selected_indexes: HashMap<IndexName, ImportedSymbol<namespace::Index>>,
820 selected_constructors: HashMap<ConstructorName, ImportedSymbol<namespace::Constructor>>,
821}
822
823impl ModuleScope {
824 #[must_use]
826 pub const fn module_aliases(&self) -> &HashMap<ModuleAliasName, ModuleAliasTarget> {
827 &self.module_aliases
828 }
829}
830
831#[derive(Debug, Clone, Copy, PartialEq, Eq)]
833pub enum SurfaceNameKind {
834 Value,
835 Dimension,
836 Unit,
837 Type,
838 Index,
839 IndexLabel,
840 Constructor,
841 DefaultImportItem,
842}
843
844impl std::fmt::Display for SurfaceNameKind {
845 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
846 let text = match self {
847 Self::Value => "a value",
848 Self::Dimension => "a dimension",
849 Self::Unit => "a unit",
850 Self::Type => "a type",
851 Self::Index => "an index",
852 Self::IndexLabel => "an index label",
853 Self::Constructor => "a constructor",
854 Self::DefaultImportItem => "a default import item",
855 };
856 f.write_str(text)
857 }
858}
859
860trait ResolvableNamespace: NameNamespace {
861 const SURFACE_KIND: SurfaceNameKind;
862}
863
864impl ResolvableNamespace for namespace::Decl {
865 const SURFACE_KIND: SurfaceNameKind = SurfaceNameKind::Value;
866}
867
868impl ResolvableNamespace for namespace::Dim {
869 const SURFACE_KIND: SurfaceNameKind = SurfaceNameKind::Dimension;
870}
871
872impl ResolvableNamespace for namespace::Unit {
873 const SURFACE_KIND: SurfaceNameKind = SurfaceNameKind::Unit;
874}
875
876impl ResolvableNamespace for namespace::StructType {
877 const SURFACE_KIND: SurfaceNameKind = SurfaceNameKind::Type;
878}
879
880impl ResolvableNamespace for namespace::Index {
881 const SURFACE_KIND: SurfaceNameKind = SurfaceNameKind::Index;
882}
883
884impl ResolvableNamespace for namespace::Constructor {
885 const SURFACE_KIND: SurfaceNameKind = SurfaceNameKind::Constructor;
886}
887
888#[derive(Debug, Clone)]
889enum ImportAddition {
890 ModuleAlias {
891 alias: Spanned<ModuleAliasName>,
892 target: DagId,
893 access: ModuleAccess,
894 },
895 Decl {
896 local: Spanned<DeclName>,
897 target: ResolvedName<namespace::Decl>,
898 visibility: SymbolVisibility,
899 },
900 Dimension {
901 local: Spanned<DimName>,
902 target: ResolvedName<namespace::Dim>,
903 visibility: SymbolVisibility,
904 },
905 Unit {
906 local: Spanned<UnitName>,
907 target: ResolvedName<namespace::Unit>,
908 visibility: SymbolVisibility,
909 },
910 StructType {
911 local: Spanned<StructTypeName>,
912 target: ResolvedName<namespace::StructType>,
913 visibility: SymbolVisibility,
914 },
915 Index {
916 local: Spanned<IndexName>,
917 target: ResolvedName<namespace::Index>,
918 visibility: SymbolVisibility,
919 },
920 Constructor {
921 local: Spanned<ConstructorName>,
922 target: ResolvedName<namespace::Constructor>,
923 visibility: SymbolVisibility,
924 },
925}
926
927#[derive(Debug, Default, Clone, PartialEq, Eq)]
929pub struct ModuleResolver {
930 modules: HashMap<DagId, ModuleSymbols>,
931 scopes: HashMap<DagId, ModuleScope>,
932}
933
934#[derive(Debug, Clone, PartialEq, Eq)]
935struct ResolvedModuleQualifier {
936 owner: DagId,
937 access: ModuleAccess,
938}
939
940impl ModuleResolver {
941 pub fn from_modules<'a>(
951 modules: impl IntoIterator<Item = (DagId, &'a ast::File)>,
952 ) -> Result<Self, ModuleResolveError> {
953 let mut resolver = Self::default();
954 for (owner, file) in modules {
955 resolver.add_module(owner, &file.declarations)?;
956 }
957 Ok(resolver)
958 }
959
960 pub fn add_module(
968 &mut self,
969 owner: DagId,
970 declarations: &[ast::Declaration],
971 ) -> Result<(), ModuleResolveError> {
972 if self.modules.contains_key(&owner) {
973 return Err(ModuleResolveError::DuplicateModule { owner });
974 }
975 let symbols = ModuleSymbols::from_declarations(owner.clone(), declarations)?;
976 self.scopes.entry(owner.clone()).or_default();
977 self.modules.insert(owner, symbols);
978 Ok(())
979 }
980
981 #[must_use]
983 pub const fn modules(&self) -> &HashMap<DagId, ModuleSymbols> {
984 &self.modules
985 }
986
987 #[must_use]
989 pub const fn scopes(&self) -> &HashMap<DagId, ModuleScope> {
990 &self.scopes
991 }
992
993 pub fn register_import(
1004 &mut self,
1005 owner: &DagId,
1006 path: &ModulePath,
1007 kind: &ImportKind,
1008 target: &DagId,
1009 ) -> Result<(), ModuleResolveError> {
1010 self.register_import_with_access(owner, path, kind, target, ModuleAccess::PublicOnly)
1011 }
1012
1013 pub fn register_include(
1019 &mut self,
1020 owner: &DagId,
1021 path: &ModulePath,
1022 kind: &ImportKind,
1023 target: &DagId,
1024 ) -> Result<(), ModuleResolveError> {
1025 self.register_import_with_access(owner, path, kind, target, ModuleAccess::PublicOnly)
1026 }
1027
1028 pub fn inline_instantiated_include_indexes(
1049 &mut self,
1050 importer: &DagId,
1051 source: &DagId,
1052 bound: &HashSet<&str>,
1053 ) -> Result<(), ModuleResolveError> {
1054 let source_symbols =
1055 self.modules
1056 .get(source)
1057 .ok_or_else(|| ModuleResolveError::UnknownModule {
1058 owner: source.clone(),
1059 })?;
1060 let injected: Vec<ModuleIndexSymbol> = source_symbols
1061 .indexes
1062 .iter()
1063 .filter(|(name, _)| !bound.contains(name.as_str()))
1064 .map(|(name, symbol)| ModuleIndexSymbol {
1065 symbol: ModuleSymbol::new(
1066 importer,
1067 name.clone(),
1068 symbol.visibility(),
1069 symbol.span(),
1070 ),
1071 variants: symbol.variants().clone(),
1072 })
1073 .collect();
1074 let target =
1075 self.modules
1076 .get_mut(importer)
1077 .ok_or_else(|| ModuleResolveError::UnknownModule {
1078 owner: importer.clone(),
1079 })?;
1080 for symbol in injected {
1081 let name = IndexName::from_atom(symbol.resolved().atom().clone());
1082 target.indexes.entry(name).or_insert(symbol);
1083 }
1084 Ok(())
1085 }
1086
1087 fn register_import_with_access(
1088 &mut self,
1089 owner: &DagId,
1090 path: &ModulePath,
1091 kind: &ImportKind,
1092 target: &DagId,
1093 access: ModuleAccess,
1094 ) -> Result<(), ModuleResolveError> {
1095 self.module_symbols(owner)?;
1096 self.module_symbols(target)?;
1097
1098 let additions = self.import_additions(path, kind, target, access)?;
1099 self.check_import_exclusive_name_collisions(owner, &additions)?;
1100 let scope =
1101 self.scopes
1102 .get_mut(owner)
1103 .ok_or_else(|| ModuleResolveError::UnknownModule {
1104 owner: owner.clone(),
1105 })?;
1106 for addition in additions {
1107 scope.apply_addition(owner, addition)?;
1108 }
1109 Ok(())
1110 }
1111
1112 pub fn resolve_decl_path(
1118 &self,
1119 owner: &DagId,
1120 path: &NamePath,
1121 ) -> Result<ResolvedName<namespace::Decl>, ModuleResolveError> {
1122 self.resolve_symbol_path(owner, path, ModuleSymbols::decls, |scope| {
1123 &scope.selected_decls
1124 })
1125 }
1126
1127 pub fn resolve_const_decl_path(
1129 &self,
1130 owner: &DagId,
1131 path: &NamePath,
1132 ) -> Result<ResolvedName<namespace::Decl>, ModuleResolveError> {
1133 let resolved = self.resolve_decl_path(owner, path)?;
1134 let actual = self.decl_symbol_kind(&resolved)?;
1135 if actual.is_const() {
1136 Ok(resolved)
1137 } else {
1138 Err(ModuleResolveError::UnexpectedDeclKind {
1139 name: resolved,
1140 expected: "const",
1141 actual,
1142 })
1143 }
1144 }
1145
1146 pub fn decl_symbol_kind(
1148 &self,
1149 name: &ResolvedName<namespace::Decl>,
1150 ) -> Result<DeclSymbolKind, ModuleResolveError> {
1151 let symbols = self.module_symbols(name.owner())?;
1152 let def_name = DeclName::from_atom(name.atom().clone());
1153 symbols
1154 .decls
1155 .get(def_name.as_str())
1156 .map(ModuleDeclSymbol::kind)
1157 .ok_or_else(|| ModuleResolveError::UnknownName {
1158 owner: name.owner().clone(),
1159 namespace: namespace::Decl::DISPLAY_NAME,
1160 name: name.as_str().to_string(),
1161 })
1162 }
1163
1164 pub fn resolve_dimension_path(
1166 &self,
1167 owner: &DagId,
1168 path: &NamePath,
1169 ) -> Result<ResolvedName<namespace::Dim>, ModuleResolveError> {
1170 self.resolve_symbol_path(owner, path, ModuleSymbols::dimensions, |scope| {
1171 &scope.selected_dimensions
1172 })
1173 }
1174
1175 pub fn resolve_unit_path(
1177 &self,
1178 owner: &DagId,
1179 path: &NamePath,
1180 ) -> Result<ResolvedName<namespace::Unit>, ModuleResolveError> {
1181 self.resolve_symbol_path(owner, path, ModuleSymbols::units, |scope| {
1182 &scope.selected_units
1183 })
1184 }
1185
1186 pub fn resolve_struct_type_path(
1188 &self,
1189 owner: &DagId,
1190 path: &NamePath,
1191 ) -> Result<ResolvedName<namespace::StructType>, ModuleResolveError> {
1192 self.resolve_symbol_path(owner, path, ModuleSymbols::struct_types, |scope| {
1193 &scope.selected_struct_types
1194 })
1195 }
1196
1197 pub fn resolve_constructor_path(
1199 &self,
1200 owner: &DagId,
1201 path: &NamePath,
1202 ) -> Result<ResolvedName<namespace::Constructor>, ModuleResolveError> {
1203 self.resolve_symbol_path(owner, path, ModuleSymbols::constructors, |scope| {
1204 &scope.selected_constructors
1205 })
1206 }
1207
1208 pub fn resolve_constructor_ident_path(
1211 &self,
1212 owner: &DagId,
1213 path: &IdentPath,
1214 ) -> Result<ResolvedName<namespace::Constructor>, ModuleResolveError> {
1215 self.resolve_constructor_path(owner, &ident_path_to_name_path(path))
1216 }
1217
1218 pub fn resolve_index_path(
1220 &self,
1221 owner: &DagId,
1222 path: &NamePath,
1223 ) -> Result<ResolvedName<namespace::Index>, ModuleResolveError> {
1224 self.resolve_symbol_path(owner, path, ModuleSymbols::indexes, |scope| {
1225 &scope.selected_indexes
1226 })
1227 }
1228
1229 pub fn resolve_index_variant_path(
1232 &self,
1233 owner: &DagId,
1234 path: &NamePath,
1235 ) -> Result<ResolvedIndexVariant, ModuleResolveError> {
1236 let (index_segments, variant_atom) = path.split_last();
1237 let index_path = name_path_from_slice(index_segments).ok_or_else(|| {
1238 ModuleResolveError::ExpectedIndexVariantPath {
1239 owner: owner.clone(),
1240 path: path.display_path(),
1241 }
1242 })?;
1243 self.resolve_index_variant_parts(
1244 owner,
1245 &index_path,
1246 &IndexVariantName::from_atom(variant_atom.clone()),
1247 )
1248 }
1249
1250 pub fn resolve_index_variant_parts(
1258 &self,
1259 owner: &DagId,
1260 index_path: &NamePath,
1261 variant: &IndexVariantName,
1262 ) -> Result<ResolvedIndexVariant, ModuleResolveError> {
1263 let resolved_index = self.resolve_index_path(owner, index_path)?;
1264 let index_owner = resolved_index.owner().clone();
1265 let index_name = IndexName::from_atom(resolved_index.atom().clone());
1266 let target_symbols = self.module_symbols(&index_owner)?;
1267 let index_symbol = target_symbols
1268 .indexes
1269 .get(index_name.as_str())
1270 .ok_or_else(|| ModuleResolveError::UnknownName {
1271 owner: index_owner.clone(),
1272 namespace: namespace::Index::DISPLAY_NAME,
1273 name: index_name.to_string(),
1274 })?;
1275 if !index_symbol.variants.contains_key(variant.as_str()) {
1276 return Err(ModuleResolveError::UnknownIndexVariant {
1277 index: resolved_index,
1278 variant: variant.clone(),
1279 });
1280 }
1281 Ok(ResolvedIndexVariant::new(resolved_index, variant.clone()))
1282 }
1283
1284 pub fn resolve_bare_index_variant(
1287 &self,
1288 owner: &DagId,
1289 variant: &IndexVariantName,
1290 ) -> Result<ResolvedIndexVariant, ModuleResolveError> {
1291 let local = self.module_symbols(owner)?;
1292 let scope = self.module_scope(owner)?;
1293 let mut candidates = Vec::new();
1294
1295 for symbol in local.indexes.values() {
1296 if symbol.variants().contains_key(variant.as_str()) {
1297 candidates.push(symbol.resolved().clone());
1298 }
1299 }
1300 for imported in scope.selected_indexes.values() {
1301 let resolved = imported.resolved();
1302 let index_owner = resolved.owner().clone();
1303 let index_name = IndexName::from_atom(resolved.atom().clone());
1304 let target_symbols = self.module_symbols(&index_owner)?;
1305 let Some(symbol) = target_symbols.indexes.get(index_name.as_str()) else {
1306 continue;
1307 };
1308 if symbol.variants().contains_key(variant.as_str()) {
1309 candidates.push(resolved.clone());
1310 }
1311 }
1312 match candidates.as_slice() {
1313 [] => Err(ModuleResolveError::UnknownName {
1314 owner: owner.clone(),
1315 namespace: namespace::IndexVariant::DISPLAY_NAME,
1316 name: variant.to_string(),
1317 }),
1318 [index] => Ok(ResolvedIndexVariant::new(index.clone(), variant.clone())),
1319 _ => Err(ModuleResolveError::AmbiguousIndexVariant {
1320 owner: owner.clone(),
1321 variant: variant.clone(),
1322 indexes: candidates,
1323 }),
1324 }
1325 }
1326
1327 pub fn resolve_module_path(
1336 &self,
1337 owner: &DagId,
1338 path: &ModulePath,
1339 ) -> Result<DagId, ModuleResolveError> {
1340 if let [leaf] = path.segments() {
1341 let target = owner.child(leaf.name.as_str());
1342 if self.modules.contains_key(&target) {
1343 return Ok(target);
1344 }
1345 if let Some(parent) = owner.parent() {
1346 let sibling = parent.child(leaf.name.as_str());
1347 if self.modules.contains_key(&sibling) {
1348 return Ok(sibling);
1349 }
1350 }
1351 return Err(ModuleResolveError::UnknownModule { owner: target });
1352 }
1353
1354 let atoms = path
1355 .segments()
1356 .iter()
1357 .map(|segment| segment.name.clone())
1358 .collect::<Vec<_>>();
1359 let resolved = self.resolve_module_qualifier(owner, &atoms)?;
1360 self.ensure_module_visible(&resolved.owner, resolved.access)?;
1361 Ok(resolved.owner)
1362 }
1363
1364 fn import_additions(
1365 &self,
1366 path: &ModulePath,
1367 kind: &ImportKind,
1368 target: &DagId,
1369 access: ModuleAccess,
1370 ) -> Result<Vec<ImportAddition>, ModuleResolveError> {
1371 match kind {
1372 ImportKind::Module { alias } => {
1373 let alias = alias.clone().unwrap_or_else(|| {
1374 Spanned::new(
1375 ModuleAliasName::from_atom(path.leaf().name.clone()),
1376 path.leaf().span,
1377 )
1378 });
1379 Ok(vec![ImportAddition::ModuleAlias {
1380 alias,
1381 target: target.clone(),
1382 access,
1383 }])
1384 }
1385 ImportKind::Selective(items) => items
1386 .iter()
1387 .map(|item| self.import_item_additions(target, item, access))
1388 .collect::<Result<Vec<_>, _>>()
1389 .map(|chunks| chunks.into_iter().flatten().collect()),
1390 }
1391 }
1392
1393 fn check_import_exclusive_name_collisions(
1394 &self,
1395 owner: &DagId,
1396 additions: &[ImportAddition],
1397 ) -> Result<(), ModuleResolveError> {
1398 let local = self.module_symbols(owner)?;
1399 let scope = self.module_scope(owner)?;
1400 let mut occupied = HashMap::new();
1401
1402 seed_exclusive_names(&mut occupied, &local.decls, ExclusiveNameKind::Value);
1403 seed_exclusive_names(
1404 &mut occupied,
1405 &local.dimensions,
1406 ExclusiveNameKind::Dimension,
1407 );
1408 seed_exclusive_names(
1409 &mut occupied,
1410 &local.struct_types,
1411 ExclusiveNameKind::StructType,
1412 );
1413 seed_exclusive_names(&mut occupied, &local.indexes, ExclusiveNameKind::Index);
1414 seed_exclusive_names(
1415 &mut occupied,
1416 &scope.selected_decls,
1417 ExclusiveNameKind::Value,
1418 );
1419 seed_exclusive_names(
1420 &mut occupied,
1421 &scope.selected_dimensions,
1422 ExclusiveNameKind::Dimension,
1423 );
1424 seed_exclusive_names(
1425 &mut occupied,
1426 &scope.selected_struct_types,
1427 ExclusiveNameKind::StructType,
1428 );
1429 seed_exclusive_names(
1430 &mut occupied,
1431 &scope.selected_indexes,
1432 ExclusiveNameKind::Index,
1433 );
1434
1435 check_import_addition_exclusive_names(owner, &mut occupied, additions)
1436 }
1437
1438 #[expect(
1439 clippy::too_many_lines,
1440 reason = "import namespace expansion is kept together"
1441 )]
1442 fn import_item_additions(
1443 &self,
1444 target: &DagId,
1445 item: &ImportItem,
1446 access: ModuleAccess,
1447 ) -> Result<Vec<ImportAddition>, ModuleResolveError> {
1448 let source_atom = &item.name.name;
1449 let local_atom = item
1450 .alias
1451 .as_ref()
1452 .map_or_else(|| item.name.name.clone(), |alias| alias.name.clone());
1453 let local_span = item.local_span();
1454 let local_visibility = if item.is_pub {
1455 SymbolVisibility::Public
1456 } else {
1457 SymbolVisibility::Private
1458 };
1459
1460 match item.namespace {
1461 ImportItemNamespace::Type => {
1462 match self.exported_symbol_for_import(
1463 target,
1464 source_atom,
1465 access,
1466 ModuleSymbols::struct_types,
1467 |scope| &scope.selected_struct_types,
1468 )? {
1469 ExportLookup::Public(target_name) => Ok(vec![ImportAddition::StructType {
1470 local: Spanned::new(StructTypeName::from_atom(local_atom), local_span),
1471 target: target_name,
1472 visibility: local_visibility,
1473 }]),
1474 ExportLookup::Private => Err(ModuleResolveError::PrivateName {
1475 owner: target.clone(),
1476 namespace: namespace::StructType::DISPLAY_NAME,
1477 name: source_atom.to_string(),
1478 }),
1479 ExportLookup::Missing => {
1480 if let Some(actual) =
1481 self.exported_surface_kind_for_import(target, source_atom, access)?
1482 {
1483 return Err(ModuleResolveError::WrongUniverseName {
1484 owner: target.clone(),
1485 name: source_atom.to_string(),
1486 expected: SurfaceNameKind::Type,
1487 actual,
1488 });
1489 }
1490 Err(ModuleResolveError::UnknownName {
1491 owner: target.clone(),
1492 namespace: namespace::StructType::DISPLAY_NAME,
1493 name: source_atom.to_string(),
1494 })
1495 }
1496 }
1497 }
1498 ImportItemNamespace::Default => {
1499 let mut additions = Vec::new();
1500 let mut saw_private = false;
1501
1502 match self.exported_symbol_for_import(
1503 target,
1504 source_atom,
1505 access,
1506 ModuleSymbols::decls,
1507 |scope| &scope.selected_decls,
1508 )? {
1509 ExportLookup::Public(target_name) => additions.push(ImportAddition::Decl {
1510 local: Spanned::new(DeclName::from_atom(local_atom.clone()), local_span),
1511 target: target_name,
1512 visibility: local_visibility,
1513 }),
1514 ExportLookup::Private => saw_private = true,
1515 ExportLookup::Missing => {}
1516 }
1517 match self.exported_symbol_for_import(
1518 target,
1519 source_atom,
1520 access,
1521 ModuleSymbols::dimensions,
1522 |scope| &scope.selected_dimensions,
1523 )? {
1524 ExportLookup::Public(target_name) => {
1525 additions.push(ImportAddition::Dimension {
1526 local: Spanned::new(DimName::from_atom(local_atom.clone()), local_span),
1527 target: target_name,
1528 visibility: local_visibility,
1529 });
1530 }
1531 ExportLookup::Private => saw_private = true,
1532 ExportLookup::Missing => {}
1533 }
1534 match self.exported_symbol_for_import(
1535 target,
1536 source_atom,
1537 access,
1538 ModuleSymbols::units,
1539 |scope| &scope.selected_units,
1540 )? {
1541 ExportLookup::Public(target_name) => additions.push(ImportAddition::Unit {
1542 local: Spanned::new(UnitName::from_atom(local_atom.clone()), local_span),
1543 target: target_name,
1544 visibility: local_visibility,
1545 }),
1546 ExportLookup::Private => saw_private = true,
1547 ExportLookup::Missing => {}
1548 }
1549 match self.exported_symbol_for_import(
1550 target,
1551 source_atom,
1552 access,
1553 ModuleSymbols::indexes,
1554 |scope| &scope.selected_indexes,
1555 )? {
1556 ExportLookup::Public(target_name) => additions.push(ImportAddition::Index {
1557 local: Spanned::new(IndexName::from_atom(local_atom.clone()), local_span),
1558 target: target_name,
1559 visibility: local_visibility,
1560 }),
1561 ExportLookup::Private => saw_private = true,
1562 ExportLookup::Missing => {}
1563 }
1564 match self.exported_symbol_for_import(
1565 target,
1566 source_atom,
1567 access,
1568 ModuleSymbols::constructors,
1569 |scope| &scope.selected_constructors,
1570 )? {
1571 ExportLookup::Public(target_name) => {
1572 additions.push(ImportAddition::Constructor {
1573 local: Spanned::new(ConstructorName::from_atom(local_atom), local_span),
1574 target: target_name,
1575 visibility: local_visibility,
1576 });
1577 }
1578 ExportLookup::Private => saw_private = true,
1579 ExportLookup::Missing => {}
1580 }
1581
1582 if !additions.is_empty() {
1583 Ok(additions)
1584 } else if saw_private {
1585 Err(ModuleResolveError::PrivateName {
1586 owner: target.clone(),
1587 namespace: "default import namespace",
1588 name: source_atom.to_string(),
1589 })
1590 } else if let Some(actual) =
1591 self.exported_surface_kind_for_import(target, source_atom, access)?
1592 {
1593 Err(ModuleResolveError::WrongUniverseName {
1594 owner: target.clone(),
1595 name: source_atom.to_string(),
1596 expected: SurfaceNameKind::DefaultImportItem,
1597 actual,
1598 })
1599 } else {
1600 Err(ModuleResolveError::UnknownName {
1601 owner: target.clone(),
1602 namespace: "default import namespace",
1603 name: source_atom.to_string(),
1604 })
1605 }
1606 }
1607 }
1608 }
1609
1610 fn exported_symbol_for_import<Ns, S>(
1611 &self,
1612 target: &DagId,
1613 atom: &NameAtom,
1614 access: ModuleAccess,
1615 local_symbols: fn(&ModuleSymbols) -> &HashMap<NameDef<Ns>, S>,
1616 selected_symbols: fn(&ModuleScope) -> &HashMap<NameDef<Ns>, ImportedSymbol<Ns>>,
1617 ) -> Result<ExportLookup<Ns>, ModuleResolveError>
1618 where
1619 Ns: ResolvableNamespace,
1620 S: ModuleSymbolLookup<Ns>,
1621 {
1622 let target_symbols = self.module_symbols(target)?;
1623 match exported_symbol(local_symbols(target_symbols), atom, access) {
1624 ExportLookup::Missing => {}
1625 found => return Ok(found),
1626 }
1627
1628 let target_scope = self.module_scope(target)?;
1629 Ok(exported_symbol(
1630 selected_symbols(target_scope),
1631 atom,
1632 access,
1633 ))
1634 }
1635
1636 fn exported_surface_kind_for_import(
1637 &self,
1638 target: &DagId,
1639 atom: &NameAtom,
1640 access: ModuleAccess,
1641 ) -> Result<Option<SurfaceNameKind>, ModuleResolveError> {
1642 macro_rules! probe {
1643 ($kind:expr, $local:expr, $selected:expr) => {
1644 match self.exported_symbol_for_import(target, atom, access, $local, $selected)? {
1645 ExportLookup::Public(_) | ExportLookup::Private => return Ok(Some($kind)),
1646 ExportLookup::Missing => {}
1647 }
1648 };
1649 }
1650
1651 probe!(SurfaceNameKind::Value, ModuleSymbols::decls, |scope| &scope
1652 .selected_decls);
1653 probe!(
1654 SurfaceNameKind::Dimension,
1655 ModuleSymbols::dimensions,
1656 |scope| &scope.selected_dimensions
1657 );
1658 probe!(SurfaceNameKind::Unit, ModuleSymbols::units, |scope| &scope
1659 .selected_units);
1660 probe!(
1661 SurfaceNameKind::Type,
1662 ModuleSymbols::struct_types,
1663 |scope| &scope.selected_struct_types
1664 );
1665 probe!(SurfaceNameKind::Index, ModuleSymbols::indexes, |scope| {
1666 &scope.selected_indexes
1667 });
1668 probe!(
1669 SurfaceNameKind::Constructor,
1670 ModuleSymbols::constructors,
1671 |scope| &scope.selected_constructors
1672 );
1673
1674 Ok(None)
1675 }
1676
1677 fn resolve_symbol_path<Ns, S>(
1678 &self,
1679 owner: &DagId,
1680 path: &NamePath,
1681 local_symbols: fn(&ModuleSymbols) -> &HashMap<NameDef<Ns>, S>,
1682 selected_symbols: fn(&ModuleScope) -> &HashMap<NameDef<Ns>, ImportedSymbol<Ns>>,
1683 ) -> Result<ResolvedName<Ns>, ModuleResolveError>
1684 where
1685 Ns: ResolvableNamespace,
1686 S: ModuleSymbolLookup<Ns>,
1687 {
1688 if let Some(atom) = path.as_bare() {
1689 let local = self.module_symbols(owner)?;
1690 if let Some(symbol) = local_symbols(local).get(atom.as_str()) {
1691 return Ok(symbol.resolved().clone());
1692 }
1693 let scope = self.module_scope(owner)?;
1694 if let Some(imported) = selected_symbols(scope).get(atom.as_str()) {
1695 return Ok(imported.resolved().clone());
1696 }
1697 if let Some(actual) = self.visible_surface_kind_for_bare_name(owner, atom)? {
1698 return Err(ModuleResolveError::WrongUniverseName {
1699 owner: owner.clone(),
1700 name: atom.to_string(),
1701 expected: Ns::SURFACE_KIND,
1702 actual,
1703 });
1704 }
1705 return Err(ModuleResolveError::UnknownName {
1706 owner: owner.clone(),
1707 namespace: Ns::DISPLAY_NAME,
1708 name: atom.to_string(),
1709 });
1710 }
1711
1712 let (qualifier, leaf) = path.split_last();
1713 let target_ref = match self.resolve_module_qualifier(owner, qualifier) {
1714 Ok(target_ref) => target_ref,
1715 Err(
1716 err @ (ModuleResolveError::UnknownModuleAlias { .. }
1717 | ModuleResolveError::UnknownModule { .. }),
1718 ) => {
1719 if let Some(actual) = self.visible_index_variant_path_kind(owner, path)? {
1720 return Err(ModuleResolveError::WrongUniverseName {
1721 owner: owner.clone(),
1722 name: path.display_path(),
1723 expected: Ns::SURFACE_KIND,
1724 actual,
1725 });
1726 }
1727 return Err(err);
1728 }
1729 Err(err) => return Err(err),
1730 };
1731 let target = self.module_symbols(&target_ref.owner)?;
1732 if let Some(symbol) = local_symbols(target).get(leaf.as_str()) {
1733 if target_ref.access.requires_public() && !symbol.visibility().is_public() {
1734 return Err(ModuleResolveError::PrivateName {
1735 owner: target_ref.owner,
1736 namespace: Ns::DISPLAY_NAME,
1737 name: leaf.to_string(),
1738 });
1739 }
1740 return Ok(symbol.resolved().clone());
1741 }
1742
1743 let target_scope = self.module_scope(&target_ref.owner)?;
1744 if let Some(imported) = selected_symbols(target_scope).get(leaf.as_str()) {
1745 if target_ref.access.requires_public() && !imported.visibility().is_public() {
1746 return Err(ModuleResolveError::PrivateName {
1747 owner: target_ref.owner,
1748 namespace: Ns::DISPLAY_NAME,
1749 name: leaf.to_string(),
1750 });
1751 }
1752 return Ok(imported.resolved().clone());
1753 }
1754
1755 if let Some(actual) =
1756 self.visible_surface_kind_for_qualified_leaf(&target_ref, leaf, path)?
1757 {
1758 return Err(ModuleResolveError::WrongUniverseName {
1759 owner: target_ref.owner,
1760 name: path.display_path(),
1761 expected: Ns::SURFACE_KIND,
1762 actual,
1763 });
1764 }
1765
1766 Err(ModuleResolveError::UnknownName {
1767 owner: target_ref.owner,
1768 namespace: Ns::DISPLAY_NAME,
1769 name: leaf.to_string(),
1770 })
1771 }
1772
1773 fn visible_surface_kind_for_bare_name(
1774 &self,
1775 owner: &DagId,
1776 atom: &NameAtom,
1777 ) -> Result<Option<SurfaceNameKind>, ModuleResolveError> {
1778 let local = self.module_symbols(owner)?;
1779 if let Some(kind) = surface_kind_in_local_symbols(local, atom, false) {
1780 return Ok(Some(kind));
1781 }
1782 let scope = self.module_scope(owner)?;
1783 Ok(surface_kind_in_scope(scope, atom, false))
1784 }
1785
1786 fn visible_surface_kind_for_qualified_leaf(
1787 &self,
1788 target_ref: &ResolvedModuleQualifier,
1789 leaf: &NameAtom,
1790 path: &NamePath,
1791 ) -> Result<Option<SurfaceNameKind>, ModuleResolveError> {
1792 if let Some(kind) = self.visible_index_variant_path_kind(&target_ref.owner, path)? {
1793 return Ok(Some(kind));
1794 }
1795
1796 let target = self.module_symbols(&target_ref.owner)?;
1797 if let Some(kind) =
1798 surface_kind_in_local_symbols(target, leaf, target_ref.access.requires_public())
1799 {
1800 return Ok(Some(kind));
1801 }
1802 let target_scope = self.module_scope(&target_ref.owner)?;
1803 Ok(surface_kind_in_scope(
1804 target_scope,
1805 leaf,
1806 target_ref.access.requires_public(),
1807 ))
1808 }
1809
1810 fn visible_index_variant_path_kind(
1811 &self,
1812 owner: &DagId,
1813 path: &NamePath,
1814 ) -> Result<Option<SurfaceNameKind>, ModuleResolveError> {
1815 match self.resolve_index_variant_path(owner, path) {
1816 Ok(_) => Ok(Some(SurfaceNameKind::IndexLabel)),
1817 Err(
1818 ModuleResolveError::UnknownName { .. }
1819 | ModuleResolveError::UnknownIndexVariant { .. }
1820 | ModuleResolveError::ExpectedIndexVariantPath { .. }
1821 | ModuleResolveError::UnknownModuleAlias { .. }
1822 | ModuleResolveError::UnknownModule { .. },
1823 ) => Ok(None),
1824 Err(err) => Err(err),
1825 }
1826 }
1827
1828 fn resolve_module_qualifier(
1829 &self,
1830 owner: &DagId,
1831 qualifier: &[NameAtom],
1832 ) -> Result<ResolvedModuleQualifier, ModuleResolveError> {
1833 let Some((head, rest)) = qualifier.split_first() else {
1834 return Err(ModuleResolveError::UnknownName {
1835 owner: owner.clone(),
1836 namespace: "module",
1837 name: String::new(),
1838 });
1839 };
1840 let scope = self.module_scope(owner)?;
1841 let alias = ModuleAliasName::from_atom(head.clone());
1842 let alias_target = scope.module_aliases.get(alias.as_str()).ok_or_else(|| {
1843 ModuleResolveError::UnknownModuleAlias {
1844 owner: owner.clone(),
1845 alias,
1846 }
1847 })?;
1848 let mut target = alias_target.target.clone();
1854 for segment in rest {
1855 target = target.child(segment.as_str());
1856 if !self.modules.contains_key(&target) {
1857 return Err(ModuleResolveError::UnknownModule { owner: target });
1858 }
1859 self.ensure_module_visible(&target, alias_target.access)?;
1860 }
1861 if self.modules.contains_key(&target) {
1862 Ok(ResolvedModuleQualifier {
1863 owner: target,
1864 access: alias_target.access,
1865 })
1866 } else {
1867 Err(ModuleResolveError::UnknownModule { owner: target })
1868 }
1869 }
1870
1871 fn module_symbols(&self, owner: &DagId) -> Result<&ModuleSymbols, ModuleResolveError> {
1872 self.modules
1873 .get(owner)
1874 .ok_or_else(|| ModuleResolveError::UnknownModule {
1875 owner: owner.clone(),
1876 })
1877 }
1878
1879 #[must_use]
1884 pub fn scope(&self, owner: &DagId) -> Option<&ModuleScope> {
1885 self.scopes.get(owner)
1886 }
1887
1888 fn module_scope(&self, owner: &DagId) -> Result<&ModuleScope, ModuleResolveError> {
1889 self.scopes
1890 .get(owner)
1891 .ok_or_else(|| ModuleResolveError::UnknownModule {
1892 owner: owner.clone(),
1893 })
1894 }
1895
1896 fn ensure_module_visible(
1897 &self,
1898 target: &DagId,
1899 access: ModuleAccess,
1900 ) -> Result<(), ModuleResolveError> {
1901 if !access.requires_public() {
1902 return Ok(());
1903 }
1904 let Some(parent) = target.parent() else {
1905 return Ok(());
1906 };
1907 let Some(parent_symbols) = self.modules.get(&parent) else {
1908 return Ok(());
1909 };
1910 let Some(symbol) = parent_symbols.decls.get(target.name()) else {
1911 return Ok(());
1912 };
1913 if symbol.kind() == DeclSymbolKind::Dag && !symbol.visibility().is_public() {
1914 return Err(ModuleResolveError::PrivateName {
1915 owner: parent,
1916 namespace: "dag",
1917 name: target.name().to_string(),
1918 });
1919 }
1920 Ok(())
1921 }
1922}
1923
1924impl ModuleScope {
1925 fn apply_addition(
1926 &mut self,
1927 owner: &DagId,
1928 addition: ImportAddition,
1929 ) -> Result<(), ModuleResolveError> {
1930 match addition {
1931 ImportAddition::ModuleAlias {
1932 alias,
1933 target,
1934 access,
1935 } => insert_module_alias(
1936 owner,
1937 &mut self.module_aliases,
1938 alias,
1939 target,
1940 access,
1941 namespace::ModuleAlias::DISPLAY_NAME,
1942 ),
1943 ImportAddition::Decl {
1944 local,
1945 target,
1946 visibility,
1947 } => insert_imported_symbol(
1948 owner,
1949 &mut self.selected_decls,
1950 local,
1951 target,
1952 visibility,
1953 namespace::Decl::DISPLAY_NAME,
1954 ),
1955 ImportAddition::Dimension {
1956 local,
1957 target,
1958 visibility,
1959 } => insert_imported_symbol(
1960 owner,
1961 &mut self.selected_dimensions,
1962 local,
1963 target,
1964 visibility,
1965 namespace::Dim::DISPLAY_NAME,
1966 ),
1967 ImportAddition::Unit {
1968 local,
1969 target,
1970 visibility,
1971 } => insert_imported_symbol(
1972 owner,
1973 &mut self.selected_units,
1974 local,
1975 target,
1976 visibility,
1977 namespace::Unit::DISPLAY_NAME,
1978 ),
1979 ImportAddition::StructType {
1980 local,
1981 target,
1982 visibility,
1983 } => insert_imported_symbol(
1984 owner,
1985 &mut self.selected_struct_types,
1986 local,
1987 target,
1988 visibility,
1989 namespace::StructType::DISPLAY_NAME,
1990 ),
1991 ImportAddition::Index {
1992 local,
1993 target,
1994 visibility,
1995 } => insert_imported_symbol(
1996 owner,
1997 &mut self.selected_indexes,
1998 local,
1999 target,
2000 visibility,
2001 namespace::Index::DISPLAY_NAME,
2002 ),
2003 ImportAddition::Constructor {
2004 local,
2005 target,
2006 visibility,
2007 } => insert_imported_symbol(
2008 owner,
2009 &mut self.selected_constructors,
2010 local,
2011 target,
2012 visibility,
2013 namespace::Constructor::DISPLAY_NAME,
2014 ),
2015 }
2016 }
2017}
2018
2019fn insert_module_alias(
2020 owner: &DagId,
2021 map: &mut HashMap<ModuleAliasName, ModuleAliasTarget>,
2022 alias: Spanned<ModuleAliasName>,
2023 target: DagId,
2024 access: ModuleAccess,
2025 namespace_name: &'static str,
2026) -> Result<(), ModuleResolveError> {
2027 if let Some(first) = map.get(alias.value.as_str()) {
2028 return Err(ModuleResolveError::DuplicateImportName {
2029 owner: owner.clone(),
2030 namespace: namespace_name,
2031 name: alias.value.to_string(),
2032 first: first.span(),
2033 duplicate: alias.span,
2034 });
2035 }
2036 map.insert(
2037 alias.value,
2038 ModuleAliasTarget {
2039 target,
2040 span: alias.span,
2041 access,
2042 },
2043 );
2044 Ok(())
2045}
2046
2047fn surface_kind_in_local_symbols(
2048 symbols: &ModuleSymbols,
2049 atom: &NameAtom,
2050 requires_public: bool,
2051) -> Option<SurfaceNameKind> {
2052 macro_rules! probe {
2053 ($map:expr, $kind:expr) => {
2054 if let Some(symbol) = $map.get(atom.as_str())
2055 && (!requires_public || symbol.visibility().is_public())
2056 {
2057 return Some($kind);
2058 }
2059 };
2060 }
2061
2062 probe!(symbols.decls, SurfaceNameKind::Value);
2063 probe!(symbols.dimensions, SurfaceNameKind::Dimension);
2064 probe!(symbols.units, SurfaceNameKind::Unit);
2065 probe!(symbols.struct_types, SurfaceNameKind::Type);
2066 probe!(symbols.indexes, SurfaceNameKind::Index);
2067 probe!(symbols.constructors, SurfaceNameKind::Constructor);
2068 None
2069}
2070
2071fn surface_kind_in_scope(
2072 scope: &ModuleScope,
2073 atom: &NameAtom,
2074 requires_public: bool,
2075) -> Option<SurfaceNameKind> {
2076 macro_rules! probe {
2077 ($map:expr, $kind:expr) => {
2078 if let Some(symbol) = $map.get(atom.as_str())
2079 && (!requires_public || symbol.visibility().is_public())
2080 {
2081 return Some($kind);
2082 }
2083 };
2084 }
2085
2086 probe!(scope.selected_decls, SurfaceNameKind::Value);
2087 probe!(scope.selected_dimensions, SurfaceNameKind::Dimension);
2088 probe!(scope.selected_units, SurfaceNameKind::Unit);
2089 probe!(scope.selected_struct_types, SurfaceNameKind::Type);
2090 probe!(scope.selected_indexes, SurfaceNameKind::Index);
2091 probe!(scope.selected_constructors, SurfaceNameKind::Constructor);
2092 None
2093}
2094
2095fn seed_exclusive_names<Ns, S>(
2096 occupied: &mut HashMap<NameAtom, ExclusiveNameBinding>,
2097 symbols: &HashMap<NameDef<Ns>, S>,
2098 kind: ExclusiveNameKind,
2099) where
2100 Ns: NameNamespace,
2101 S: ModuleSymbolLookup<Ns>,
2102{
2103 for (name, symbol) in symbols {
2104 occupied.insert(
2105 name.atom().clone(),
2106 ExclusiveNameBinding {
2107 kind,
2108 span: symbol.span(),
2109 },
2110 );
2111 }
2112}
2113
2114fn check_import_addition_exclusive_names(
2115 owner: &DagId,
2116 occupied: &mut HashMap<NameAtom, ExclusiveNameBinding>,
2117 additions: &[ImportAddition],
2118) -> Result<(), ModuleResolveError> {
2119 for addition in additions {
2120 match addition {
2121 ImportAddition::Decl { local, .. } => register_import_exclusive_name(
2122 owner,
2123 occupied,
2124 local.value.atom(),
2125 ExclusiveNameKind::Value,
2126 local.span,
2127 )?,
2128 ImportAddition::Dimension { local, .. } => register_import_exclusive_name(
2129 owner,
2130 occupied,
2131 local.value.atom(),
2132 ExclusiveNameKind::Dimension,
2133 local.span,
2134 )?,
2135 ImportAddition::StructType { local, .. } => register_import_exclusive_name(
2136 owner,
2137 occupied,
2138 local.value.atom(),
2139 ExclusiveNameKind::StructType,
2140 local.span,
2141 )?,
2142 ImportAddition::Index { local, .. } => register_import_exclusive_name(
2143 owner,
2144 occupied,
2145 local.value.atom(),
2146 ExclusiveNameKind::Index,
2147 local.span,
2148 )?,
2149 ImportAddition::ModuleAlias { .. }
2150 | ImportAddition::Unit { .. }
2151 | ImportAddition::Constructor { .. } => {}
2152 }
2153 }
2154 Ok(())
2155}
2156
2157fn register_import_exclusive_name(
2158 owner: &DagId,
2159 occupied: &mut HashMap<NameAtom, ExclusiveNameBinding>,
2160 atom: &NameAtom,
2161 kind: ExclusiveNameKind,
2162 span: Span,
2163) -> Result<(), ModuleResolveError> {
2164 if let Some(first) = occupied.get(atom) {
2165 return Err(ModuleResolveError::DuplicateImportName {
2166 owner: owner.clone(),
2167 namespace: "name",
2168 name: atom.to_string(),
2169 first: first.span,
2170 duplicate: span,
2171 });
2172 }
2173 occupied.insert(atom.clone(), ExclusiveNameBinding { kind, span });
2174 Ok(())
2175}
2176
2177fn insert_imported_symbol<Ns: NameNamespace>(
2178 owner: &DagId,
2179 map: &mut HashMap<NameDef<Ns>, ImportedSymbol<Ns>>,
2180 local: Spanned<NameDef<Ns>>,
2181 target: ResolvedName<Ns>,
2182 visibility: SymbolVisibility,
2183 namespace_name: &'static str,
2184) -> Result<(), ModuleResolveError> {
2185 if let Some(first) = map.get(local.value.as_str()) {
2186 return Err(ModuleResolveError::DuplicateImportName {
2187 owner: owner.clone(),
2188 namespace: namespace_name,
2189 name: local.value.to_string(),
2190 first: first.span(),
2191 duplicate: local.span,
2192 });
2193 }
2194 map.insert(
2195 local.value,
2196 ImportedSymbol::new(target, local.span, visibility),
2197 );
2198 Ok(())
2199}
2200
2201#[derive(Debug, Clone, PartialEq, Eq)]
2202enum ExportLookup<Ns: NameNamespace> {
2203 Public(ResolvedName<Ns>),
2204 Private,
2205 Missing,
2206}
2207
2208fn exported_symbol<Ns, S>(
2209 map: &HashMap<NameDef<Ns>, S>,
2210 atom: &NameAtom,
2211 access: ModuleAccess,
2212) -> ExportLookup<Ns>
2213where
2214 Ns: NameNamespace,
2215 S: ModuleSymbolLookup<Ns>,
2216{
2217 map.get(atom.as_str())
2218 .map_or(ExportLookup::Missing, |symbol| {
2219 if !access.requires_public() || symbol.visibility().is_public() {
2220 ExportLookup::Public(symbol.resolved().clone())
2221 } else {
2222 ExportLookup::Private
2223 }
2224 })
2225}
2226
2227fn name_path_from_slice(segments: &[NameAtom]) -> Option<NamePath> {
2228 NonEmpty::try_from_vec(segments.to_vec())
2229 .ok()
2230 .map(NamePath::new)
2231}
2232
2233fn ident_path_to_name_path(path: &IdentPath) -> NamePath {
2234 let segments = path.segments();
2235 NamePath::new(NonEmpty::new(
2236 segments[0].name.clone(),
2237 segments[1..]
2238 .iter()
2239 .map(|ident| ident.name.clone())
2240 .collect(),
2241 ))
2242}
2243
2244#[derive(Debug, Clone, PartialEq, Eq, Error)]
2246pub enum ModuleResolveError {
2247 #[error("duplicate module `{owner}`")]
2249 DuplicateModule { owner: DagId },
2250 #[error("unknown module `{owner}`")]
2252 UnknownModule { owner: DagId },
2253 #[error("module alias `{alias}` is not in scope of `{owner}`")]
2255 UnknownModuleAlias {
2256 owner: DagId,
2257 alias: ModuleAliasName,
2258 },
2259 #[error("duplicate {namespace} `{name}` in module `{owner}`")]
2261 DuplicateSymbol {
2262 owner: DagId,
2263 namespace: &'static str,
2264 name: String,
2265 first: Span,
2266 duplicate: Span,
2267 },
2268 #[error("duplicate imported {namespace} `{name}` in module `{owner}`")]
2270 DuplicateImportName {
2271 owner: DagId,
2272 namespace: &'static str,
2273 name: String,
2274 first: Span,
2275 duplicate: Span,
2276 },
2277 #[error("unknown {namespace} `{name}` in module `{owner}`")]
2279 UnknownName {
2280 owner: DagId,
2281 namespace: &'static str,
2282 name: String,
2283 },
2284 #[error("in module `{owner}`, `{name}` is {actual}, not {expected}")]
2286 WrongUniverseName {
2287 owner: DagId,
2288 name: String,
2289 expected: SurfaceNameKind,
2290 actual: SurfaceNameKind,
2291 },
2292 #[error("expected {expected} declaration `{name}`, found {actual}")]
2294 UnexpectedDeclKind {
2295 name: ResolvedName<namespace::Decl>,
2296 expected: &'static str,
2297 actual: DeclSymbolKind,
2298 },
2299 #[error("private {namespace} `{name}` in module `{owner}`")]
2301 PrivateName {
2302 owner: DagId,
2303 namespace: &'static str,
2304 name: String,
2305 },
2306 #[error("expected index-variant path in module `{owner}`, got `{path}`")]
2308 ExpectedIndexVariantPath { owner: DagId, path: String },
2309 #[error("unknown variant `{variant}` for index `{index}`")]
2311 UnknownIndexVariant {
2312 index: ResolvedName<namespace::Index>,
2313 variant: IndexVariantName,
2314 },
2315 #[error("ambiguous index label `{variant}` in module `{owner}`; qualify it with an index name")]
2317 AmbiguousIndexVariant {
2318 owner: DagId,
2319 variant: IndexVariantName,
2320 indexes: Vec<ResolvedName<namespace::Index>>,
2321 },
2322}
2323
2324#[cfg(test)]
2325mod tests {
2326 use super::*;
2327 use crate::syntax::ast::Ident;
2328 use crate::syntax::parser::Parser;
2329
2330 fn desugared_source(source: &str) -> ast::File {
2331 let raw = Parser::new(source).parse_file().unwrap();
2332 crate::syntax::desugar::desugar_multi_decls_in_file(raw)
2333 }
2334
2335 fn first_import(file: &ast::File) -> (&ModulePath, &ImportKind) {
2336 file.declarations
2337 .iter()
2338 .find_map(|decl| match &decl.kind {
2339 ast::DeclKind::Import(import) => Some((&import.path, &import.kind)),
2340 _ => None,
2341 })
2342 .expect("source should contain an import")
2343 }
2344
2345 fn imports(file: &ast::File) -> Vec<(&ModulePath, &ImportKind)> {
2346 file.declarations
2347 .iter()
2348 .filter_map(|decl| match &decl.kind {
2349 ast::DeclKind::Import(import) => Some((&import.path, &import.kind)),
2350 _ => None,
2351 })
2352 .collect()
2353 }
2354
2355 fn first_include(file: &ast::File) -> (&ModulePath, &ImportKind) {
2356 file.declarations
2357 .iter()
2358 .find_map(|decl| match &decl.kind {
2359 ast::DeclKind::Include(include) => Some((&include.path, &include.kind)),
2360 _ => None,
2361 })
2362 .expect("source should contain an include")
2363 }
2364
2365 fn atom(s: &str) -> NameAtom {
2366 NameAtom::parse(s).unwrap()
2367 }
2368
2369 fn path(segments: &[&str]) -> NamePath {
2370 let atoms = segments.iter().map(|s| atom(s)).collect::<Vec<_>>();
2371 NamePath::new(NonEmpty::try_from_vec(atoms).unwrap())
2372 }
2373
2374 fn module_path(segments: &[&str]) -> ModulePath {
2375 let idents = segments
2376 .iter()
2377 .map(|s| Ident {
2378 name: atom(s),
2379 span: Span::new(0, 0),
2380 })
2381 .collect::<Vec<_>>();
2382 ModulePath {
2383 segments: NonEmpty::try_from_vec(idents).unwrap(),
2384 span: Span::new(0, 0),
2385 }
2386 }
2387
2388 fn first_dag(file: &ast::File) -> &ast::DagDecl {
2389 file.declarations
2390 .iter()
2391 .find_map(|decl| match &decl.kind {
2392 ast::DeclKind::Dag(dag) => Some(dag),
2393 _ => None,
2394 })
2395 .expect("source should contain a dag")
2396 }
2397
2398 #[test]
2399 fn local_type_index_name_collision_is_rejected() {
2400 let owner = DagId::root("main");
2401 let file = desugared_source("type M { Mk(v: Dimensionless) }\npub index M = { A, B };");
2402
2403 let err = ModuleSymbols::from_declarations(owner.clone(), &file.declarations).unwrap_err();
2404
2405 assert!(matches!(
2406 err,
2407 ModuleResolveError::DuplicateSymbol {
2408 owner: err_owner,
2409 namespace: "name",
2410 name,
2411 ..
2412 } if err_owner == owner && name == "M"
2413 ));
2414 }
2415
2416 #[test]
2417 fn local_dimension_type_name_collision_is_rejected() {
2418 let owner = DagId::root("main");
2419 let file = desugared_source("dim M = Length;\ntype M { Mk(v: Dimensionless) }");
2420
2421 let err = ModuleSymbols::from_declarations(owner.clone(), &file.declarations).unwrap_err();
2422
2423 assert!(matches!(
2424 err,
2425 ModuleResolveError::DuplicateSymbol {
2426 owner: err_owner,
2427 namespace: "name",
2428 name,
2429 ..
2430 } if err_owner == owner && name == "M"
2431 ));
2432 }
2433
2434 #[test]
2435 fn same_named_type_and_constructor_remain_distinct() {
2436 let owner = DagId::root("main");
2437 let file = desugared_source("type T { T }");
2438
2439 let symbols = ModuleSymbols::from_declarations(owner, &file.declarations).unwrap();
2440
2441 assert!(symbols.struct_types().contains_key("T"));
2442 assert!(symbols.constructors().contains_key("T"));
2443 }
2444
2445 #[test]
2446 fn selective_import_cross_universe_name_collision_is_rejected() {
2447 let type_lib_id = DagId::root("type_lib");
2448 let index_lib_id = DagId::root("index_lib");
2449 let main_id = DagId::root("main");
2450 let type_lib = desugared_source("pub type M { Mk(v: Dimensionless) }");
2451 let index_lib = desugared_source("pub index M = { A, B };");
2452 let main = desugared_source(
2453 "import type_lib.{ type M };
2454 import index_lib.{ M };",
2455 );
2456 let imports = imports(&main);
2457
2458 let mut resolver = ModuleResolver::default();
2459 resolver
2460 .add_module(type_lib_id.clone(), &type_lib.declarations)
2461 .unwrap();
2462 resolver
2463 .add_module(index_lib_id.clone(), &index_lib.declarations)
2464 .unwrap();
2465 resolver
2466 .add_module(main_id.clone(), &main.declarations)
2467 .unwrap();
2468 resolver
2469 .register_import(&main_id, imports[0].0, imports[0].1, &type_lib_id)
2470 .unwrap();
2471 let err = resolver
2472 .register_import(&main_id, imports[1].0, imports[1].1, &index_lib_id)
2473 .unwrap_err();
2474
2475 assert!(matches!(
2476 err,
2477 ModuleResolveError::DuplicateImportName {
2478 owner,
2479 namespace: "name",
2480 name,
2481 ..
2482 } if owner == main_id && name == "M"
2483 ));
2484 }
2485
2486 #[test]
2487 fn resolves_qualified_index_variant_to_canonical_owner() {
2488 let lib_id = DagId::root("lib");
2489 let main_id = DagId::root("main");
2490 let lib = desugared_source("pub index Phase = { Burn, Coast };");
2491 let main = desugared_source("import lib as physics;");
2492 let (import_path, import_kind) = first_import(&main);
2493
2494 let mut resolver = ModuleResolver::default();
2495 resolver
2496 .add_module(lib_id.clone(), &lib.declarations)
2497 .unwrap();
2498 resolver
2499 .add_module(main_id.clone(), &main.declarations)
2500 .unwrap();
2501 resolver
2502 .register_import(&main_id, import_path, import_kind, &lib_id)
2503 .unwrap();
2504
2505 let resolved_name = resolver
2506 .resolve_index_variant_path(&main_id, &path(&["physics", "Phase", "Burn"]))
2507 .unwrap();
2508
2509 assert_eq!(resolved_name.index().owner(), &lib_id);
2510 assert_eq!(resolved_name.index().as_str(), "Phase");
2511 assert_eq!(resolved_name.variant().as_str(), "Burn");
2512 }
2513
2514 #[test]
2515 fn selective_type_alias_resolves_to_original_owner_and_leaf() {
2516 let lib_id = DagId::root("lib");
2517 let main_id = DagId::root("main");
2518 let lib = desugared_source("pub type Vec3 { Vec3 }");
2519 let main = desugared_source("import lib.{ type Vec3 as Vector };");
2520 let (import_path, import_kind) = first_import(&main);
2521
2522 let mut resolver = ModuleResolver::default();
2523 resolver
2524 .add_module(lib_id.clone(), &lib.declarations)
2525 .unwrap();
2526 resolver
2527 .add_module(main_id.clone(), &main.declarations)
2528 .unwrap();
2529 resolver
2530 .register_import(&main_id, import_path, import_kind, &lib_id)
2531 .unwrap();
2532
2533 let resolved_name = resolver
2534 .resolve_struct_type_path(&main_id, &path(&["Vector"]))
2535 .unwrap();
2536
2537 assert_eq!(resolved_name.owner(), &lib_id);
2538 assert_eq!(resolved_name.as_str(), "Vec3");
2539 }
2540
2541 #[test]
2542 fn type_import_in_child_dag_does_not_import_same_named_constructor() {
2543 let main_id = DagId::root("main");
2544 let child_id = main_id.child("build_transfer");
2545 let main = desugared_source(
2546 "pub type TransferResult { TransferResult }
2547 dag build_transfer {
2548 import main.{ type TransferResult };
2549 }",
2550 );
2551 let dag = first_dag(&main);
2552 let import = dag
2553 .body
2554 .iter()
2555 .find_map(|decl| match &decl.kind {
2556 ast::DeclKind::Import(import) => Some((&import.path, &import.kind)),
2557 _ => None,
2558 })
2559 .expect("dag body should contain an import");
2560
2561 let mut resolver = ModuleResolver::default();
2562 resolver
2563 .add_module(main_id.clone(), &main.declarations)
2564 .unwrap();
2565 resolver.add_module(child_id.clone(), &dag.body).unwrap();
2566 resolver
2567 .register_import(&child_id, import.0, import.1, &main_id)
2568 .unwrap();
2569
2570 let resolved_type = resolver
2571 .resolve_struct_type_path(&child_id, &path(&["TransferResult"]))
2572 .unwrap();
2573 assert_eq!(resolved_type.owner(), &main_id);
2574 assert_eq!(resolved_type.as_str(), "TransferResult");
2575
2576 let err = resolver
2577 .resolve_constructor_path(&child_id, &path(&["TransferResult"]))
2578 .unwrap_err();
2579 assert!(matches!(
2580 err,
2581 ModuleResolveError::WrongUniverseName {
2582 owner,
2583 name,
2584 expected: SurfaceNameKind::Constructor,
2585 actual: SurfaceNameKind::Type,
2586 } if owner == child_id && name == "TransferResult"
2587 ));
2588 }
2589
2590 #[test]
2591 fn type_marker_importing_index_reports_wrong_universe() {
2592 let lib_id = DagId::root("lib");
2593 let main_id = DagId::root("main");
2594 let lib = desugared_source("pub index M = { A };");
2595 let main = desugared_source("import lib.{ type M };");
2596 let (import_path, import_kind) = first_import(&main);
2597
2598 let mut resolver = ModuleResolver::default();
2599 resolver
2600 .add_module(lib_id.clone(), &lib.declarations)
2601 .unwrap();
2602 resolver
2603 .add_module(main_id.clone(), &main.declarations)
2604 .unwrap();
2605
2606 let err = resolver
2607 .register_import(&main_id, import_path, import_kind, &lib_id)
2608 .unwrap_err();
2609
2610 assert!(matches!(
2611 err,
2612 ModuleResolveError::WrongUniverseName {
2613 owner,
2614 name,
2615 expected: SurfaceNameKind::Type,
2616 actual: SurfaceNameKind::Index,
2617 } if owner == lib_id && name == "M"
2618 ));
2619 }
2620
2621 #[test]
2622 fn default_importing_type_reports_wrong_universe() {
2623 let lib_id = DagId::root("lib");
2624 let main_id = DagId::root("main");
2625 let lib = desugared_source("pub type Foo { MkFoo }");
2626 let main = desugared_source("import lib.{ Foo };");
2627 let (import_path, import_kind) = first_import(&main);
2628
2629 let mut resolver = ModuleResolver::default();
2630 resolver
2631 .add_module(lib_id.clone(), &lib.declarations)
2632 .unwrap();
2633 resolver
2634 .add_module(main_id.clone(), &main.declarations)
2635 .unwrap();
2636
2637 let err = resolver
2638 .register_import(&main_id, import_path, import_kind, &lib_id)
2639 .unwrap_err();
2640
2641 assert!(matches!(
2642 err,
2643 ModuleResolveError::WrongUniverseName {
2644 owner,
2645 name,
2646 expected: SurfaceNameKind::DefaultImportItem,
2647 actual: SurfaceNameKind::Type,
2648 } if owner == lib_id && name == "Foo"
2649 ));
2650 }
2651
2652 #[test]
2653 fn qualified_private_type_is_rejected() {
2654 let lib_id = DagId::root("lib");
2655 let main_id = DagId::root("main");
2656 let lib = desugared_source("type Secret { Secret }");
2657 let main = desugared_source("import lib as hidden;");
2658 let (import_path, import_kind) = first_import(&main);
2659
2660 let mut resolver = ModuleResolver::default();
2661 resolver
2662 .add_module(lib_id.clone(), &lib.declarations)
2663 .unwrap();
2664 resolver
2665 .add_module(main_id.clone(), &main.declarations)
2666 .unwrap();
2667 resolver
2668 .register_import(&main_id, import_path, import_kind, &lib_id)
2669 .unwrap();
2670
2671 let err = resolver
2672 .resolve_struct_type_path(&main_id, &path(&["hidden", "Secret"]))
2673 .unwrap_err();
2674
2675 assert!(matches!(
2676 err,
2677 ModuleResolveError::PrivateName {
2678 owner,
2679 namespace: "StructTypeName",
2680 name,
2681 } if owner == lib_id && name == "Secret"
2682 ));
2683 }
2684
2685 #[test]
2686 fn include_selective_private_decl_is_rejected() {
2687 let lib_id = DagId::root("lib");
2688 let main_id = DagId::root("main");
2689 let lib = desugared_source("node hidden: Dimensionless = 1.0;");
2690 let main = desugared_source("include lib().{ hidden };");
2691 let (include_path, include_kind) = first_include(&main);
2692
2693 let mut resolver = ModuleResolver::default();
2694 resolver
2695 .add_module(lib_id.clone(), &lib.declarations)
2696 .unwrap();
2697 resolver
2698 .add_module(main_id.clone(), &main.declarations)
2699 .unwrap();
2700
2701 let err = resolver
2702 .register_include(&main_id, include_path, include_kind, &lib_id)
2703 .unwrap_err();
2704
2705 assert!(matches!(
2706 err,
2707 ModuleResolveError::PrivateName {
2708 owner,
2709 namespace: _,
2710 name,
2711 } if owner == lib_id && name == "hidden"
2712 ));
2713 }
2714
2715 #[test]
2716 fn qualified_private_dag_path_is_rejected() {
2717 let lib_id = DagId::root("lib");
2718 let helper_id = lib_id.child("helper");
2719 let main_id = DagId::root("main");
2720 let lib = desugared_source(
2721 "dag helper {
2722 pub node shown: Dimensionless = 1.0;
2723 }",
2724 );
2725 let main = desugared_source("import lib as lib;");
2726 let (import_path, import_kind) = first_import(&main);
2727
2728 let mut resolver = ModuleResolver::default();
2729 resolver
2730 .add_module(lib_id.clone(), &lib.declarations)
2731 .unwrap();
2732 resolver
2733 .add_module(helper_id, &first_dag(&lib).body)
2734 .unwrap();
2735 resolver
2736 .add_module(main_id.clone(), &main.declarations)
2737 .unwrap();
2738 resolver
2739 .register_import(&main_id, import_path, import_kind, &lib_id)
2740 .unwrap();
2741
2742 let err = resolver
2743 .resolve_module_path(&main_id, &module_path(&["lib", "helper"]))
2744 .unwrap_err();
2745
2746 assert!(matches!(
2747 err,
2748 ModuleResolveError::PrivateName {
2749 owner,
2750 namespace: "dag",
2751 name,
2752 } if owner == lib_id && name == "helper"
2753 ));
2754 }
2755
2756 #[test]
2757 fn qualified_symbol_path_through_private_dag_is_rejected() {
2758 let lib_id = DagId::root("lib");
2762 let helper_id = lib_id.child("helper");
2763 let main_id = DagId::root("main");
2764 let lib = desugared_source(
2765 "dag helper {
2766 pub node shown: Dimensionless = 1.0;
2767 }",
2768 );
2769 let main = desugared_source("import lib as lib;");
2770 let (import_path, import_kind) = first_import(&main);
2771
2772 let mut resolver = ModuleResolver::default();
2773 resolver
2774 .add_module(lib_id.clone(), &lib.declarations)
2775 .unwrap();
2776 resolver
2777 .add_module(helper_id, &first_dag(&lib).body)
2778 .unwrap();
2779 resolver
2780 .add_module(main_id.clone(), &main.declarations)
2781 .unwrap();
2782 resolver
2783 .register_import(&main_id, import_path, import_kind, &lib_id)
2784 .unwrap();
2785
2786 let err = resolver
2787 .resolve_decl_path(&main_id, &path(&["lib", "helper", "shown"]))
2788 .unwrap_err();
2789
2790 assert!(
2791 matches!(
2792 err,
2793 ModuleResolveError::PrivateName {
2794 ref owner,
2795 namespace: "dag",
2796 ref name,
2797 } if *owner == lib_id && name == "helper"
2798 ),
2799 "expected PrivateName for dag `helper`, got: {err:?}"
2800 );
2801 }
2802
2803 #[test]
2804 fn qualified_constructor_resolves_to_canonical_owner() {
2805 let lib_id = DagId::root("lib");
2806 let main_id = DagId::root("main");
2807 let lib = desugared_source("pub type BurnKind { Impulsive, Coast }");
2808 let main = desugared_source("import lib as mission;");
2809 let (import_path, import_kind) = first_import(&main);
2810
2811 let mut resolver = ModuleResolver::default();
2812 resolver
2813 .add_module(lib_id.clone(), &lib.declarations)
2814 .unwrap();
2815 resolver
2816 .add_module(main_id.clone(), &main.declarations)
2817 .unwrap();
2818 resolver
2819 .register_import(&main_id, import_path, import_kind, &lib_id)
2820 .unwrap();
2821
2822 let resolved_name = resolver
2823 .resolve_constructor_path(&main_id, &path(&["mission", "Impulsive"]))
2824 .unwrap();
2825
2826 assert_eq!(resolved_name.owner(), &lib_id);
2827 assert_eq!(resolved_name.as_str(), "Impulsive");
2828 }
2829
2830 #[test]
2831 fn selective_pub_reexport_resolves_to_original_owner() {
2832 let leaf_id = DagId::root("leaf");
2833 let middle_id = DagId::root("middle");
2834 let main_id = DagId::root("main");
2835 let leaf = desugared_source("pub dim Acceleration = Length / Time^2;");
2836 let middle = desugared_source("import leaf.{ pub Acceleration };");
2837 let main = desugared_source("import middle.{ Acceleration };");
2838 let (middle_import_path, middle_import_kind) = first_import(&middle);
2839 let (main_import_path, main_import_kind) = first_import(&main);
2840
2841 let mut resolver = ModuleResolver::default();
2842 resolver
2843 .add_module(leaf_id.clone(), &leaf.declarations)
2844 .unwrap();
2845 resolver
2846 .add_module(middle_id.clone(), &middle.declarations)
2847 .unwrap();
2848 resolver
2849 .add_module(main_id.clone(), &main.declarations)
2850 .unwrap();
2851 resolver
2852 .register_import(&middle_id, middle_import_path, middle_import_kind, &leaf_id)
2853 .unwrap();
2854 resolver
2855 .register_import(&main_id, main_import_path, main_import_kind, &middle_id)
2856 .unwrap();
2857
2858 let resolved_name = resolver
2859 .resolve_dimension_path(&main_id, &path(&["Acceleration"]))
2860 .unwrap();
2861
2862 assert_eq!(resolved_name.owner(), &leaf_id);
2863 assert_eq!(resolved_name.as_str(), "Acceleration");
2864 }
2865}