Pull changes to compiler/passes/symbol_table from improved-flattening; remove unused files

This commit is contained in:
Pranav Gaddamadugu 2022-07-01 15:55:35 -07:00
parent 6b5f9a3926
commit 24f38721d6
6 changed files with 260 additions and 174 deletions

View File

@ -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> {

View File

@ -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 <https://www.gnu.org/licenses/>.
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<FunctionInput>,
}
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(),
}
}
}

View File

@ -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<SymbolTable<'a>>;
type Output = Result<SymbolTable>;
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)
}
}

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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<Box<SymbolTable>>,
/// Functions represents the name of each function mapped to the Ast's function definition.
/// This field is populated at a first pass.
functions: IndexMap<Symbol, &'a Function>,
pub functions: IndexMap<Symbol, FunctionSymbol>,
/// Maps circuit names to circuit definitions.
/// This field is populated at a first pass.
circuits: IndexMap<Symbol, &'a Circuit>,
/// Variables represents functions variable definitions and input variables.
/// This field is not populated till necessary.
pub(crate) variables: VariableScope<'a>,
pub circuits: IndexMap<Symbol, Circuit>,
/// The variables defined in a scope.
/// This field is populated as necessary.
pub(crate) variables: IndexMap<Symbol, VariableSymbol>,
/// 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<RefCell<SymbolTable>>,
}
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<Self>> {
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>> {
self.scopes.get(index)
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
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<Box<VariableScope<'a>>>,
/// The variables defined in a scope.
/// This field is populated as necessary.
pub(crate) variables: IndexMap<Symbol, VariableSymbol<'a>>,
}
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(())
}
}

View File

@ -14,40 +14,61 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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<Value>),
Input(Type, ParamMode),
Mut(Option<Value>),
}
impl Display for Declaration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl Declaration {
pub fn get_as_u128(&self) -> Result<Option<u128>> {
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<Type> {
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<Self> 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<Value> {
use Declaration::*;
match &self.declaration {
Const(Some(v)) | Mut(Some(v)) => Some(v.clone()),
Input(type_, ParamMode::Const) => Some(Value::Input(*type_, ident)),
_ => None,
}
}
}