1use leo_ast::{Composite, Expression, Function, Interface, Location, NodeBuilder, NodeID, Path, Type};
18use leo_errors::{AstError, Color, Label, LeoError, Result};
19use leo_span::{Span, Symbol};
20
21use indexmap::{IndexMap, IndexSet};
22use itertools::Itertools;
23use std::{cell::RefCell, collections::HashMap, rc::Rc};
24
25mod symbols;
26pub use symbols::*;
27
28#[derive(Debug, Default)]
32pub struct SymbolTable {
33 imports: IndexMap<Symbol, IndexSet<Symbol>>,
35
36 libraries: IndexSet<Symbol>,
38
39 functions: IndexMap<Location, FunctionSymbol>,
41
42 records: IndexMap<Location, Composite>,
44
45 structs: IndexMap<Location, Composite>,
47
48 interfaces: IndexMap<Location, Interface>,
50
51 global_consts: IndexMap<Location, Expression>,
53
54 globals: IndexMap<Location, VariableSymbol>,
56
57 all_locals: HashMap<NodeID, LocalTable>,
59
60 local: Option<LocalTable>,
62}
63
64#[derive(Clone, Default, Debug)]
65struct LocalTable {
66 inner: Rc<RefCell<LocalTableInner>>,
67}
68
69#[derive(Clone, Default, Debug)]
70struct LocalTableInner {
71 id: NodeID,
73
74 parent: Option<NodeID>,
76
77 children: Vec<NodeID>,
79
80 consts: HashMap<Symbol, Expression>,
82
83 variables: HashMap<Symbol, VariableSymbol>,
85}
86
87impl LocalTable {
88 fn new(symbol_table: &mut SymbolTable, id: NodeID, parent: Option<NodeID>) -> Self {
89 if let Some(parent_id) = parent
91 && let Some(parent_table) = symbol_table.all_locals.get_mut(&parent_id)
92 {
93 parent_table.inner.borrow_mut().children.push(id);
94 }
95
96 LocalTable {
97 inner: Rc::new(RefCell::new(LocalTableInner {
98 id,
99 parent,
100 consts: HashMap::new(),
101 variables: HashMap::new(),
102 children: vec![], })),
104 }
105 }
106
107 fn clear_but_consts(&mut self) {
108 self.inner.borrow_mut().variables.clear();
109 }
110
111 pub fn dup(
114 &self,
115 node_builder: &NodeBuilder,
116 all_locals: &mut HashMap<NodeID, LocalTable>,
117 new_parent: Option<NodeID>,
118 ) -> LocalTable {
119 let inner = self.inner.borrow();
120
121 let new_id = node_builder.next_id();
123
124 let new_children: Vec<NodeID> = inner
126 .children
127 .iter()
128 .map(|child_id| {
129 let child_table = all_locals.get(child_id).expect("Child must exist").clone();
130 let duped_child = child_table.dup(node_builder, all_locals, Some(new_id));
131 let child_new_id = duped_child.inner.borrow().id;
132 all_locals.insert(child_new_id, duped_child);
133 child_new_id
134 })
135 .collect();
136
137 let new_table = LocalTable {
139 inner: Rc::new(RefCell::new(LocalTableInner {
140 id: new_id,
141 parent: new_parent,
142 children: new_children,
143 consts: inner.consts.clone(),
144 variables: inner.variables.clone(),
145 })),
146 };
147
148 all_locals.insert(new_id, new_table.clone());
150
151 new_table
152 }
153}
154
155impl SymbolTable {
156 pub fn add_import(&mut self, importer: Symbol, imported: Symbol) {
158 self.imports.entry(importer).or_default().insert(imported);
159 }
160
161 pub fn add_imported_by(&mut self, imported: Symbol, importers: &IndexSet<Symbol>) {
163 for importer in importers {
164 self.add_import(*importer, imported);
165 }
166 }
167
168 pub fn get_imports(&self, program: &Symbol) -> IndexSet<Symbol> {
170 self.imports.get(program).cloned().unwrap_or_default()
171 }
172
173 pub fn get_imports_mut(&mut self, program: &Symbol) -> Option<&mut IndexSet<Symbol>> {
175 self.imports.get_mut(program)
176 }
177
178 pub fn iter_imports(&self) -> impl Iterator<Item = (&Symbol, &IndexSet<Symbol>)> {
180 self.imports.iter()
181 }
182
183 pub fn add_as_library(&mut self, library: Symbol) {
185 self.libraries.insert(library);
186 }
187
188 pub fn is_library(&self, name: Symbol) -> bool {
190 self.libraries.contains(&name)
191 }
192
193 fn is_visible(&self, current: Symbol, target: &Symbol) -> bool {
195 current == *target || self.imports.get(¤t).map(|imports| imports.contains(target)).unwrap_or(false)
196 }
197
198 pub fn get_transitive_imports(&self, program: &Symbol) -> IndexSet<Symbol> {
201 let mut ordered = IndexSet::new();
202 let mut seen = IndexSet::new();
203
204 if let Some(direct_imports) = self.imports.get(program) {
205 for imported in direct_imports {
206 self.collect_imports_postorder(imported, &mut ordered, &mut seen);
207 }
208 }
209
210 ordered.retain(|sym| !self.libraries.contains(sym));
211 ordered
212 }
213
214 fn collect_imports_postorder(&self, program: &Symbol, ordered: &mut IndexSet<Symbol>, seen: &mut IndexSet<Symbol>) {
217 if !seen.insert(*program) {
219 return;
220 }
221
222 if let Some(direct_imports) = self.imports.get(program) {
223 for imported in direct_imports {
224 self.collect_imports_postorder(imported, ordered, seen);
225 }
226 }
227
228 ordered.insert(*program);
229 }
230
231 pub fn reset_but_consts(&mut self) {
233 self.functions.clear();
234 self.records.clear();
235 self.structs.clear();
236 self.globals.clear();
237
238 for local_table in self.all_locals.values_mut() {
240 local_table.clear_but_consts();
241 }
242
243 self.local = None;
244 }
245
246 pub fn global_scope(&self) -> bool {
248 self.local.is_none()
249 }
250
251 pub fn iter_structs(&self) -> impl Iterator<Item = (&Location, &Composite)> {
253 self.structs.iter()
254 }
255
256 pub fn iter_records(&self) -> impl Iterator<Item = (&Location, &Composite)> {
258 self.records.iter()
259 }
260
261 pub fn iter_functions(&self) -> impl Iterator<Item = (&Location, &FunctionSymbol)> {
263 self.functions.iter()
264 }
265
266 pub fn iter_interfaces(&self) -> impl Iterator<Item = (&Location, &Interface)> {
268 self.interfaces.iter()
269 }
270
271 pub fn lookup_struct(&self, current_program: Symbol, loc: &Location) -> Option<&Composite> {
273 if self.is_visible(current_program, &loc.program) { self.structs.get(loc) } else { None }
274 }
275
276 pub fn lookup_record(&self, current_program: Symbol, location: &Location) -> Option<&Composite> {
278 if self.is_visible(current_program, &location.program) { self.records.get(location) } else { None }
279 }
280
281 pub fn lookup_function(&self, current_program: Symbol, location: &Location) -> Option<&FunctionSymbol> {
283 if self.is_visible(current_program, &location.program) { self.functions.get(location) } else { None }
284 }
285
286 pub fn lookup_interface(&self, current_program: Symbol, location: &Location) -> Option<&Interface> {
288 if self.is_visible(current_program, &location.program) { self.interfaces.get(location) } else { None }
289 }
290
291 pub fn lookup_path(&self, current_program: Symbol, path: &Path) -> Option<VariableSymbol> {
306 if let Some(loc) = path.try_global_location() {
307 self.lookup_global(current_program, loc).cloned()
308 } else if let Some(sym) = path.try_local_symbol() {
309 self.lookup_local(sym)
310 } else {
311 None
312 }
313 }
314
315 pub fn lookup_local(&self, name: Symbol) -> Option<VariableSymbol> {
317 let mut current = self.local.as_ref();
318
319 while let Some(table) = current {
320 let borrowed = table.inner.borrow();
321 let value = borrowed.variables.get(&name);
322 if value.is_some() {
323 return value.cloned();
324 }
325
326 current = borrowed.parent.and_then(|id| self.all_locals.get(&id));
327 }
328
329 None
330 }
331
332 pub fn enter_scope(&mut self, id: Option<NodeID>) {
336 self.local = id.map(|id| {
337 let parent = self.local.as_ref().map(|table| table.inner.borrow().id);
338 let new_local_table = if let Some(existing) = self.all_locals.get(&id) {
339 existing.clone()
340 } else {
341 let new_table = LocalTable::new(self, id, parent);
342 self.all_locals.insert(id, new_table.clone());
343 new_table
344 };
345
346 assert_eq!(parent, new_local_table.inner.borrow().parent, "Entered scopes out of order.");
347 new_local_table.clone()
348 });
349 }
350
351 pub fn enter_existing_scope(&mut self, id: Option<NodeID>) {
352 self.local = id.map(|id| {
353 let parent = self.local.as_ref().map(|table| table.inner.borrow().id);
354 let new_local_table = if let Some(existing) = self.all_locals.get(&id) {
355 existing.clone()
356 } else {
357 panic!("local scope must exist");
358 };
359
360 assert_eq!(parent, new_local_table.inner.borrow().parent, "Entered scopes out of order.");
361 new_local_table.clone()
362 });
363 }
364
365 pub fn enter_scope_duped(&mut self, old_id: NodeID, node_builder: &NodeBuilder) -> usize {
371 let old_local_table = self.all_locals.get(&old_id).expect("Must have an old scope to dup from.").clone();
372
373 let new_local_table =
375 old_local_table.dup(node_builder, &mut self.all_locals, self.local.as_ref().map(|t| t.inner.borrow().id));
376
377 let new_id = new_local_table.inner.borrow().id;
378
379 self.local = Some(new_local_table);
381
382 new_id
383 }
384
385 pub fn enter_parent(&mut self) {
387 let parent: Option<NodeID> = self.local.as_ref().and_then(|table| table.inner.borrow().parent);
388 self.local = parent.map(|id| self.all_locals.get(&id).expect("Parent should exist.")).cloned();
389 }
390
391 pub fn is_local_to(&self, scope: NodeID, symbol: Symbol) -> bool {
393 self.all_locals.get(&scope).map(|locals| locals.inner.borrow().variables.contains_key(&symbol)).unwrap_or(false)
394 }
395
396 pub fn is_defined_in_scope_or_ancestor_until(&self, scope: NodeID, symbol: Symbol) -> bool {
401 let mut current = self.local.as_ref();
402
403 while let Some(table) = current {
404 let inner = table.inner.borrow();
405
406 if inner.variables.contains_key(&symbol) {
408 return true;
409 }
410
411 if inner.id == scope {
413 break;
414 }
415
416 current = inner.parent.and_then(|parent_id| self.all_locals.get(&parent_id));
418 }
419
420 false
421 }
422
423 pub fn is_local_to_or_in_child_scope(&self, scope: NodeID, symbol: Symbol) -> bool {
425 let mut stack = vec![scope];
426
427 while let Some(current_id) = stack.pop() {
428 if let Some(table) = self.all_locals.get(¤t_id) {
429 let inner = table.inner.borrow();
430
431 if inner.variables.contains_key(&symbol) {
432 return true;
433 }
434
435 stack.extend(&inner.children);
436 }
437 }
438
439 false
440 }
441
442 pub fn insert_local_const(&mut self, name: Symbol, value: Expression) {
445 if let Some(table) = self.local.as_mut() {
446 table.inner.borrow_mut().consts.insert(name, value);
447 }
448 }
449
450 pub fn insert_global_const(&mut self, location: Location, value: Expression) {
453 if self.global_scope() {
454 self.global_consts.insert(location, value);
455 }
456 }
457
458 pub fn lookup_local_const(&self, name: Symbol) -> Option<Expression> {
460 let mut current = self.local.as_ref();
461
462 while let Some(table) = current {
463 let borrowed = table.inner.borrow();
464 let value = borrowed.consts.get(&name);
465 if value.is_some() {
466 return value.cloned();
467 }
468
469 current = borrowed.parent.and_then(|id| self.all_locals.get(&id));
470 }
471
472 None
473 }
474
475 pub fn lookup_global_const(&self, current_program: Symbol, location: &Location) -> Option<Expression> {
476 if self.is_visible(current_program, &location.program) {
477 self.global_consts.get(location).cloned()
478 } else {
479 None
480 }
481 }
482
483 pub fn insert_struct(&mut self, location: Location, r#struct: Composite) -> Result<()> {
485 self.check_shadow_global(&location, r#struct.identifier.span)?;
486 self.structs.insert(location, r#struct);
487 Ok(())
488 }
489
490 pub fn insert_record(&mut self, location: Location, record: Composite) -> Result<()> {
492 self.check_shadow_global(&location, record.identifier.span)?;
493 self.records.insert(location, record);
494 Ok(())
495 }
496
497 pub fn insert_function(&mut self, location: Location, function: Function) -> Result<()> {
499 self.check_shadow_global(&location, function.identifier.span)?;
500 self.functions.insert(location, FunctionSymbol { function, finalizer: None });
501 Ok(())
502 }
503
504 pub fn insert_interface(&mut self, location: Location, int: Interface) -> Result<()> {
506 self.check_shadow_global(&location, int.identifier.span)?;
507 self.interfaces.insert(location, int);
508 Ok(())
509 }
510
511 pub fn insert_global(&mut self, location: Location, var: VariableSymbol) -> Result<()> {
513 self.check_shadow_global(&location, var.span)?;
514 self.globals.insert(location, var);
515 Ok(())
516 }
517
518 pub fn lookup_global(&self, current_program: Symbol, location: &Location) -> Option<&VariableSymbol> {
521 if self.is_visible(current_program, &location.program) { self.globals.get(location) } else { None }
522 }
523
524 pub fn set_local_type(&mut self, name: Symbol, ty: Type) -> bool {
526 let mut current = self.local.as_ref();
527
528 while let Some(table) = current {
529 let mut inner = table.inner.borrow_mut();
530
531 if let Some(sym) = inner.variables.get_mut(&name) {
532 sym.type_ = Some(ty);
533 return true;
534 }
535
536 current = inner.parent.and_then(|id| self.all_locals.get(&id));
537 }
538
539 false
540 }
541
542 pub fn set_global_type(&mut self, location: &Location, ty: Type) -> bool {
544 if let Some(sym) = self.globals.get_mut(location) {
545 sym.type_ = Some(ty);
546 return true;
547 }
548 false
549 }
550
551 pub fn emit_shadow_error(name: Symbol, span: Span, prev_span: Span) -> LeoError {
552 AstError::name_defined_multiple_times(name, span)
553 .with_labels(vec![
554 Label::new(format!("previous definition of `{name}` here"), prev_span).with_color(Color::Blue),
555 Label::new(format!("`{name}` redefined here"), span),
556 ])
557 .into()
558 }
559
560 fn check_shadow_global(&self, location: &Location, span: Span) -> Result<()> {
561 let name = location.path.last().expect("location path must have at least one segment.");
562
563 self.functions
564 .get(location)
565 .map(|f| f.function.identifier.span)
566 .or_else(|| self.records.get(location).map(|r| r.identifier.span))
567 .or_else(|| self.structs.get(location).map(|s| s.identifier.span))
568 .or_else(|| self.globals.get(location).map(|g| g.span))
569 .map_or_else(|| Ok(()), |prev_span| Err(Self::emit_shadow_error(*name, span, prev_span)))
570 }
571
572 fn check_shadow_variable(&self, program: Symbol, path: &[Symbol], span: Span) -> Result<()> {
573 let mut current = self.local.as_ref();
574
575 while let Some(table) = current {
576 if let [name] = &path
577 && let Some(var_symbol) = table.inner.borrow().variables.get(name)
578 {
579 return Err(Self::emit_shadow_error(*name, span, var_symbol.span));
580 }
581 current = table.inner.borrow().parent.map(|id| self.all_locals.get(&id).expect("Parent should exist."));
582 }
583
584 self.check_shadow_global(&Location::new(program, path.to_vec()), span)?;
585
586 Ok(())
587 }
588
589 pub fn insert_variable(&mut self, program: Symbol, path: &[Symbol], var: VariableSymbol) -> Result<()> {
591 self.check_shadow_variable(program, path, var.span)?;
592
593 if let Some(table) = self.local.as_mut() {
594 let [name] = &path else { panic!("Local variables cannot have paths with more than 1 segment.") };
595 table.inner.borrow_mut().variables.insert(*name, var);
596 } else {
597 self.globals.insert(Location::new(program, path.to_vec()), var);
598 }
599
600 Ok(())
601 }
602
603 pub fn attach_finalizer(
605 &mut self,
606 caller: Location,
607 callee: Location,
608 future_inputs: Vec<Location>,
609 inferred_inputs: Vec<Type>,
610 ) -> Result<()> {
611 let callee_location = Location::new(callee.program, callee.path);
612
613 if let Some(func) = self.functions.get_mut(&caller) {
614 func.finalizer = Some(Finalizer { location: callee_location, future_inputs, inferred_inputs });
615 Ok(())
616 } else {
617 Err(AstError::function_not_found(caller.path.iter().format("::")).into())
618 }
619 }
620}