1use colored::Colorize;
2use itertools::{EitherOrBoth, Itertools};
3use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
4use serde::{Deserialize, Serialize};
5
6use spade_common::id_tracker::NameIdTracker;
7use spade_common::location_info::{Loc, WithLocation};
8use spade_common::name::{Identifier, NameID, Path, PathPrefix, PathSegment, Visibility};
9use spade_diagnostics::diagnostic::{Diagnostic, Subdiagnostic};
10use spade_types::meta_types::MetaType;
11
12use crate::{FunctionKind, ParameterList, TraitSpec, TypeParam, TypeSpec, UnitHead, UnitKind};
13
14#[derive(Debug, Clone, PartialEq)]
15pub enum LookupError {
16 NoSuchSymbol(Loc<Path>),
17 NotAThing(Loc<Path>),
18 NotATypeSymbol(Loc<Path>, Thing),
19 NotAVariable(Loc<Path>, Thing),
20 NotAUnit(Loc<Path>, Thing),
21 NotAnEnumVariant(Loc<Path>, Thing),
22 NotAPatternableType(Loc<Path>, Thing),
23 NotAStruct(Loc<Path>, Thing),
24 NotAValue(Loc<Path>, Thing),
25 NotAComptimeValue(Loc<Path>, Thing),
26 NotATrait(Loc<Path>, Thing),
27 IsAType(Loc<Path>, Loc<()>),
28 BarrierError(Diagnostic),
29 TooManySuperSegments(Loc<Path>, PathSegment),
30 NotVisible {
31 target_segment: PathSegment,
32 invisible_segment: PathSegment,
33 invisible_item: Option<Loc<()>>,
36 target_item: Option<Loc<()>>,
40 visibility_marker: Option<Loc<()>>,
41 suggest_modifications: bool,
42 },
43}
44
45impl From<LookupError> for Diagnostic {
46 fn from(lookup_error: LookupError) -> Diagnostic {
47 match &lookup_error {
48 LookupError::NoSuchSymbol(path) => {
49 Diagnostic::error(path, format!("Use of undeclared name {path}"))
50 .primary_label("Undeclared name")
51 }
52 LookupError::NotAThing(path) => {
53 Diagnostic::error(path, format!("Use of {path} before it was declared"))
54 .primary_label("Undeclared name")
55 }
56 LookupError::IsAType(path, loc) => {
57 Diagnostic::error(path, format!("Unexpected type {path}"))
58 .primary_label("Unexpected type")
59 .secondary_label(loc, format!("{path} is defined as a type here"))
60 }
61 LookupError::BarrierError(diag) => diag.clone(),
62 LookupError::NotATypeSymbol(path, got)
63 | LookupError::NotAVariable(path, got)
64 | LookupError::NotAUnit(path, got)
65 | LookupError::NotAnEnumVariant(path, got)
66 | LookupError::NotAPatternableType(path, got)
67 | LookupError::NotAStruct(path, got)
68 | LookupError::NotAValue(path, got)
69 | LookupError::NotATrait(path, got)
70 | LookupError::NotAComptimeValue(path, got) => {
71 let expected = match lookup_error {
72 LookupError::NotATypeSymbol(_, _) => "a type",
73 LookupError::NotAVariable(_, _) => "a variable",
74 LookupError::NotAUnit(_, _) => "a unit",
75 LookupError::NotAnEnumVariant(_, _) => "an enum variant",
76 LookupError::NotAPatternableType(_, _) => "a patternable type",
77 LookupError::NotAStruct(_, _) => "a struct",
78 LookupError::NotAValue(_, _) => "a value",
79 LookupError::NotAComptimeValue(_, _) => "a compile time value",
80 LookupError::NotATrait(_, _) => "a trait",
81 LookupError::NoSuchSymbol(_)
82 | LookupError::IsAType(_, _)
83 | LookupError::BarrierError(_)
84 | LookupError::TooManySuperSegments(_, _)
85 | LookupError::NotVisible { .. }
86 | LookupError::NotAThing(_) => unreachable!(),
87 };
88
89 let hint = match lookup_error {
91 LookupError::NotAComptimeValue(_, _) => {
92 Some("compile time values can be defined with $config <name> = value")
93 }
94 _ => None,
95 };
96 let mut diagnostic =
97 Diagnostic::error(path, format!("Expected {path} to be {expected}"))
98 .primary_label(format!("Expected {expected}"))
99 .secondary_label(got.loc(), format!("{path} is a {}", got.kind_string()));
100
101 if let Some(hint) = hint {
102 diagnostic.add_help(hint);
103 }
104
105 match lookup_error {
106 LookupError::NotAValue(path, Thing::EnumVariant(v)) => diagnostic
107 .span_suggest_insert_after(
108 "Consider specifying the arguments to the variant",
109 path,
110 format!(
111 "({})",
112 v.inner
113 .params
114 .0
115 .iter()
116 .map(|a| format!("/* {} */", a.name))
117 .join(", ")
118 ),
119 ),
120 LookupError::NotAValue(path, Thing::Struct(v)) => diagnostic
121 .span_suggest_insert_after(
122 "Consider specifying the struct parameters",
123 path,
124 format!(
125 "({})",
126 v.inner
127 .params
128 .0
129 .iter()
130 .map(|a| format!("/*{}*/", a.name))
131 .join(", ")
132 ),
133 ),
134 _ => diagnostic,
135 }
136 }
137 LookupError::TooManySuperSegments(path, segment) => {
138 Diagnostic::error(path, "Too many `super` segments")
139 .primary_label("Too many `super` segments")
140 .subdiagnostic(Subdiagnostic::span_note(
141 segment.loc(),
142 "parent does not exist for the root namespace",
143 ))
144 }
145 LookupError::NotVisible {
146 target_segment,
147 invisible_segment,
148 target_item,
149 invisible_item,
150 visibility_marker,
151 suggest_modifications,
152 } => {
153 let diag = Diagnostic::error(
154 target_segment.loc(),
155 format!("{} is inaccessible", target_segment),
156 )
157 .primary_label(format!("{} is inaccessible", target_segment));
158
159 let diag = if *suggest_modifications {
160 if let Some(pub_insertion) = invisible_item.or(*target_item) {
161 if let Some(marker) = visibility_marker {
162 diag.span_suggest_replace(
163 "Consider changing the visibility to `pub`",
164 marker,
165 "pub",
166 )
167 } else {
168 diag.span_suggest_insert_before(
169 "Consider adding `pub`",
170 pub_insertion,
171 "pub ",
172 )
173 .span_suggest_insert_before(
174 "Or a more limited visibility like pub(lib) or pub(super)",
175 pub_insertion,
176 "pub(lib) ",
177 )
178 }
179 } else {
180 diag
181 }
182 } else {
183 diag
184 };
185
186 let diag = if !invisible_segment.loc().is_same_loc(&target_segment.loc()) {
187 diag.secondary_label(
188 invisible_segment.loc(),
189 format!("Because {invisible_segment} is inaccessible"),
190 )
191 } else {
192 diag
193 };
194
195 let diag = if let Some(loc) = target_item {
196 diag.secondary_label(loc, "The inaccessible item is defined here")
197 } else {
198 diag
199 };
200
201 if let Some(loc) = invisible_item {
202 diag.secondary_label(
203 loc,
204 format!(
205 "The item is inaccessible because {invisible_segment} is inaccessible"
206 ),
207 )
208 } else {
209 diag
210 }
211 }
212 }
213 }
214}
215
216#[derive(Debug, Clone, PartialEq)]
217pub enum UniqueNameError {
218 MultipleDefinitions { new: Loc<Path>, prev: Loc<()> },
219}
220
221#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
222pub struct EnumVariant {
223 pub name: Loc<Identifier>,
224 pub output_type: Loc<TypeSpec>,
225 pub option: usize,
226 pub params: Loc<ParameterList>,
227 pub type_params: Vec<Loc<TypeParam>>,
228 pub documentation: String,
229}
230
231impl EnumVariant {
232 pub fn as_unit_head(&self) -> UnitHead {
233 UnitHead {
234 name: self.name.clone(),
235 is_nonstatic_method: false,
236 inputs: self.params.clone(),
237 output_type: Some(self.output_type.clone()),
238 unit_type_params: self.type_params.clone(),
239 scope_type_params: self.type_params.clone(),
240 unit_kind: UnitKind::Function(FunctionKind::Enum).at_loc(&self.name),
241 where_clauses: vec![],
242 unsafe_marker: None,
243 documentation: String::new(),
244 }
245 }
246}
247
248#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
249pub struct StructCallable {
250 pub name: Loc<Identifier>,
251 pub self_type: Loc<TypeSpec>,
252 pub params: Loc<ParameterList>,
253 pub type_params: Vec<Loc<TypeParam>>,
254}
255impl StructCallable {
256 pub fn as_unit_head(&self) -> UnitHead {
257 UnitHead {
258 name: self.name.clone(),
259 is_nonstatic_method: false,
260 inputs: self.params.clone(),
261 output_type: Some(self.self_type.clone()),
262 unit_type_params: self.type_params.clone(),
263 scope_type_params: vec![],
264 unit_kind: UnitKind::Function(FunctionKind::Struct).at_loc(&self.name),
265 where_clauses: vec![],
266 unsafe_marker: None,
267 documentation: String::new(),
268 }
269 }
270}
271
272#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
273pub struct TraitMarker {
274 pub name: Loc<Identifier>,
275 pub paren_sugar: bool,
276}
277
278#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
281pub enum Thing {
282 Struct(Loc<StructCallable>),
284 EnumVariant(Loc<EnumVariant>),
285 Unit(Loc<UnitHead>),
286 Variable(Loc<Identifier>),
287 Alias {
288 loc: Loc<()>,
289 path: Loc<Path>,
290 in_namespace: Path,
291 },
292 ArrayLabel(Loc<usize>),
293 Module(Loc<()>, Loc<Identifier>),
294 Trait(Loc<TraitMarker>),
297 Dummy,
301}
302
303impl Thing {
304 pub fn kind_string(&self) -> &'static str {
305 match self {
306 Thing::Struct(_) => "struct",
307 Thing::Unit(_) => "unit",
308 Thing::Variable(_) => "variable",
309 Thing::EnumVariant(_) => "enum variant",
310 Thing::Alias { .. } => "alias",
311 Thing::ArrayLabel(_) => "array label",
312 Thing::Trait(_) => "trait",
313 Thing::Module(_, _) => "module",
314 Thing::Dummy => "dummy (implementation detail)",
315 }
316 }
317
318 pub fn loc(&self) -> Loc<()> {
320 match self {
321 Thing::Struct(i) => i.loc(),
322 Thing::Variable(i) => i.loc(),
323 Thing::Unit(i) => i.loc(),
324 Thing::EnumVariant(i) => i.loc(),
325 Thing::Alias {
326 loc,
327 path: _,
328 in_namespace: _,
329 } => loc.loc(),
330 Thing::ArrayLabel(v) => v.loc(),
331 Thing::Trait(loc) => loc.loc(),
332 Thing::Module(loc, _name) => loc.loc(),
333 Thing::Dummy => ().nowhere(),
334 }
335 }
336
337 pub fn name_loc(&self) -> Loc<()> {
339 match self {
340 Thing::Struct(s) => s.name.loc(),
341 Thing::EnumVariant(v) => v.name.loc(),
342 Thing::Unit(f) => f.name.loc(),
343 Thing::Variable(v) => v.loc(),
344 Thing::ArrayLabel(l) => l.loc(),
345 Thing::Alias {
346 loc: _,
347 path,
348 in_namespace: _,
349 } => path.loc(),
350 Thing::Trait(loc) => loc.loc(),
351 Thing::Module(_loc, name) => name.loc(),
352 Thing::Dummy => ().nowhere(),
353 }
354 }
355}
356
357#[derive(PartialEq, Debug, Clone)]
358pub enum PatternableKind {
359 Struct,
360 Enum,
361}
362#[derive(PartialEq, Debug, Clone)]
363pub struct Patternable {
364 pub kind: PatternableKind,
365 pub params: Loc<ParameterList>,
366}
367
368#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
369pub enum GenericArg {
370 TypeName {
371 name: Identifier,
372 traits: Vec<Loc<TraitSpec>>,
373 },
374 TypeWithMeta {
375 name: Identifier,
376 meta: MetaType,
377 },
378}
379
380impl GenericArg {
381 pub fn uint(name: Identifier) -> Self {
382 GenericArg::TypeWithMeta {
383 name,
384 meta: MetaType::Uint,
385 }
386 }
387}
388
389#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
390pub enum TypeDeclKind {
391 Struct { is_port: bool },
392 Enum,
393 Primitive { is_port: bool, is_inout: bool },
394}
395
396impl TypeDeclKind {
397 pub fn normal_struct() -> Self {
398 TypeDeclKind::Struct { is_port: false }
399 }
400 pub fn struct_port() -> Self {
401 TypeDeclKind::Struct { is_port: true }
402 }
403
404 pub fn name(&self) -> String {
405 match self {
406 TypeDeclKind::Struct { is_port } => {
407 format!("struct{}", if *is_port { " port" } else { "" })
408 }
409 TypeDeclKind::Enum => "enum".to_string(),
410 TypeDeclKind::Primitive { .. } => "primitive".to_string(),
411 }
412 }
413}
414
415#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
417pub enum TypeSymbol {
418 Declared(Vec<Loc<GenericArg>>, TypeDeclKind),
421 GenericArg {
423 traits: Vec<Loc<TraitSpec>>,
424 },
425 GenericMeta(MetaType),
426 Alias(Loc<TypeSpec>),
429}
430
431#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
433pub enum DeclarationState {
434 Undefined(NameID),
436 Undecleared(NameID),
438 Defined(Loc<()>),
441}
442
443pub type ScopeBarrier =
444 dyn Fn(&Loc<Path>, &Loc<NameID>, &Thing) -> Result<(), Diagnostic> + Send + Sync;
445
446#[derive(Serialize, Deserialize)]
447pub struct Scope {
448 vars: HashMap<Path, NameID>,
449 #[serde(skip)]
450 lookup_barrier: Option<Box<ScopeBarrier>>,
451}
452impl std::fmt::Debug for Scope {
453 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454 let Self {
455 vars,
456 lookup_barrier: _,
457 } = self;
458 write!(f, "Scope({vars:?})")
459 }
460}
461
462#[derive(Debug, Serialize, Deserialize)]
470pub struct SymbolTable {
471 pub symbols: Vec<Scope>,
473 pub declarations: Vec<HashMap<Loc<Identifier>, DeclarationState>>,
474 id_tracker: NameIdTracker,
475 pub types: HashMap<NameID, Loc<TypeSymbol>>,
476 pub things: HashMap<NameID, Thing>,
477 pub visibilities: HashMap<NameID, Loc<Visibility>>,
478 namespace: Path,
482 base_namespace: Path,
484}
485
486impl Default for SymbolTable {
487 fn default() -> Self {
488 Self::new()
489 }
490}
491
492impl SymbolTable {
493 pub fn new() -> Self {
494 let mut result = Self {
495 symbols: vec![Scope {
496 vars: HashMap::default(),
497 lookup_barrier: None,
498 }],
499 declarations: vec![HashMap::default()],
500 id_tracker: NameIdTracker::new(),
501 types: HashMap::default(),
502 things: HashMap::default(),
503 visibilities: HashMap::default(),
504 namespace: Path(vec![]),
505 base_namespace: Path(vec![]),
506 };
507
508 result.add_thing(
509 Path(vec![]),
510 Thing::Module(().nowhere(), Identifier::intern("<root>").nowhere()),
511 None,
512 );
513
514 result
515 }
516 #[tracing::instrument(skip_all)]
517 pub fn new_scope(&mut self) {
518 self.symbols.push(Scope {
519 vars: HashMap::default(),
520 lookup_barrier: None,
521 });
522 self.declarations.push(HashMap::default());
523 }
524
525 pub fn new_scope_with_barrier(&mut self, barrier: Box<ScopeBarrier>) {
526 self.symbols.push(Scope {
527 vars: HashMap::default(),
528 lookup_barrier: Some(barrier),
529 });
530 self.declarations.push(HashMap::default());
531 }
532
533 #[tracing::instrument(skip_all)]
534 pub fn close_scope(&mut self) {
535 self.symbols.pop();
536 self.declarations.pop();
537 }
538
539 pub fn current_scope(&self) -> usize {
540 self.symbols.len() - 1
541 }
542
543 #[tracing::instrument(skip_all, fields(%new_segment))]
547 pub fn push_namespace(&mut self, new_segment: PathSegment) {
548 self.namespace = self.namespace.push_segment(new_segment);
549 }
550
551 #[tracing::instrument(skip_all)]
552 pub fn pop_namespace(&mut self) {
553 self.namespace = self.namespace.pop();
554 }
555
556 pub fn current_namespace(&self) -> &Path {
557 &self.namespace
558 }
559
560 pub fn set_base_namespace(&mut self, base_namespace: Path) {
561 self.base_namespace = base_namespace
562 }
563
564 pub fn add_thing_with_id_at_offset(
566 &mut self,
567 offset: usize,
568 id: u64,
569 name: Path,
570 item: Thing,
571 visibility: Option<Loc<Visibility>>,
572 ) -> NameID {
573 let full_name = self.namespace.join(name);
574 let name_id = NameID(id, full_name.clone());
575
576 if self.things.contains_key(&name_id) {
577 panic!("Duplicate nameID inserted, {}", id);
578 }
579 if offset > self.symbols.len() {
580 panic!("Not enough scopes to add symbol at offset {}", offset);
581 }
582
583 let index = self.symbols.len() - 1 - offset;
584 self.symbols[index].vars.insert(full_name, name_id.clone());
585 self.add_thing_with_name_id(name_id.clone(), item, visibility);
586
587 name_id
588 }
589
590 pub fn add_thing_with_name_id(
593 &mut self,
594 name_id: NameID,
595 item: Thing,
596 visibility: Option<Loc<Visibility>>,
597 ) {
598 self.things.insert(name_id.clone(), item);
599 if let Some(vis) = visibility {
600 self.visibilities.insert(name_id, vis);
601 }
602 }
603
604 pub fn add_thing_with_id(
605 &mut self,
606 id: u64,
607 name: Path,
608 item: Thing,
609 visibility: Option<Loc<Visibility>>,
610 ) -> NameID {
611 self.add_thing_with_id_at_offset(0, id, name, item, visibility)
612 }
613
614 #[tracing::instrument(skip_all, fields(?name))]
615 pub fn add_unique_thing(
616 &mut self,
617 name: Loc<Path>,
618 item: Thing,
619 visibility: Option<Loc<Visibility>>,
620 ) -> Result<NameID, Diagnostic> {
621 self.ensure_is_unique(&name)?;
622 Ok(self.add_thing(name.inner, item, visibility))
623 }
624
625 pub fn add_thing(
626 &mut self,
627 name: Path,
628 item: Thing,
629 visibility: Option<Loc<Visibility>>,
630 ) -> NameID {
631 let id = self.id_tracker.next();
632 self.add_thing_with_id(id, name, item, visibility)
633 }
634
635 pub fn re_add_type(&mut self, name: Loc<Identifier>, name_id: NameID) {
636 assert!(self.types.contains_key(&name_id));
637 self.symbols
638 .last_mut()
639 .unwrap()
640 .vars
641 .insert(self.namespace.join(Path::ident(name)), name_id);
642 }
643
644 pub fn add_type_with_id(
645 &mut self,
646 id: u64,
647 name: Loc<Identifier>,
648 t: Loc<TypeSymbol>,
649 visibility: Loc<Visibility>,
650 ) -> NameID {
651 let full_name = self.namespace.push_ident(name);
652 let name_id = NameID(id, full_name.clone());
653
654 if self.types.contains_key(&name_id) {
655 panic!("Duplicate nameID for types, {}", id)
656 }
657
658 self.types.insert(name_id.clone(), t);
659 self.visibilities.insert(name_id.clone(), visibility);
660 self.symbols
661 .last_mut()
662 .unwrap()
663 .vars
664 .insert(full_name, name_id.clone());
665
666 name_id
667 }
668
669 pub fn add_type(
670 &mut self,
671 name: Loc<Identifier>,
672 t: Loc<TypeSymbol>,
673 visibility: Loc<Visibility>,
674 ) -> NameID {
675 let id = self.id_tracker.next();
676 self.add_type_with_id(id, name, t, visibility)
677 }
678
679 pub fn add_traits_to_generic(
680 &mut self,
681 name_id: &NameID,
682 traits: Vec<Loc<TraitSpec>>,
683 ) -> Result<(), Diagnostic> {
684 assert!(self.types.contains_key(name_id));
685 match &mut self.types.get_mut(name_id).unwrap().inner {
686 TypeSymbol::GenericArg { traits: existing } => {
687 existing.extend(traits);
688 Ok(())
689 }
690 _ => Err(Diagnostic::bug(
691 self.type_symbol_by_id(name_id).loc(),
692 "Attempted to add trait bounds to a non-generic type",
693 )),
694 }
695 }
696
697 pub fn add_unique_type(
698 &mut self,
699 name: Loc<Identifier>,
700 t: Loc<TypeSymbol>,
701 visibility: Loc<Visibility>,
702 ) -> Result<NameID, Diagnostic> {
703 self.ensure_is_unique(&Path::ident_with_loc(name.clone()))?;
704 Ok(self.add_type(name, t, visibility))
705 }
706
707 #[tracing::instrument(skip_all, fields(?name, ?target))]
708 pub fn add_alias(
709 &mut self,
710 loc: Loc<()>,
711 name: Loc<Identifier>,
712 target: Loc<Path>,
713 visibility: Loc<Visibility>,
714 ) -> Result<NameID, Diagnostic> {
715 self.ensure_is_unique(&Path::ident_with_loc(name.clone()))?;
716
717 Ok(self.add_thing(
718 Path::ident(name),
719 Thing::Alias {
720 loc: loc,
721 path: target,
722 in_namespace: self.current_namespace().clone(),
723 },
724 Some(visibility),
725 ))
726 }
727
728 pub fn add_thing_at_offset(
730 &mut self,
731 offset: usize,
732 name: Path,
733 item: Thing,
734 visibility: Option<Loc<Visibility>>,
735 ) -> NameID {
736 let id = self.id_tracker.next();
737 self.add_thing_with_id_at_offset(offset, id, name, item, visibility)
738 }
739
740 pub fn freeze(self) -> FrozenSymtab {
741 let id_tracker = self.id_tracker.make_clone();
742 FrozenSymtab {
743 inner: self,
744 id_tracker: id_tracker,
745 }
746 }
747
748 pub fn add_local_variable(&mut self, name: Loc<Identifier>) -> NameID {
749 self.add_local_variable_at_offset(0, name)
750 }
751 pub fn add_local_variable_at_offset(&mut self, offset: usize, name: Loc<Identifier>) -> NameID {
752 self.add_thing_at_offset(
753 offset,
754 Path::ident(name.clone()),
755 Thing::Variable(name),
756 None,
757 )
758 }
759
760 pub fn add_dummy(&mut self, segment: PathSegment) -> NameID {
761 self.add_thing(Path(vec![segment]), Thing::Dummy, None)
762 }
763
764 pub fn add_declaration(&mut self, ident: Loc<Identifier>) -> Result<NameID, Diagnostic> {
765 let declared_more_than_once = |new, old| {
766 Diagnostic::error(new, "Variable declared more than once")
767 .primary_label("This variable has been declared more than once")
768 .secondary_label(old, "Previously declared here")
769 };
770 if let Some(id) = self.try_lookup_id(&Path::ident_with_loc(ident.clone())) {
772 if let Some(Thing::Variable(prev)) = self.things.get(&id) {
773 return Err(declared_more_than_once(ident, prev));
774 }
775 }
776
777 if let Some((old, _)) = self.declarations.last().unwrap().get_key_value(&ident) {
778 Err(declared_more_than_once(ident, old))
779 } else {
780 let name_id = self.add_local_variable(ident.clone());
781 self.declarations
782 .last_mut()
783 .unwrap()
784 .insert(ident, DeclarationState::Undefined(name_id.clone()));
785 Ok(name_id)
786 }
787 }
788
789 pub fn add_undecleared_at_offset(&mut self, offset: usize, name: Loc<Identifier>) -> NameID {
790 let path = Path::ident(name.clone());
791
792 let name_id = NameID(self.id_tracker.next(), path.clone());
793 let full_name = self.namespace.join(path);
794
795 let index = self.symbols.len() - 1 - offset;
796 if index > self.symbols.len() {
797 panic!("Not enough scopes to add symbol at offset {}", offset);
798 }
799 self.symbols[index].vars.insert(full_name, name_id.clone());
800 self.declarations[index].insert(name, DeclarationState::Undecleared(name_id.clone()));
801
802 name_id
803 }
804
805 pub fn get_declaration(&mut self, ident: &Loc<Identifier>) -> Option<Loc<DeclarationState>> {
806 self.declarations
807 .last()
808 .unwrap()
809 .get_key_value(ident)
810 .map(|(k, v)| v.clone().at_loc(k))
811 }
812
813 pub fn mark_declaration_defined(&mut self, ident: Loc<Identifier>, definition_point: Loc<()>) {
814 *self
815 .declarations
816 .last_mut()
817 .unwrap()
818 .get_mut(&ident)
819 .unwrap() = DeclarationState::Defined(definition_point)
820 }
821
822 pub fn get_undefined_declarations(&self) -> Vec<(Loc<Identifier>, DeclarationState)> {
823 self.declarations
824 .last()
825 .unwrap()
826 .iter()
827 .filter_map(|(ident, state)| match state {
828 DeclarationState::Undefined(_) => Some((ident.clone(), state.clone())),
829 DeclarationState::Undecleared(_) => Some((ident.clone(), state.clone())),
830 DeclarationState::Defined(_) => None,
831 })
832 .collect()
833 }
834
835 pub fn lookup_thing_impl(
836 &self,
837 path: &Loc<Path>,
838 check_visibility: bool,
839 ) -> Result<(NameID, &Thing), LookupError> {
840 let id = self.lookup_id(path, check_visibility)?;
841
842 match self.things.get(&id) {
843 Some(thing) => Ok((id, thing)),
844 None => match self.types.get(&id) {
845 Some(ty) => Err(LookupError::IsAType(path.clone(), ty.loc())),
846 None => Err(LookupError::NotAThing(path.clone())),
847 },
848 }
849 }
850
851 pub fn lookup_thing_ignore_visibility(
852 &self,
853 path: &Loc<Path>,
854 ) -> Result<(NameID, &Thing), LookupError> {
855 self.lookup_thing_impl(path, false)
856 }
857
858 pub fn lookup_thing(&self, path: &Loc<Path>) -> Result<(NameID, &Thing), LookupError> {
859 self.lookup_thing_impl(path, true)
860 }
861}
862
863macro_rules! thing_accessors {
864 (
865 $(
866 $by_id_name:ident,
867 $lookup_name:ident,
868 $lookup_ignore_visibility:ident,
869 $result:path,
870 $err:ident $(,)?
871 {$($thing:pat => $conversion:expr),*$(,)?}
872 ),*
873 ) => {
874 $(
875 pub fn $by_id_name(&self, id: &NameID) -> Loc<$result> {
878 match self.things.get(&id) {
879 $(
880 Some($thing) => {$conversion}
881 )*,
882 Some(other) => panic!("attempted to look up {} but it was {:?}", stringify!($result), other),
883 None => panic!("No thing entry found for {:?}", id)
884 }
885 }
886
887 #[tracing::instrument(level = "trace", skip_all, fields(%name.inner, %name.span, %name.file_id))]
890 pub fn $lookup_name(&self, name: &Loc<Path>) -> Result<(NameID, Loc<$result>), LookupError> {
891 let (id, thing) = self.lookup_thing(name)?;
892
893 match thing {
894 $(
895 $thing => {Ok((id, $conversion))}
896 )*,
897 other => Err(LookupError::$err(name.clone(), other.clone())),
898 }
899 }
900
901 #[tracing::instrument(level = "trace", skip_all, fields(%name.inner, %name.span, %name.file_id))]
904 pub fn $lookup_ignore_visibility(&self, name: &Loc<Path>) -> Result<(NameID, Loc<$result>), LookupError> {
905 let (id, thing) = self.lookup_thing_ignore_visibility(name)?;
906
907 match thing {
908 $(
909 $thing => {Ok((id, $conversion))}
910 )*,
911 other => Err(LookupError::$err(name.clone(), other.clone())),
912 }
913 }
914 )*
915 }
916}
917
918impl SymbolTable {
919 thing_accessors! {
926 unit_by_id, lookup_unit, lookup_unit_ignore_visibility, UnitHead, NotAUnit {
927 Thing::Unit(head) => head.clone(),
928 Thing::EnumVariant(variant) => variant.as_unit_head().at_loc(variant),
929 Thing::Struct(s) => s.as_unit_head().at_loc(s),
930 },
931 enum_variant_by_id, lookup_enum_variant, lookup_enum_variant_ignore_visibility, EnumVariant, NotAnEnumVariant {
932 Thing::EnumVariant(variant) => variant.clone()
933 },
934 patternable_type_by_id, lookup_patternable_type, lookup_patternable_type_ignore_visibility, Patternable, NotAPatternableType {
935 Thing::EnumVariant(variant) => Patternable{
936 kind: PatternableKind::Enum,
937 params: variant.params.clone()
938 }.at_loc(variant),
939 Thing::Struct(variant) => Patternable {
940 kind: PatternableKind::Struct,
941 params: variant.params.clone()
942 }.at_loc(variant),
943 },
944 struct_by_id, lookup_struct, lookup_struct_ignore_visibility, StructCallable, NotAStruct {
945 Thing::Struct(s) => s.clone()
946 },
947 trait_by_id, lookup_trait, lookup_trait_ignore_visibility, TraitMarker, NotATrait {
948 Thing::Trait(t) => t.clone()
949 }
950 }
951
952 pub fn type_symbol_by_id(&self, id: &NameID) -> Loc<TypeSymbol> {
953 match self.types.get(id) {
954 Some(inner) => inner.clone(),
955 None => panic!("No thing entry found for {:?}", id),
956 }
957 }
958
959 pub fn try_type_symbol_by_id(&self, id: &NameID) -> Option<&Loc<TypeSymbol>> {
960 self.types.get(id)
961 }
962
963 pub fn thing_by_id(&self, id: &NameID) -> Option<&Thing> {
964 self.things.get(id)
965 }
966
967 pub fn lookup_type_symbol(
968 &self,
969 name: &Loc<Path>,
970 ) -> Result<(NameID, Loc<TypeSymbol>), LookupError> {
971 let id = self.lookup_id(name, true)?;
972
973 match self.types.get(&id) {
974 Some(tsym) => Ok((id, tsym.clone())),
975 None => match self.things.get(&id) {
976 Some(thing) => Err(LookupError::NotATypeSymbol(name.clone(), thing.clone())),
977 None => panic!("{:?} was in symtab but is neither a type nor a thing", id),
978 },
979 }
980 }
981
982 pub fn has_symbol(&self, name: Path) -> bool {
983 match self.lookup_id(&name.nowhere(), false) {
984 Ok(_) => true,
985 Err(LookupError::NoSuchSymbol(_)) => false,
986 Err(LookupError::BarrierError(_)) => unreachable!(),
987 Err(LookupError::NotATypeSymbol(_, _)) => unreachable!(),
988 Err(LookupError::NotAVariable(_, _)) => unreachable!(),
989 Err(LookupError::NotAUnit(_, _)) => unreachable!(),
990 Err(LookupError::NotAPatternableType(_, _)) => unreachable!(),
991 Err(LookupError::NotAnEnumVariant(_, _)) => unreachable!(),
992 Err(LookupError::NotAStruct(_, _)) => unreachable!(),
993 Err(LookupError::NotAValue(_, _)) => unreachable!(),
994 Err(LookupError::NotAComptimeValue(_, _)) => unreachable!(),
995 Err(LookupError::NotATrait(_, _)) => unreachable!(),
996 Err(LookupError::IsAType(_, _)) => unreachable!(),
997 Err(LookupError::NotAThing(_)) => unreachable!(),
998 Err(LookupError::TooManySuperSegments(_, _)) => unreachable!(),
999 Err(LookupError::NotVisible { .. }) => unreachable!(),
1000 }
1001 }
1002
1003 pub fn ensure_is_unique(&self, name: &Loc<Path>) -> Result<(), Diagnostic> {
1007 let full_path = self.current_namespace().join(name.inner.clone());
1008
1009 let prev = self
1010 .symbols
1011 .first()
1012 .unwrap()
1013 .vars
1014 .get(&full_path)
1015 .and_then(|id| {
1016 self.things
1017 .get(id)
1018 .map(|thing| thing.name_loc())
1019 .or_else(|| self.types.get(id).map(|t| t.loc()))
1020 });
1021
1022 match prev {
1023 Some(prev) => Err(Diagnostic::error(name, "Multiple items with the same name")
1024 .primary_label(format!("{} is defined multiple times", name))
1025 .secondary_label(prev, "Previous definition here")),
1026 None => Ok(()),
1027 }
1028 }
1029
1030 pub fn lookup_variable(&self, name: &Loc<Path>) -> Result<NameID, LookupError> {
1031 let id = self.lookup_id(name, false)?;
1032
1033 match self.things.get(&id) {
1034 Some(Thing::Variable(_)) => Ok(id),
1035 Some(other) => Err(LookupError::NotAVariable(name.clone(), other.clone())),
1036 None => match self.types.get(&id) {
1037 Some(ty) => Err(LookupError::IsAType(name.clone(), ty.loc())),
1038 None => Err(LookupError::NotAThing(name.clone())),
1039 },
1040 }
1041 }
1042
1043 pub fn try_lookup_variable(&self, name: &Loc<Path>) -> Result<Option<NameID>, LookupError> {
1048 let id = self.try_lookup_id(name);
1049 match id {
1050 Some(id) => match self.things.get(&id) {
1051 Some(Thing::Variable(_)) => Ok(Some(id)),
1052 Some(other) => Err(LookupError::NotAVariable(name.clone(), other.clone())),
1053 None => match self.types.get(&id) {
1054 Some(ty) => Err(LookupError::IsAType(name.clone(), ty.loc())),
1055 None => Ok(None),
1056 },
1057 },
1058 None => Ok(None),
1059 }
1060 }
1061
1062 pub fn try_lookup_id(&self, name: &Loc<Path>) -> Option<NameID> {
1063 match self.lookup_id(name, true) {
1064 Ok(id) => Some(id),
1065 Err(LookupError::NoSuchSymbol(_)) => None,
1066 Err(err) => unreachable!("Got {err:?} when looking up final ID"),
1067 }
1068 }
1069
1070 pub fn lookup_id(
1073 &self,
1074 name: &Loc<Path>,
1075 check_visibility: bool,
1076 ) -> Result<NameID, LookupError> {
1077 self.lookup_id_in_namespace(name, &self.namespace, check_visibility)
1078 }
1079
1080 pub fn lookup_id_in_namespace(
1083 &self,
1084 name: &Loc<Path>,
1085 namespace: &Path,
1086 check_visibility: bool,
1087 ) -> Result<NameID, LookupError> {
1088 let (offset, path) = self.canonicalize_in_namespace(
1089 name,
1090 namespace,
1091 &Default::default(),
1092 0,
1093 check_visibility,
1094 )?;
1095
1096 let id = self.symbols[self.current_scope() - offset]
1097 .vars
1098 .get(&path)
1099 .expect("Canonical path not present in symbol table (that is impossible)")
1100 .clone();
1101
1102 if let Some(thing) = self.things.get(&id) {
1103 self.symbols
1104 .iter()
1105 .rev()
1106 .take(offset)
1107 .flat_map(|s| &s.lookup_barrier)
1108 .try_for_each(|b| (b)(name, &id.clone().at_loc(&thing.name_loc()), thing))
1109 .map_err(LookupError::BarrierError)?;
1110 }
1111
1112 Ok(id)
1113 }
1114
1115 fn canonicalize_in_namespace(
1116 &self,
1117 path: &Loc<Path>,
1118 namespace: &Path,
1119 forbidden: &HashSet<NameID>,
1120 offset: usize,
1121 check_visibility: bool,
1122 ) -> Result<(usize, Path), LookupError> {
1123 let (mut scope_offset, mut working_path, segments) = match path.extract_prefix() {
1125 (PathPrefix::FromLib, lib_relative_path) => {
1126 let segments = lib_relative_path.0.clone();
1127 let off = self.current_scope();
1128 let ns = self.base_namespace.clone();
1129
1130 (off, ns, segments)
1131 }
1132 (PathPrefix::FromSuper(levels), super_relative_path) => {
1133 let segments = super_relative_path.0.clone();
1134 let off = self.current_scope();
1135
1136 let ns = if namespace.0.len() >= levels {
1137 Path(Vec::from(&namespace.0[0..namespace.0.len() - levels]))
1138 } else {
1139 let segment = path.0[levels - 1].clone();
1140 return Err(LookupError::TooManySuperSegments(path.clone(), segment));
1141 };
1142
1143 (off, ns, segments)
1144 }
1145 (PathPrefix::FromSelf, self_relative_path) => {
1146 let mut segments = self_relative_path.0.clone();
1147 let mut off = self.current_scope();
1148 let ns = namespace.clone();
1149
1150 if self_relative_path.0.is_empty() {
1153 let head = path.0[0].clone();
1154 let absolute_head = namespace.push_segment(head);
1155
1156 if let Some((new_offset, _)) = self
1157 .symbols
1158 .iter()
1159 .rev()
1160 .enumerate()
1161 .skip(offset)
1162 .find(|(_, s)| s.vars.contains_key(&absolute_head))
1163 {
1164 segments = path.0.clone();
1165 off = new_offset;
1166 }
1167 }
1168
1169 (off, ns, segments)
1170 }
1171 (PathPrefix::None, _) => {
1172 if path.0.is_empty() {
1173 (0, self.current_namespace().clone(), vec![])
1174 } else {
1175 let segments = path.0.clone();
1176 let head = path.0[0].clone();
1177 let absolute_head = namespace.push_segment(head.clone());
1178
1179 let (off, ns) = self
1180 .symbols
1181 .iter()
1182 .rev()
1183 .enumerate()
1184 .skip(offset)
1185 .find_map(|(o, s)| {
1186 s.vars.get(&absolute_head).map(|_| (o, namespace.clone()))
1187 })
1188 .unwrap_or((self.current_scope(), Path(vec![])));
1189
1190 (off, ns, segments)
1191 }
1192 }
1193 };
1194
1195 for segment in segments.iter() {
1198 let mut local_forbidden = forbidden.clone();
1199 let idx = self.current_scope() - scope_offset;
1200 let scope = &self.symbols[idx];
1201 working_path = working_path.push_segment(segment.clone());
1202
1203 loop {
1204 let Some(id) = scope.vars.get(&working_path) else {
1207 let segment_path = working_path.at_loc(&segment.loc());
1208 return Err(LookupError::NoSuchSymbol(segment_path));
1209 };
1210
1211 if forbidden.contains(id) {
1213 let segment_path = working_path.at_loc(&segment.loc());
1214 return Err(LookupError::NoSuchSymbol(segment_path));
1215 }
1216
1217 if check_visibility {
1218 if let Some(visibility) = self.visibilities.get(id) {
1221 let vis_path = match visibility.inner {
1222 Visibility::Public => Path(vec![]),
1223 Visibility::Implicit => working_path.prelude(),
1224 Visibility::AtLib => self.base_namespace.clone(),
1225 Visibility::AtSelf => working_path.prelude(),
1226 Visibility::AtSuper => working_path.prelude().prelude(),
1227 Visibility::AtSuperSuper => working_path.prelude().prelude().prelude(),
1228 };
1229
1230 let is_visible =
1231 vis_path
1232 .0
1233 .iter()
1234 .zip_longest(&namespace.0)
1235 .all(|entry| match entry {
1236 EitherOrBoth::Both(l, r) => l == r,
1237 EitherOrBoth::Left(_) => false,
1238 EitherOrBoth::Right(_) => true,
1239 });
1240
1241 if !is_visible {
1242 let target_id =
1243 self.lookup_id_in_namespace(path, namespace, false).ok();
1244
1245 let target_item = target_id.as_ref().and_then(|id| {
1246 self.thing_by_id(id)
1247 .map(Thing::loc)
1248 .or_else(|| self.try_type_symbol_by_id(id).map(|ty| ty.loc()))
1249 });
1250
1251 let invisible_item = if target_id != Some(id.clone()) {
1252 self.things.get(id)
1253 } else {
1254 None
1255 };
1256
1257 return Err(LookupError::NotVisible {
1258 target_segment: path.tail(),
1259 invisible_segment: segment.clone(),
1260 target_item,
1261 invisible_item: invisible_item.map(Thing::loc),
1262 visibility_marker: if visibility.inner != Visibility::Implicit {
1263 Some(visibility.loc())
1264 } else {
1265 None
1266 },
1267 suggest_modifications: target_id
1273 .map(|id| id.1.starts_with(&self.base_namespace))
1274 .unwrap_or(false),
1275 });
1276 }
1277 }
1278 }
1279
1280 if self.types.contains_key(id) {
1281 break;
1282 }
1283
1284 match self.things.get(id) {
1285 Some(Thing::Alias {
1286 loc: _,
1287 path,
1288 in_namespace,
1289 }) => {
1290 local_forbidden.insert(id.clone());
1291
1292 (scope_offset, working_path) = self.canonicalize_in_namespace(
1294 path,
1295 in_namespace,
1296 &local_forbidden,
1297 offset,
1298 check_visibility,
1299 )?;
1300 }
1301 _ => break,
1302 }
1303 }
1304 }
1305
1306 Ok((scope_offset, working_path))
1309 }
1310
1311 pub fn print_symbols(&self) {
1312 println!("=============================================================");
1313 println!(" Symtab dump");
1314 println!("=============================================================");
1315 println!("Current namespace is `{}`", self.namespace);
1316 println!("Current base_namespace is `{}`", self.base_namespace);
1317 for (level, scope) in self.symbols.iter().enumerate() {
1318 let indent = (1..level + 1).map(|_| "\t").collect::<Vec<_>>().join("");
1319 println!(">>> new_scope",);
1320
1321 for (path, name) in &scope.vars {
1322 println!(
1323 "{indent}{path} => {name}",
1324 path = format!("{path}").cyan(),
1325 name = format!("{name:?}").yellow()
1326 );
1327 }
1328 }
1329
1330 println!("Things:");
1331 for (name, thing) in &self.things {
1332 print!("{}: ", format!("{name:?}").purple());
1333 match thing {
1334 Thing::Struct(_) => println!("struct"),
1335 Thing::EnumVariant(_) => println!("enum variant"),
1336 Thing::Unit(_) => println!("unit"),
1337 Thing::Variable(_) => println!("variable"),
1338 Thing::ArrayLabel(_) => println!("array label"),
1339 Thing::Alias {
1340 loc: _,
1341 path,
1342 in_namespace,
1343 } => {
1344 println!("{}", format!("alias => {path} in {in_namespace}").green())
1345 }
1346 Thing::Trait(t) => println!("trait {}", t.name),
1347 Thing::Module(_, name) => println!("mod {name}"),
1348 Thing::Dummy => println!("dummy"),
1349 }
1350 }
1351
1352 println!("Types:");
1353 for name in self.types.keys() {
1354 print!("{}: ", format!("{name:?}").purple());
1355 println!("type");
1356 }
1357 }
1358}
1359
1360#[derive(Serialize, Deserialize)]
1366pub struct FrozenSymtab {
1367 inner: SymbolTable,
1368 pub id_tracker: NameIdTracker,
1369}
1370
1371impl FrozenSymtab {
1372 pub fn symtab(&self) -> &SymbolTable {
1373 &self.inner
1374 }
1375
1376 pub fn new_name(&self, description: Path) -> NameID {
1377 NameID(self.id_tracker.next(), description)
1378 }
1379
1380 pub fn unfreeze(self) -> SymbolTable {
1383 SymbolTable {
1387 id_tracker: self.id_tracker,
1388 ..self.inner
1389 }
1390 }
1391}