leo_passes/common/symbol_table/
mod.rs1use leo_ast::{Composite, Expression, Function, Location, NodeID, Type};
18use leo_errors::{AstError, 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 if let Some(parent_table) = symbol_table.all_locals.get_mut(&parent_id) {
83 parent_table.inner.borrow_mut().children.push(id);
84 }
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 dup(&self, new_id: NodeID) -> Self {
104 let mut inner = self.inner.borrow().clone();
105 inner.id = new_id;
106 LocalTable { inner: Rc::new(RefCell::new(inner)) }
107 }
108}
109
110impl SymbolTable {
111 pub fn reset_but_consts(&mut self) {
113 self.functions.clear();
114 self.records.clear();
115 self.structs.clear();
116 self.globals.clear();
117 self.all_locals.clear();
118 self.local = None;
119 }
120
121 pub fn global_scope(&self) -> bool {
123 self.local.is_none()
124 }
125
126 pub fn iter_structs(&self) -> impl Iterator<Item = (&Vec<Symbol>, &Composite)> {
128 self.structs.iter()
129 }
130
131 pub fn iter_records(&self) -> impl Iterator<Item = (&Location, &Composite)> {
133 self.records.iter()
134 }
135
136 pub fn iter_functions(&self) -> impl Iterator<Item = (&Location, &FunctionSymbol)> {
138 self.functions.iter()
139 }
140
141 pub fn lookup_struct(&self, path: &[Symbol]) -> Option<&Composite> {
143 self.structs.get(path)
144 }
145
146 pub fn lookup_record(&self, location: &Location) -> Option<&Composite> {
148 self.records.get(location)
149 }
150
151 pub fn lookup_function(&self, location: &Location) -> Option<&FunctionSymbol> {
153 self.functions.get(location)
154 }
155
156 pub fn lookup_path(&self, program: Symbol, path: &[Symbol]) -> Option<VariableSymbol> {
171 self.lookup_global(&Location::new(program, path.to_vec()))
172 .cloned()
173 .or_else(|| path.last().copied().and_then(|name| self.lookup_local(name)))
174 }
175
176 pub fn lookup_local(&self, name: Symbol) -> Option<VariableSymbol> {
178 let mut current = self.local.as_ref();
179
180 while let Some(table) = current {
181 let borrowed = table.inner.borrow();
182 let value = borrowed.variables.get(&name);
183 if value.is_some() {
184 return value.cloned();
185 }
186
187 current = borrowed.parent.and_then(|id| self.all_locals.get(&id));
188 }
189
190 None
191 }
192
193 pub fn enter_scope(&mut self, id: Option<NodeID>) {
197 self.local = id.map(|id| {
198 let parent = self.local.as_ref().map(|table| table.inner.borrow().id);
199 let new_local_table = if let Some(existing) = self.all_locals.get(&id) {
200 existing.clone()
201 } else {
202 let new_table = LocalTable::new(self, id, parent);
203 self.all_locals.insert(id, new_table.clone());
204 new_table
205 };
206
207 assert_eq!(parent, new_local_table.inner.borrow().parent, "Entered scopes out of order.");
208 new_local_table.clone()
209 });
210 }
211
212 pub fn enter_scope_duped(&mut self, new_id: NodeID, old_id: NodeID) {
216 let old_local_table = self.all_locals.get(&old_id).expect("Must have an old scope to dup from.");
217 let new_local_table = old_local_table.dup(new_id);
218 let parent = self.local.as_ref().map(|table| table.inner.borrow().id);
219 new_local_table.inner.borrow_mut().parent = parent;
220 self.all_locals.insert(new_id, new_local_table.clone());
221 self.local = Some(new_local_table);
222 }
223
224 pub fn enter_parent(&mut self) {
226 let parent: Option<NodeID> = self.local.as_ref().and_then(|table| table.inner.borrow().parent);
227 self.local = parent.map(|id| self.all_locals.get(&id).expect("Parent should exist.")).cloned();
228 }
229
230 pub fn is_local_to(&self, scope: NodeID, symbol: Symbol) -> bool {
232 self.all_locals.get(&scope).map(|locals| locals.inner.borrow().variables.contains_key(&symbol)).unwrap_or(false)
233 }
234
235 pub fn is_defined_in_scope_or_ancestor_until(&self, scope: NodeID, symbol: Symbol) -> bool {
240 let mut current = self.local.as_ref();
241
242 while let Some(table) = current {
243 let inner = table.inner.borrow();
244
245 if inner.variables.contains_key(&symbol) {
247 return true;
248 }
249
250 if inner.id == scope {
252 break;
253 }
254
255 current = inner.parent.and_then(|parent_id| self.all_locals.get(&parent_id));
257 }
258
259 false
260 }
261
262 pub fn is_local_to_or_in_child_scope(&self, scope: NodeID, symbol: Symbol) -> bool {
264 let mut stack = vec![scope];
265
266 while let Some(current_id) = stack.pop() {
267 if let Some(table) = self.all_locals.get(¤t_id) {
268 let inner = table.inner.borrow();
269
270 if inner.variables.contains_key(&symbol) {
271 return true;
272 }
273
274 stack.extend(&inner.children);
275 }
276 }
277
278 false
279 }
280
281 pub fn insert_const(&mut self, program: Symbol, path: &[Symbol], value: Expression) {
283 if let Some(table) = self.local.as_mut() {
284 let [const_name] = &path else { panic!("Local consts cannot have paths with more than 1 segment.") };
285 table.inner.borrow_mut().consts.insert(*const_name, value);
286 } else {
287 self.global_consts.insert(Location::new(program, path.to_vec()), value);
288 }
289 }
290
291 pub fn lookup_const(&self, program: Symbol, path: &[Symbol]) -> Option<Expression> {
293 let mut current = self.local.as_ref();
294
295 while let Some(table) = current {
296 let borrowed = table.inner.borrow();
297 let value = borrowed.consts.get(path.last().expect("all paths must have at least 1 segment"));
298 if value.is_some() {
299 return value.cloned();
300 }
301
302 current = borrowed.parent.and_then(|id| self.all_locals.get(&id));
303 }
304
305 self.global_consts.get(&Location::new(program, path.to_vec())).cloned()
306 }
307
308 pub fn insert_struct(&mut self, program: Symbol, path: &[Symbol], composite: Composite) -> Result<()> {
312 if let Some(old_composite) = self.structs.get(path) {
313 if eq_struct(&composite, old_composite) {
314 Ok(())
315 } else {
316 Err(AstError::redefining_external_struct(path.iter().format("::"), composite.span).into())
317 }
318 } else {
319 let location = Location::new(program, path.to_vec());
320 self.check_shadow_global(&location, composite.span)?;
321 self.structs.insert(path.to_vec(), composite);
322 Ok(())
323 }
324 }
325
326 pub fn insert_record(&mut self, location: Location, composite: Composite) -> Result<()> {
328 self.check_shadow_global(&location, composite.span)?;
329 self.records.insert(location, composite);
330 Ok(())
331 }
332
333 pub fn insert_function(&mut self, location: Location, function: Function) -> Result<()> {
335 self.check_shadow_global(&location, function.span)?;
336 self.functions.insert(location, FunctionSymbol { function, finalizer: None });
337 Ok(())
338 }
339
340 pub fn insert_global(&mut self, location: Location, var: VariableSymbol) -> Result<()> {
342 self.check_shadow_global(&location, var.span)?;
343 self.globals.insert(location, var);
344 Ok(())
345 }
346
347 pub fn lookup_global(&self, location: &Location) -> Option<&VariableSymbol> {
349 self.globals.get(location)
350 }
351
352 fn check_shadow_global(&self, location: &Location, span: Span) -> Result<()> {
353 let display_name = location.path.iter().format("::");
354 if self.functions.contains_key(location) {
355 Err(AstError::shadowed_function(display_name, span).into())
356 } else if self.records.contains_key(location) {
357 Err(AstError::shadowed_record(display_name, span).into())
358 } else if self.structs.contains_key(&location.path) {
359 Err(AstError::shadowed_struct(display_name, span).into())
360 } else if self.globals.contains_key(location) {
361 Err(AstError::shadowed_variable(display_name, span).into())
362 } else {
363 Ok(())
364 }
365 }
366
367 fn check_shadow_variable(&self, program: Symbol, path: &[Symbol], span: Span) -> Result<()> {
368 let mut current = self.local.as_ref();
369
370 while let Some(table) = current {
371 if let [name] = &path {
372 if table.inner.borrow().variables.contains_key(name) {
373 return Err(AstError::shadowed_variable(name, span).into());
374 }
375 }
376 current = table.inner.borrow().parent.map(|id| self.all_locals.get(&id).expect("Parent should exist."));
377 }
378
379 self.check_shadow_global(&Location::new(program, path.to_vec()), span)?;
380
381 Ok(())
382 }
383
384 pub fn insert_variable(&mut self, program: Symbol, path: &[Symbol], var: VariableSymbol) -> Result<()> {
386 self.check_shadow_variable(program, path, var.span)?;
387
388 if let Some(table) = self.local.as_mut() {
389 let [name] = &path else { panic!("Local variables cannot have paths with more than 1 segment.") };
390 table.inner.borrow_mut().variables.insert(*name, var);
391 } else {
392 self.globals.insert(Location::new(program, path.to_vec()), var);
393 }
394
395 Ok(())
396 }
397
398 pub fn attach_finalizer(
400 &mut self,
401 caller: Location,
402 callee: Location,
403 future_inputs: Vec<Location>,
404 inferred_inputs: Vec<Type>,
405 ) -> Result<()> {
406 let callee_location = Location::new(callee.program, callee.path);
407
408 if let Some(func) = self.functions.get_mut(&caller) {
409 func.finalizer = Some(Finalizer { location: callee_location, future_inputs, inferred_inputs });
410 Ok(())
411 } else {
412 Err(AstError::function_not_found(caller.path.iter().format("::")).into())
413 }
414 }
415}
416
417fn eq_struct(new: &Composite, old: &Composite) -> bool {
418 if new.members.len() != old.members.len() {
419 return false;
420 }
421
422 new.members
423 .iter()
424 .zip(old.members.iter())
425 .all(|(member1, member2)| member1.name() == member2.name() && member1.type_.eq_flat_relaxed(&member2.type_))
426}