diff --git a/compiler/passes/src/symbol_table/create.rs b/compiler/passes/src/symbol_table/create.rs
index aaa85c5e26..059878badd 100644
--- a/compiler/passes/src/symbol_table/create.rs
+++ b/compiler/passes/src/symbol_table/create.rs
@@ -20,20 +20,17 @@ use leo_errors::emitter::Handler;
use crate::SymbolTable;
pub struct CreateSymbolTable<'a> {
- symbol_table: SymbolTable<'a>,
+ pub(crate) symbol_table: SymbolTable,
handler: &'a Handler,
}
impl<'a> CreateSymbolTable<'a> {
pub fn new(handler: &'a Handler) -> Self {
Self {
- symbol_table: SymbolTable::default(),
+ symbol_table: Default::default(),
handler,
}
}
- pub fn symbol_table(self) -> SymbolTable<'a> {
- self.symbol_table
- }
}
impl<'a> ExpressionVisitor<'a> for CreateSymbolTable<'a> {
diff --git a/compiler/passes/src/symbol_table/function_symbol.rs b/compiler/passes/src/symbol_table/function_symbol.rs
new file mode 100644
index 0000000000..7123adf53e
--- /dev/null
+++ b/compiler/passes/src/symbol_table/function_symbol.rs
@@ -0,0 +1,39 @@
+// Copyright (C) 2019-2022 Aleo Systems Inc.
+// This file is part of the Leo library.
+
+// The Leo library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// The Leo library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with the Leo library. If not, see .
+
+use leo_ast::{Function, FunctionInput, Type};
+use leo_span::Span;
+
+use crate::SymbolTable;
+
+#[derive(Clone, Debug)]
+pub struct FunctionSymbol {
+ pub(crate) id: usize,
+ pub(crate) type_: Type,
+ pub(crate) span: Span,
+ pub(crate) input: Vec,
+}
+
+impl SymbolTable {
+ pub(crate) fn new_function_symbol(id: usize, func: &Function) -> FunctionSymbol {
+ FunctionSymbol {
+ id,
+ type_: func.output,
+ span: func.span,
+ input: func.input.clone(),
+ }
+ }
+}
diff --git a/compiler/passes/src/symbol_table/mod.rs b/compiler/passes/src/symbol_table/mod.rs
index 67f5727b74..7e3f793e38 100644
--- a/compiler/passes/src/symbol_table/mod.rs
+++ b/compiler/passes/src/symbol_table/mod.rs
@@ -17,12 +17,12 @@
pub mod create;
pub use create::*;
+pub mod function_symbol;
+pub use function_symbol::*;
+
pub mod table;
pub use table::*;
-pub mod variable_scope;
-pub use variable_scope::*;
-
pub mod variable_symbol;
pub use variable_symbol::*;
@@ -33,13 +33,13 @@ use leo_errors::{emitter::Handler, Result};
impl<'a> Pass for CreateSymbolTable<'a> {
type Input = (&'a Ast, &'a Handler);
- type Output = Result>;
+ type Output = Result;
fn do_pass((ast, handler): Self::Input) -> Self::Output {
let mut visitor = CreateSymbolTable::new(handler);
visitor.visit_program(ast.as_repr());
handler.last_err()?;
- Ok(visitor.symbol_table())
+ Ok(visitor.symbol_table)
}
}
diff --git a/compiler/passes/src/symbol_table/table.rs b/compiler/passes/src/symbol_table/table.rs
index 52a1c9cd7a..954923f79f 100644
--- a/compiler/passes/src/symbol_table/table.rs
+++ b/compiler/passes/src/symbol_table/table.rs
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see .
-use std::fmt::Display;
+use std::cell::RefCell;
use leo_ast::{Circuit, Function};
use leo_errors::{AstError, Result};
@@ -22,100 +22,207 @@ use leo_span::{Span, Symbol};
use indexmap::IndexMap;
-use crate::{VariableScope, VariableSymbol};
+use crate::{Declaration, FunctionSymbol, Value, VariableSymbol};
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-pub struct SymbolTable<'a> {
- /// Maps function names to function definitions.
+#[derive(Clone, Debug, Default)]
+pub struct SymbolTable {
+ /// The parent scope if it exists.
+ /// For example if we are in a if block inside a function.
+ pub(crate) parent: Option>,
+ /// Functions represents the name of each function mapped to the Ast's function definition.
/// This field is populated at a first pass.
- functions: IndexMap,
+ pub functions: IndexMap,
/// Maps circuit names to circuit definitions.
/// This field is populated at a first pass.
- circuits: IndexMap,
- /// Variables represents functions variable definitions and input variables.
- /// This field is not populated till necessary.
- pub(crate) variables: VariableScope<'a>,
+ pub circuits: IndexMap,
+ /// The variables defined in a scope.
+ /// This field is populated as necessary.
+ pub(crate) variables: IndexMap,
+ /// The index of the current scope
+ pub(crate) scope_index: usize,
+ /// If the block will always be executed when the parent block is executed
+ pub(crate) is_locally_non_const: bool,
+ /// The subscopes of this scope
+ pub(crate) scopes: Vec>,
}
-impl<'a> SymbolTable<'a> {
+impl SymbolTable {
pub fn check_shadowing(&self, symbol: Symbol, span: Span) -> Result<()> {
- if self.functions.contains_key(&symbol) {
+ if self.variables.contains_key(&symbol) {
+ Err(AstError::shadowed_variable(symbol, span).into())
+ } else if self.functions.contains_key(&symbol) {
Err(AstError::shadowed_function(symbol, span).into())
+ } else if let Some(existing) = self.circuits.get(&symbol) {
+ match existing.is_record {
+ true => Err(AstError::shadowed_record(symbol, insert.span).into()),
+ false => Err(AstError::shadowed_circuit(symbol, span).into())
+ }
+ } else if let Some(parent) = self.parent.as_ref() {
+ parent.check_shadowing(symbol, span)
} else {
- self.variables.check_shadowing(symbol, span)?;
Ok(())
}
}
- pub fn clear_variables(&mut self) {
- self.variables.clear();
+ pub fn scope_index(&mut self) -> usize {
+ let index = self.scope_index;
+ self.scope_index += 1;
+ index
}
- pub fn insert_fn(&mut self, symbol: Symbol, insert: &'a Function) -> Result<()> {
+ pub fn insert_fn(&mut self, symbol: Symbol, insert: &Function) -> Result<()> {
self.check_shadowing(symbol, insert.span)?;
- self.functions.insert(symbol, insert);
+ let id = self.scope_index();
+ self.functions.insert(symbol, Self::new_function_symbol(id, insert));
+ self.scopes.push(Default::default());
Ok(())
}
- pub fn insert_circuit(&mut self, symbol: Symbol, insert: &'a Circuit) -> Result<()> {
- if let Some(existing) = self.circuits.get(&symbol) {
- // Error if the circuit or record already exists.
- let err = if existing.is_record {
- AstError::shadowed_record(symbol, insert.span).into()
+ pub fn insert_circuit(&mut self, symbol: Symbol, insert: &Circuit) -> Result<()> {
+ self.check_shadowing(symbol, insert.span)?;
+ self.circuits.insert(symbol, insert.clone());
+ Ok(())
+ }
+
+ pub fn insert_variable(&mut self, symbol: Symbol, insert: VariableSymbol) -> Result<()> {
+ self.check_shadowing(symbol, insert.span)?;
+ self.variables.insert(symbol, insert);
+ Ok(())
+ }
+
+ pub fn insert_block(&mut self, is_locally_non_const: bool) -> usize {
+ self.scopes.push(RefCell::new(SymbolTable {
+ is_locally_non_const,
+ ..Default::default()
+ }));
+ self.scope_index()
+ }
+
+ pub fn lookup_fn(&self, symbol: &Symbol) -> Option<&FunctionSymbol> {
+ if let Some(func) = self.functions.get(symbol) {
+ Some(func)
+ } else if let Some(parent) = self.parent.as_ref() {
+ parent.lookup_fn(symbol)
+ } else {
+ None
+ }
+ }
+
+ pub fn lookup_circuit(&self, symbol: &Symbol) -> Option<&Circuit> {
+ if let Some(circ) = self.circuits.get(symbol) {
+ Some(circ)
+ } else if let Some(parent) = self.parent.as_ref() {
+ parent.lookup_circuit(symbol)
+ } else {
+ None
+ }
+ }
+
+ pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol> {
+ if let Some(var) = self.variables.get(symbol) {
+ Some(var)
+ } else if let Some(parent) = self.parent.as_ref() {
+ parent.lookup_variable(symbol)
+ } else {
+ None
+ }
+ }
+
+ /// Returns true if the variable exists in the local scope
+ pub fn variable_in_local_scope(&self, symbol: &Symbol) -> bool {
+ self.variables.contains_key(symbol)
+ }
+
+ /// Returns true if the variable exists in any parent scope
+ pub fn variable_in_parent_scope(&self, symbol: &Symbol) -> bool {
+ if let Some(parent) = self.parent.as_ref() {
+ if parent.variables.contains_key(symbol) {
+ true
} else {
- AstError::shadowed_circuit(symbol, insert.span).into()
+ parent.variable_in_parent_scope(symbol)
+ }
+ } else {
+ false
+ }
+ }
+
+ pub fn lookup_variable_mut(&mut self, symbol: &Symbol) -> Option<&mut VariableSymbol> {
+ if let Some(var) = self.variables.get_mut(symbol) {
+ Some(var)
+ } else if let Some(parent) = self.parent.as_mut() {
+ parent.lookup_variable_mut(symbol)
+ } else {
+ None
+ }
+ }
+
+ /// Finds the variable in the parent scope, then SHADOWS it in most recent non-const scope with a mut const value.
+ /// returns a boolean for if the variable should be slated for deconstification
+ pub fn locally_constify_variable(&mut self, symbol: Symbol, value: Value) -> bool {
+ let mut var = self
+ .lookup_variable(&symbol)
+ .unwrap_or_else(|| panic!("attempting to constify non-existent variable `{symbol}`"))
+ .clone();
+ var.declaration = Declaration::Mut(Some(value));
+
+ let mut st = self;
+ loop {
+ let is_in_parent = st.variable_in_parent_scope(&symbol);
+ let is_in_local = st.variable_in_local_scope(&symbol);
+ if is_in_local && is_in_parent {
+ st.variables.insert(symbol, var.clone());
+ } else if st.is_locally_non_const && !is_in_parent {
+ st.variables.insert(symbol, var);
+ break false;
+ } else if st.is_locally_non_const && is_in_parent {
+ st.variables.insert(symbol, var);
+ break true;
+ }
+
+ if !st.is_locally_non_const && st.parent.is_some() && is_in_parent {
+ st = st.parent.as_mut().unwrap()
+ } else {
+ break true;
+ }
+ }
+ }
+
+ pub fn set_variable(&mut self, symbol: &Symbol, value: Value) -> bool {
+ if let Some(var) = self.variables.get_mut(symbol) {
+ var.declaration = match &var.declaration {
+ Declaration::Const(_) => Declaration::Const(Some(value)),
+ Declaration::Mut(_) => Declaration::Mut(Some(value)),
+ other => other.clone(),
};
- return Err(err);
+ true
+ } else if let Some(parent) = &mut self.parent {
+ parent.set_variable(symbol, value)
+ } else {
+ false
}
- self.circuits.insert(symbol, insert);
- Ok(())
}
- pub fn insert_variable(&mut self, symbol: Symbol, insert: VariableSymbol<'a>) -> Result<()> {
- self.check_shadowing(symbol, insert.span)?;
- self.variables.variables.insert(symbol, insert);
- Ok(())
+ /// Finds all previous occurrences of the variable and replaces it with a non-const mutable value
+ pub fn deconstify_variable(&mut self, symbol: &Symbol) {
+ if let Some(var) = self.variables.get_mut(symbol) {
+ var.declaration = Declaration::Mut(None);
+ }
+ if let Some(parent) = &mut self.parent {
+ parent.deconstify_variable(symbol)
+ }
}
- pub fn lookup_fn(&self, symbol: Symbol) -> Option<&&'a Function> {
- self.functions.get(&symbol)
+ pub fn get_fn_scope(&self, symbol: &Symbol) -> Option<&RefCell> {
+ if let Some(func) = self.functions.get(symbol) {
+ self.scopes.get(func.id)
+ } else if let Some(parent) = self.parent.as_ref() {
+ parent.get_fn_scope(symbol)
+ } else {
+ None
+ }
}
- pub fn lookup_circuit(&self, symbol: &Symbol) -> Option<&&'a Circuit> {
- self.circuits.get(symbol)
- }
-
- pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol<'a>> {
- self.variables.lookup_variable(symbol)
- }
-
- pub fn push_variable_scope(&mut self) {
- let current_scope = self.variables.clone();
- self.variables = VariableScope {
- parent: Some(Box::new(current_scope)),
- variables: Default::default(),
- };
- }
-
- pub fn pop_variable_scope(&mut self) {
- let parent = self.variables.parent.clone().unwrap();
-
- self.variables = *parent;
- }
-}
-
-impl Display for SymbolTable<'_> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "SymbolTable")?;
-
- for func in self.functions.values() {
- write!(f, "{func}")?;
- }
-
- for circ in self.circuits.values() {
- write!(f, "{circ}")?;
- }
-
- write!(f, "{}", self.variables)
+ pub fn get_block_scope(&self, index: usize) -> Option<&RefCell> {
+ self.scopes.get(index)
}
}
diff --git a/compiler/passes/src/symbol_table/variable_scope.rs b/compiler/passes/src/symbol_table/variable_scope.rs
deleted file mode 100644
index 4c8ca8b93f..0000000000
--- a/compiler/passes/src/symbol_table/variable_scope.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2019-2022 Aleo Systems Inc.
-// This file is part of the Leo library.
-
-// The Leo library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// The Leo library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with the Leo library. If not, see .
-
-use std::fmt::Display;
-
-use indexmap::IndexMap;
-use leo_errors::{AstError, Result};
-use leo_span::{Span, Symbol};
-
-use crate::VariableSymbol;
-
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-pub struct VariableScope<'a> {
- /// The parent scope of variables if it exists.
- /// For example if we are in a if block inside a function.
- /// The parent would be the functions variables and inputs.
- /// This field is populated as necessary.
- pub(crate) parent: Option>>,
- /// The variables defined in a scope.
- /// This field is populated as necessary.
- pub(crate) variables: IndexMap>,
-}
-
-impl<'a> VariableScope<'a> {
- pub fn check_shadowing(&self, symbol: Symbol, span: Span) -> Result<()> {
- if self.variables.contains_key(&symbol) {
- Err(AstError::shadowed_variable(symbol, span).into())
- } else if let Some(parent) = &self.parent {
- parent.check_shadowing(symbol, span)
- } else {
- Ok(())
- }
- }
-
- pub fn clear(&mut self) {
- self.parent = None;
- self.variables.clear();
- }
-
- pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol<'a>> {
- if let Some(var) = self.variables.get(symbol) {
- Some(var)
- } else if let Some(parent) = &self.parent {
- parent.lookup_variable(symbol)
- } else {
- None
- }
- }
-}
-
-impl<'a> Display for VariableScope<'a> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "VariableScope")?;
- self.parent
- .as_ref()
- .map(|parent| write!(f, "parent {parent}"))
- .transpose()?;
-
- for (sym, var) in self.variables.iter() {
- write!(f, "{sym} {var}")?;
- }
-
- Ok(())
- }
-}
diff --git a/compiler/passes/src/symbol_table/variable_symbol.rs b/compiler/passes/src/symbol_table/variable_symbol.rs
index c96aaffecb..28812022c4 100644
--- a/compiler/passes/src/symbol_table/variable_symbol.rs
+++ b/compiler/passes/src/symbol_table/variable_symbol.rs
@@ -14,40 +14,61 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see .
-use std::fmt::Display;
-
-use leo_ast::{ParamMode, Type};
+use leo_ast::{Identifier, ParamMode, Type};
+use leo_errors::Result;
use leo_span::Span;
+use crate::Value;
+
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Declaration {
- Const,
- Input(ParamMode),
- Mut,
+ Const(Option),
+ Input(Type, ParamMode),
+ Mut(Option),
}
-impl Display for Declaration {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl Declaration {
+ pub fn get_as_u128(&self) -> Result