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> { use Declaration::*; match self { - Const => write!(f, "const var"), - Input(m) => write!(f, "{m} input"), - Mut => write!(f, "mut var"), + Const(Some(value)) => Ok(Some(value.try_into()?)), + Input(_, _) => Ok(None), + _ => Ok(None), + } + } + + pub fn get_type(&self) -> Option { + use Declaration::*; + + match self { + Const(Some(value)) => Some(value.into()), + Input(type_, _) => Some(*type_), + _ => None, } } } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct VariableSymbol<'a> { - pub type_: &'a Type, +impl AsRef for Declaration { + fn as_ref(&self) -> &Self { + self + } +} + +#[derive(Clone, Debug)] +pub struct VariableSymbol { + pub type_: Type, pub span: Span, pub declaration: Declaration, } -impl<'a> Display for VariableSymbol<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}: {}", self.declaration, self.type_)?; - Ok(()) +impl VariableSymbol { + pub fn get_const_value(&self, ident: Identifier) -> Option { + use Declaration::*; + match &self.declaration { + Const(Some(v)) | Mut(Some(v)) => Some(v.clone()), + Input(type_, ParamMode::Const) => Some(Value::Input(*type_, ident)), + _ => None, + } } }