leo_passes/common/symbol_table/
mod.rs1use leo_ast::{Composite, Expression, Function, Location, NodeBuilder, NodeID, Type};
18use leo_errors::{AstError, Color, Label, LeoError, Result};
19use leo_span::{Span, Symbol};
20
21use indexmap::IndexMap;
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 functions: IndexMap<Location, FunctionSymbol>,
35
36 records: IndexMap<Location, Composite>,
38
39 structs: IndexMap<Vec<Symbol>, Composite>,
41
42 global_consts: IndexMap<Location, Expression>,
44
45 globals: IndexMap<Location, VariableSymbol>,
47
48 all_locals: HashMap<NodeID, LocalTable>,
50
51 local: Option<LocalTable>,
53}
54
55#[derive(Clone, Default, Debug)]
56struct LocalTable {
57 inner: Rc<RefCell<LocalTableInner>>,
58}
59
60#[derive(Clone, Default, Debug)]
61struct LocalTableInner {
62 id: NodeID,
64
65 parent: Option<NodeID>,
67
68 children: Vec<NodeID>,
70
71 consts: HashMap<Symbol, Expression>,
73
74 variables: HashMap<Symbol, VariableSymbol>,
76}
77
78impl LocalTable {
79 fn new(symbol_table: &mut SymbolTable, id: NodeID, parent: Option<NodeID>) -> Self {
80 if let Some(parent_id) = parent
82 && let Some(parent_table) = symbol_table.all_locals.get_mut(&parent_id)
83 {
84 parent_table.inner.borrow_mut().children.push(id);
85 }
86
87 LocalTable {
88 inner: Rc::new(RefCell::new(LocalTableInner {
89 id,
90 parent,
91 consts: HashMap::new(),
92 variables: HashMap::new(),
93 children: vec![], })),
95 }
96 }
97
98 fn clear_but_consts(&mut self) {
99 self.inner.borrow_mut().variables.clear();
100 }
101
102 pub fn dup(
105 &self,
106 node_builder: &NodeBuilder,
107 all_locals: &mut HashMap<NodeID, LocalTable>,
108 new_parent: Option<NodeID>,
109 ) -> LocalTable {
110 let inner = self.inner.borrow();
111
112 let new_id = node_builder.next_id();
114
115 let new_children: Vec<NodeID> = inner
117 .children
118 .iter()
119 .map(|child_id| {
120 let child_table = all_locals.get(child_id).expect("Child must exist").clone();
121 let duped_child = child_table.dup(node_builder, all_locals, Some(new_id));
122 let child_new_id = duped_child.inner.borrow().id;
123 all_locals.insert(child_new_id, duped_child);
124 child_new_id
125 })
126 .collect();
127
128 let new_table = LocalTable {
130 inner: Rc::new(RefCell::new(LocalTableInner {
131 id: new_id,
132 parent: new_parent,
133 children: new_children,
134 consts: inner.consts.clone(),
135 variables: inner.variables.clone(),
136 })),
137 };
138
139 all_locals.insert(new_id, new_table.clone());
141
142 new_table
143 }
144}
145
146impl SymbolTable {
147 pub fn reset_but_consts(&mut self) {
149 self.functions.clear();
150 self.records.clear();
151 self.structs.clear();
152 self.globals.clear();
153
154 for local_table in self.all_locals.values_mut() {
156 local_table.clear_but_consts();
157 }
158
159 self.local = None;
160 }
161
162 pub fn global_scope(&self) -> bool {
164 self.local.is_none()
165 }
166
167 pub fn iter_structs(&self) -> impl Iterator<Item = (&Vec<Symbol>, &Composite)> {
169 self.structs.iter()
170 }
171
172 pub fn iter_records(&self) -> impl Iterator<Item = (&Location, &Composite)> {
174 self.records.iter()
175 }
176
177 pub fn iter_functions(&self) -> impl Iterator<Item = (&Location, &FunctionSymbol)> {
179 self.functions.iter()
180 }
181
182 pub fn lookup_struct(&self, path: &[Symbol]) -> Option<&Composite> {
184 self.structs.get(path)
185 }
186
187 pub fn lookup_record(&self, location: &Location) -> Option<&Composite> {
189 self.records.get(location)
190 }
191
192 pub fn lookup_function(&self, location: &Location) -> Option<&FunctionSymbol> {
194 self.functions.get(location)
195 }
196
197 pub fn lookup_path(&self, program: Symbol, path: &[Symbol]) -> Option<VariableSymbol> {
212 self.lookup_global(&Location::new(program, path.to_vec()))
213 .cloned()
214 .or_else(|| path.last().copied().and_then(|name| self.lookup_local(name)))
215 }
216
217 pub fn lookup_local(&self, name: Symbol) -> Option<VariableSymbol> {
219 let mut current = self.local.as_ref();
220
221 while let Some(table) = current {
222 let borrowed = table.inner.borrow();
223 let value = borrowed.variables.get(&name);
224 if value.is_some() {
225 return value.cloned();
226 }
227
228 current = borrowed.parent.and_then(|id| self.all_locals.get(&id));
229 }
230
231 None
232 }
233
234 pub fn enter_scope(&mut self, id: Option<NodeID>) {
238 self.local = id.map(|id| {
239 let parent = self.local.as_ref().map(|table| table.inner.borrow().id);
240 let new_local_table = if let Some(existing) = self.all_locals.get(&id) {
241 existing.clone()
242 } else {
243 let new_table = LocalTable::new(self, id, parent);
244 self.all_locals.insert(id, new_table.clone());
245 new_table
246 };
247
248 assert_eq!(parent, new_local_table.inner.borrow().parent, "Entered scopes out of order.");
249 new_local_table.clone()
250 });
251 }
252
253 pub fn enter_scope_duped(&mut self, old_id: NodeID, node_builder: &NodeBuilder) -> usize {
259 let old_local_table = self.all_locals.get(&old_id).expect("Must have an old scope to dup from.").clone();
260
261 let new_local_table =
263 old_local_table.dup(node_builder, &mut self.all_locals, self.local.as_ref().map(|t| t.inner.borrow().id));
264
265 let new_id = new_local_table.inner.borrow().id;
266
267 self.local = Some(new_local_table);
269
270 new_id
271 }
272
273 pub fn enter_parent(&mut self) {
275 let parent: Option<NodeID> = self.local.as_ref().and_then(|table| table.inner.borrow().parent);
276 self.local = parent.map(|id| self.all_locals.get(&id).expect("Parent should exist.")).cloned();
277 }
278
279 pub fn is_local_to(&self, scope: NodeID, symbol: Symbol) -> bool {
281 self.all_locals.get(&scope).map(|locals| locals.inner.borrow().variables.contains_key(&symbol)).unwrap_or(false)
282 }
283
284 pub fn is_defined_in_scope_or_ancestor_until(&self, scope: NodeID, symbol: Symbol) -> bool {
289 let mut current = self.local.as_ref();
290
291 while let Some(table) = current {
292 let inner = table.inner.borrow();
293
294 if inner.variables.contains_key(&symbol) {
296 return true;
297 }
298
299 if inner.id == scope {
301 break;
302 }
303
304 current = inner.parent.and_then(|parent_id| self.all_locals.get(&parent_id));
306 }
307
308 false
309 }
310
311 pub fn is_local_to_or_in_child_scope(&self, scope: NodeID, symbol: Symbol) -> bool {
313 let mut stack = vec![scope];
314
315 while let Some(current_id) = stack.pop() {
316 if let Some(table) = self.all_locals.get(¤t_id) {
317 let inner = table.inner.borrow();
318
319 if inner.variables.contains_key(&symbol) {
320 return true;
321 }
322
323 stack.extend(&inner.children);
324 }
325 }
326
327 false
328 }
329
330 pub fn insert_const(&mut self, program: Symbol, path: &[Symbol], value: Expression) {
332 if let Some(table) = self.local.as_mut() {
333 let [const_name] = &path else { panic!("Local consts cannot have paths with more than 1 segment.") };
334 table.inner.borrow_mut().consts.insert(*const_name, value);
335 } else {
336 self.global_consts.insert(Location::new(program, path.to_vec()), value);
337 }
338 }
339
340 pub fn lookup_const(&self, program: Symbol, path: &[Symbol]) -> Option<Expression> {
342 let mut current = self.local.as_ref();
343
344 while let Some(table) = current {
345 let borrowed = table.inner.borrow();
346 let value = borrowed.consts.get(path.last().expect("all paths must have at least 1 segment"));
347 if value.is_some() {
348 return value.cloned();
349 }
350
351 current = borrowed.parent.and_then(|id| self.all_locals.get(&id));
352 }
353
354 self.global_consts.get(&Location::new(program, path.to_vec())).cloned()
355 }
356
357 pub fn insert_struct(&mut self, program: Symbol, path: &[Symbol], composite: Composite) -> Result<()> {
361 if let Some(old_composite) = self.structs.get(path) {
362 if eq_struct(&composite, old_composite) {
363 Ok(())
364 } else {
365 Err(AstError::redefining_external_struct(path.iter().format("::"), old_composite.span).into())
366 }
367 } else {
368 let location = Location::new(program, path.to_vec());
369 self.check_shadow_global(&location, composite.identifier.span)?;
370 self.structs.insert(path.to_vec(), composite);
371 Ok(())
372 }
373 }
374
375 pub fn insert_record(&mut self, location: Location, composite: Composite) -> Result<()> {
377 self.check_shadow_global(&location, composite.identifier.span)?;
378 self.records.insert(location, composite);
379 Ok(())
380 }
381
382 pub fn insert_function(&mut self, location: Location, function: Function) -> Result<()> {
384 self.check_shadow_global(&location, function.identifier.span)?;
385 self.functions.insert(location, FunctionSymbol { function, finalizer: None });
386 Ok(())
387 }
388
389 pub fn insert_global(&mut self, location: Location, var: VariableSymbol) -> Result<()> {
391 self.check_shadow_global(&location, var.span)?;
392 self.globals.insert(location, var);
393 Ok(())
394 }
395
396 pub fn lookup_global(&self, location: &Location) -> Option<&VariableSymbol> {
398 self.globals.get(location)
399 }
400
401 pub fn emit_shadow_error(name: Symbol, span: Span, prev_span: Span) -> LeoError {
402 AstError::name_defined_multiple_times(name, span)
403 .with_labels(vec![
404 Label::new(format!("previous definition of `{name}` here"), prev_span).with_color(Color::Blue),
405 Label::new(format!("`{name}` redefined here"), span),
406 ])
407 .into()
408 }
409
410 fn check_shadow_global(&self, location: &Location, span: Span) -> Result<()> {
411 let name = location.path.last().expect("location path must have at least one segment.");
412
413 self.functions
414 .get(location)
415 .map(|f| f.function.identifier.span)
416 .or_else(|| self.records.get(location).map(|r| r.identifier.span))
417 .or_else(|| self.structs.get(&location.path).map(|s| s.identifier.span))
418 .or_else(|| self.globals.get(location).map(|g| g.span))
419 .map_or_else(|| Ok(()), |prev_span| Err(Self::emit_shadow_error(*name, span, prev_span)))
420 }
421
422 fn check_shadow_variable(&self, program: Symbol, path: &[Symbol], span: Span) -> Result<()> {
423 let mut current = self.local.as_ref();
424
425 while let Some(table) = current {
426 if let [name] = &path
427 && let Some(var_symbol) = table.inner.borrow().variables.get(name)
428 {
429 return Err(Self::emit_shadow_error(*name, span, var_symbol.span));
430 }
431 current = table.inner.borrow().parent.map(|id| self.all_locals.get(&id).expect("Parent should exist."));
432 }
433
434 self.check_shadow_global(&Location::new(program, path.to_vec()), span)?;
435
436 Ok(())
437 }
438
439 pub fn insert_variable(&mut self, program: Symbol, path: &[Symbol], var: VariableSymbol) -> Result<()> {
441 self.check_shadow_variable(program, path, var.span)?;
442
443 if let Some(table) = self.local.as_mut() {
444 let [name] = &path else { panic!("Local variables cannot have paths with more than 1 segment.") };
445 table.inner.borrow_mut().variables.insert(*name, var);
446 } else {
447 self.globals.insert(Location::new(program, path.to_vec()), var);
448 }
449
450 Ok(())
451 }
452
453 pub fn attach_finalizer(
455 &mut self,
456 caller: Location,
457 callee: Location,
458 future_inputs: Vec<Location>,
459 inferred_inputs: Vec<Type>,
460 ) -> Result<()> {
461 let callee_location = Location::new(callee.program, callee.path);
462
463 if let Some(func) = self.functions.get_mut(&caller) {
464 func.finalizer = Some(Finalizer { location: callee_location, future_inputs, inferred_inputs });
465 Ok(())
466 } else {
467 Err(AstError::function_not_found(caller.path.iter().format("::")).into())
468 }
469 }
470}
471
472fn eq_struct(new: &Composite, old: &Composite) -> bool {
473 if new.members.len() != old.members.len() {
474 return false;
475 }
476
477 new.members
478 .iter()
479 .zip(old.members.iter())
480 .all(|(member1, member2)| member1.name() == member2.name() && member1.type_.eq_flat_relaxed(&member2.type_))
481}