From 75f3647879e016873a10ef36447c5ff3b64cadaa Mon Sep 17 00:00:00 2001 From: evan-schott <53463459+evan-schott@users.noreply.github.com> Date: Wed, 27 Mar 2024 10:24:01 -0700 Subject: [PATCH] Passing ST serialization test --- .../common/symbol_table/function_symbol.rs | 4 +- .../src/common/symbol_table/location.rs | 20 ++- .../passes/src/common/symbol_table/mod.rs | 127 +++++++++--------- .../src/symbol_table_creation/creator.rs | 24 +++- 4 files changed, 95 insertions(+), 80 deletions(-) diff --git a/compiler/passes/src/common/symbol_table/function_symbol.rs b/compiler/passes/src/common/symbol_table/function_symbol.rs index 1d3c7307f7..ec347ca788 100644 --- a/compiler/passes/src/common/symbol_table/function_symbol.rs +++ b/compiler/passes/src/common/symbol_table/function_symbol.rs @@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize}; use crate::SymbolTable; /// Metadata associated with the finalize block. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct FinalizeData { /// The inputs to the finalize block. pub(crate) input: Vec, @@ -31,7 +31,7 @@ pub struct FinalizeData { } /// An entry for a function in the symbol table. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct FunctionSymbol { /// The index associated with the scope in the parent symbol table. pub(crate) id: usize, diff --git a/compiler/passes/src/common/symbol_table/location.rs b/compiler/passes/src/common/symbol_table/location.rs index 719c044d98..bda2edfca2 100644 --- a/compiler/passes/src/common/symbol_table/location.rs +++ b/compiler/passes/src/common/symbol_table/location.rs @@ -20,13 +20,13 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; // Create custom struct to wrap (Symbol, Symbol) so that it can be serialized and deserialized. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Location { - pub program: Symbol, + pub program: Option, pub name: Symbol, } impl Location { // Create new Location instance. - pub fn new(program: Symbol, name: Symbol) -> Location { + pub fn new(program: Option, name: Symbol) -> Location { Location { program, name } } } @@ -36,7 +36,11 @@ impl Serialize for Location { where S: Serializer, { - serializer.serialize_str(&format!("{}/{}", self.program, self.name)) + let condensed_str = match self.program { + Some(program) => format!("{}/{}", program, self.name), + None => format!("{}", self.name), + }; + serializer.serialize_str(&condensed_str) } } @@ -46,9 +50,13 @@ impl<'de> Deserialize<'de> for Location { D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; - let mut parts = s.split('/'); - let program = Symbol::intern(parts.next().unwrap()); - let name = Symbol::intern(parts.next().unwrap()); + let mut parts: Vec<&str> = s.split('/').collect(); + let program = if parts.len() == 1 { + None + } else { + Some(Symbol::intern(parts.remove(0))) + }; + let name = Symbol::intern(parts.get(0).unwrap()); Ok(Location::new(program, name)) } } diff --git a/compiler/passes/src/common/symbol_table/mod.rs b/compiler/passes/src/common/symbol_table/mod.rs index 9335a4684b..ae24c0b471 100644 --- a/compiler/passes/src/common/symbol_table/mod.rs +++ b/compiler/passes/src/common/symbol_table/mod.rs @@ -36,7 +36,7 @@ use serde_json; // TODO (@d0cd) Consider a safe interface for the symbol table. // TODO (@d0cd) Cleanup API -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] pub struct SymbolTable { /// The parent scope if it exists. /// For example, the parent scope of a then-block is the scope containing the associated ConditionalStatement. @@ -49,7 +49,7 @@ pub struct SymbolTable { pub structs: IndexMap, /// The variables defined in a scope. /// This field is populated as necessary. - pub(crate) variables: IndexMap, + pub(crate) variables: IndexMap, /// The index of the current scope. pub(crate) scope_index: usize, /// The sub-scopes of this scope. @@ -59,21 +59,22 @@ pub struct SymbolTable { impl SymbolTable { /// Recursively checks if the symbol table contains an entry for the given symbol. /// Leo does not allow any variable shadowing or overlap between different symbols. - pub fn check_shadowing(&self, program: Option, symbol: Symbol, span: Span) -> Result<()> { - if let Some(program) = program { - if self.functions.contains_key(&Location::new(program, symbol)) { - return Err(AstError::shadowed_function(symbol, span).into()); - } else if let Some(existing) = self.structs.get(&Location::new(program, symbol)) { + pub fn check_shadowing(&self, location: &Location, span: Span) -> Result<()> { + if let Some(_) = location.program { + if self.functions.contains_key(location) { + return Err(AstError::shadowed_function(location.name, span).into()); + } else if let Some(existing) = self.structs.get(location) { return match existing.is_record { - true => Err(AstError::shadowed_record(symbol, span).into()), - false => Err(AstError::shadowed_struct(symbol, span).into()), + true => Err(AstError::shadowed_record(location.name, span).into()), + false => Err(AstError::shadowed_struct(location.name, span).into()), }; } - } - if self.variables.contains_key(&symbol) { - Err(AstError::shadowed_variable(symbol, span).into()) - } else if let Some(parent) = self.parent.as_ref() { - parent.check_shadowing(program, symbol, span) + else if self.variables.contains_key(location) { + return Err(AstError::shadowed_variable(location.name, span).into()) + } + } + if let Some(parent) = self.parent.as_ref() { + return parent.check_shadowing(location, span) } else { Ok(()) } @@ -88,19 +89,19 @@ impl SymbolTable { } /// Inserts a function into the symbol table. - pub fn insert_fn(&mut self, program: Symbol, symbol: Symbol, insert: &Function) -> Result<()> { + pub fn insert_fn(&mut self, location: Location, insert: &Function) -> Result<()> { let id = self.scope_index(); - self.check_shadowing(Some(program), symbol, insert.span)?; - self.functions.insert(Location::new(program, symbol), Self::new_function_symbol(id, insert)); + self.check_shadowing(&location, insert.span)?; + self.functions.insert(location, Self::new_function_symbol(id, insert)); self.scopes.push(Default::default()); Ok(()) } /// Inserts a struct into the symbol table. - pub fn insert_struct(&mut self, program: Symbol, symbol: Symbol, insert: &Composite) -> Result<()> { - match self.check_shadowing(Some(program), symbol, insert.span) { + pub fn insert_struct(&mut self, location: Location, insert: &Composite) -> Result<()> { + match self.check_shadowing(&location, insert.span) { Ok(_) => { - self.structs.insert(Location::new(program, symbol), insert.clone()); + self.structs.insert(location, insert.clone()); Ok(()) } Err(e) => Err(e), @@ -108,15 +109,15 @@ impl SymbolTable { } /// Inserts a variable into the symbol table. - pub fn insert_variable(&mut self, symbol: Symbol, insert: VariableSymbol) -> Result<()> { - self.check_shadowing(None, symbol, insert.span)?; - self.variables.insert(symbol, insert); + pub fn insert_variable(&mut self, location: Location, insert: VariableSymbol) -> Result<()> { + self.check_shadowing(&location, insert.span)?; + self.variables.insert(location, insert); Ok(()) } /// Removes a variable from the symbol table. - pub fn remove_variable_from_current_scope(&mut self, symbol: Symbol) { - self.variables.remove(&symbol); + pub fn remove_variable_from_current_scope(&mut self, location: Location) { + self.variables.remove(&location); } /// Creates a new scope for the block and stores it in the symbol table. @@ -126,58 +127,33 @@ impl SymbolTable { } /// Attempts to lookup a function in the symbol table. - pub fn lookup_fn_symbol(&self, program: Symbol, symbol: Symbol) -> Option<&FunctionSymbol> { - if let Some(func) = self.functions.get(&Location::new(program, symbol)) { + pub fn lookup_fn_symbol(&self, location: Location) -> Option<&FunctionSymbol> { + if let Some(func) = self.functions.get(&location) { Some(func) } else if let Some(parent) = self.parent.as_ref() { - parent.lookup_fn_symbol(program, symbol) + parent.lookup_fn_symbol(location) } else { None } } /// Attempts to lookup a struct in the symbol table. - pub fn lookup_struct(&self, program: Symbol, symbol: Symbol) -> Option<&Composite> { - if let Some(struct_) = self.structs.get(&Location::new(program, symbol)) { + pub fn lookup_struct(&self, location: Location) -> Option<&Composite> { + if let Some(struct_) = self.structs.get(&location) { Some(struct_) } else if let Some(parent) = self.parent.as_ref() { - parent.lookup_struct(program, symbol) + parent.lookup_struct(location) } else { None } } /// Attempts to lookup a variable in the symbol table. - pub fn lookup_variable(&self, symbol: Symbol) -> Option<&VariableSymbol> { - if let Some(var) = self.variables.get(&symbol) { + pub fn lookup_variable(&self, location: Location) -> Option<&VariableSymbol> { + if let Some(var) = self.variables.get(&location) { 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 { parent.variable_in_parent_scope(symbol) } - } else { - false - } - } - - /// Returns a mutable reference to the `VariableSymbol` if it exists in the symbol table. - 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) + parent.lookup_variable(location) } else { None } @@ -251,12 +227,10 @@ mod tests { use leo_ast::{Identifier, Type, Variant}; use leo_span::symbol::create_session_if_not_set_then; #[test] - #[ignore] fn serialization_test() { create_session_if_not_set_then(|_| { let mut symbol_table = SymbolTable::default(); - let program = Symbol::intern("credits"); - let function = Symbol::intern("transfer_public"); + let func_loc = Location::new(Some(Symbol::intern("credits")), Symbol::intern("transfer_public")); let insert = Function { annotations: Vec::new(), id: 0, @@ -269,11 +243,34 @@ mod tests { output: vec![], block: Default::default(), }; - symbol_table.insert_fn(program, function, &insert).unwrap(); + symbol_table.insert_fn(func_loc, &insert).unwrap(); + symbol_table.insert_variable( + Location::new(Some(Symbol::intern("credits")), Symbol::intern("accounts")), + VariableSymbol { + type_: Type::Address, + span: Default::default(), + declaration: VariableType::Const, + }).unwrap(); + symbol_table.insert_struct( + Location::new(Some(Symbol::intern("credits")), Symbol::intern("token")), + &Composite { + is_record: false, + span: Default::default(), + id: 0, + identifier: Identifier::new(Symbol::intern("token"), Default::default()), + members: Vec::new(), + external: None, + }).unwrap(); + symbol_table.insert_variable( + Location::new(None, Symbol::intern("foo")), + VariableSymbol { + type_: Type::Address, + span: Default::default(), + declaration: VariableType::Const, + }).unwrap(); let json = symbol_table.to_json_string().unwrap(); - dbg!(json.clone()); let deserialized = SymbolTable::from_json_string(&json).unwrap(); - dbg!(deserialized); + assert_eq!(symbol_table, deserialized); }); } } diff --git a/compiler/passes/src/symbol_table_creation/creator.rs b/compiler/passes/src/symbol_table_creation/creator.rs index ac4375cbfd..ff3c092479 100644 --- a/compiler/passes/src/symbol_table_creation/creator.rs +++ b/compiler/passes/src/symbol_table_creation/creator.rs @@ -18,7 +18,7 @@ use leo_ast::*; use leo_errors::emitter::Handler; use leo_span::Symbol; -use crate::{SymbolTable, VariableSymbol, VariableType}; +use crate::{Location, SymbolTable, VariableSymbol, VariableType}; /// A compiler pass during which the `SymbolTable` is created. /// Note that this pass only creates the initial entries for functions, structs, and records. @@ -30,11 +30,13 @@ pub struct SymbolTableCreator<'a> { handler: &'a Handler, /// The current program name. program_name: Option, + /// Whether or not traversing stub. + is_stub: bool, } impl<'a> SymbolTableCreator<'a> { pub fn new(handler: &'a Handler) -> Self { - Self { symbol_table: Default::default(), handler, program_name: None } + Self { symbol_table: Default::default(), handler, program_name: None, is_stub: false } } } @@ -49,6 +51,7 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> { fn visit_program_scope(&mut self, input: &'a ProgramScope) { // Set current program name self.program_name = Some(input.program_id.name.name); + self.is_stub = false; // Visit the program scope input.structs.iter().for_each(|(_, c)| (self.visit_struct(c))); @@ -62,17 +65,23 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> { } fn visit_struct(&mut self, input: &'a Composite) { - if let Err(err) = self.symbol_table.insert_struct(self.program_name.unwrap(), input.name(), input) { + if let Err(err) = self.symbol_table.insert_struct(Location::new(self.program_name, input.name()), input) { self.handler.emit_err(err); } } fn visit_mapping(&mut self, input: &'a Mapping) { + // Check if mapping is external. + let program = match self.is_stub { + true => self.program_name, + false => None, + }; // Add the variable associated with the mapping to the symbol table. - if let Err(err) = self.symbol_table.insert_variable(input.identifier.name, VariableSymbol { + if let Err(err) = self.symbol_table.insert_variable(Location::new(program, input.identifier.name), VariableSymbol { type_: Type::Mapping(MappingType { key: Box::new(input.key_type.clone()), value: Box::new(input.value_type.clone()), + program: self.program_name.unwrap(), }), span: input.span, declaration: VariableType::Mut, @@ -82,12 +91,13 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> { } fn visit_function(&mut self, input: &'a Function) { - if let Err(err) = self.symbol_table.insert_fn(self.program_name.unwrap(), input.name(), input) { + if let Err(err) = self.symbol_table.insert_fn(Location::new(self.program_name, input.name()), input) { self.handler.emit_err(err); } } fn visit_stub(&mut self, input: &'a Stub) { + self.is_stub = true; self.program_name = Some(input.stub_id.name.name); input.functions.iter().for_each(|(_, c)| (self.visit_function_stub(c))); input.structs.iter().for_each(|(_, c)| (self.visit_struct_stub(c))); @@ -95,14 +105,14 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> { fn visit_function_stub(&mut self, input: &'a FunctionStub) { if let Err(err) = - self.symbol_table.insert_fn(self.program_name.unwrap(), input.name(), &Function::from(input.clone())) + self.symbol_table.insert_fn(Location::new(self.program_name, input.name()), &Function::from(input.clone())) { self.handler.emit_err(err); } } fn visit_struct_stub(&mut self, input: &'a Composite) { - if let Err(err) = self.symbol_table.insert_struct(self.program_name.unwrap(), input.name(), input) { + if let Err(err) = self.symbol_table.insert_struct(Location::new(self.program_name, input.name()), input) { self.handler.emit_err(err); } }