Refactor ST to account for fact that local records can have program None

This commit is contained in:
evan-schott 2024-04-15 18:35:30 -07:00
parent 0a4e5b3642
commit c296f61f2f
4 changed files with 104 additions and 85 deletions

View File

@ -16,7 +16,7 @@
use crate::CodeGenerator;
use leo_ast::{functions, Composite, Function, Mapping, Mode, Program, ProgramScope, Type, Variant};
use leo_ast::{Composite, Function, Mapping, Mode, Program, ProgramScope, Type, Variant};
use indexmap::IndexMap;
use itertools::Itertools;
@ -51,8 +51,21 @@ impl<'a> CodeGenerator<'a> {
let order = self.struct_graph.post_order().unwrap();
// Create a mapping of symbols to references of structs so can perform constant-time lookups.
let structs_map: IndexMap<Symbol, &Composite> =
program_scope.structs.iter().map(|(name, struct_)| (*name, struct_)).collect();
let structs_map: IndexMap<Symbol, &Composite> = self
.symbol_table
.structs
.iter()
.filter_map(|(name, struct_)| {
// Only include structs and local records.
if !(struct_.is_record
&& struct_.external.map(|program| program != self.program_id.unwrap().name.name).unwrap_or(false))
{
Some((name.name, struct_))
} else {
None
}
})
.collect();
// Visit each `Struct` or `Record` in the post-ordering and produce an Aleo struct or record.
program_string.push_str(
@ -60,9 +73,9 @@ impl<'a> CodeGenerator<'a> {
.into_iter()
.map(|name| {
match structs_map.get(&name) {
// If the struct is found, it is a local struct.
// If the struct is found, it is a struct or external record.
Some(struct_) => self.visit_struct_or_record(struct_),
// If the struct is not found, it is an imported struct.
// If the struct is not found, it is an imported record.
None => String::new(),
}
})
@ -169,20 +182,12 @@ impl<'a> CodeGenerator<'a> {
let register_string = format!("r{}", self.next_register);
self.next_register += 1;
let type_string = match input {
functions::Input::Internal(input) => {
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
let visibility = match (self.is_transition_function, input.mode) {
(true, Mode::None) => Mode::Private,
_ => input.mode,
};
self.visit_type_with_visibility(&input.type_, visibility)
}
functions::Input::External(input) => {
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
format!("{}.aleo/{}.record", input.program_name, input.record)
}
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
let visibility = match (self.is_transition_function, input.mode) {
(true, Mode::None) => Mode::Private,
_ => input.mode,
};
let type_string = self.visit_type_with_visibility(&input.type_, visibility);
writeln!(function_string, " input {register_string} as {type_string};",)
.expect("failed to write to string");
@ -223,23 +228,15 @@ impl<'a> CodeGenerator<'a> {
let register_string = format!("r{}", self.next_register);
self.next_register += 1;
// TODO: Dedup code.
let type_string = match input {
functions::Input::Internal(input) => {
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
let visibility = match (self.is_transition_function, input.mode) {
(true, Mode::None) => Mode::Public,
_ => input.mode,
};
self.visit_type_with_visibility(&input.type_, visibility)
}
functions::Input::External(input) => {
self.variable_mapping.insert(&input.program_name.name, register_string.clone());
format!("{}.aleo/{}.record", input.program_name, input.record)
}
let visibility = match (self.is_transition_function, input.mode) {
(true, Mode::None) => Mode::Public,
_ => input.mode,
};
let type_string = self.visit_type_with_visibility(&input.type_, visibility);
writeln!(function_string, " input {register_string} as {type_string};",)
.expect("failed to write to string");
}

View File

@ -28,7 +28,6 @@ use leo_ast::{
ExpressionStatement,
IterationStatement,
Mode,
Output,
ReturnStatement,
Statement,
};
@ -103,38 +102,28 @@ impl<'a> CodeGenerator<'a> {
.iter()
.zip_eq(output)
.map(|(operand, output)| {
match output {
Output::Internal(output) => {
let visibility = if self.is_transition_function {
match self.in_finalize {
// If in finalize block, the default visibility is public.
true => match output.mode {
Mode::None => Mode::Public,
mode => mode,
},
// If not in finalize block, the default visibility is private.
false => match output.mode {
Mode::None => Mode::Private,
mode => mode,
},
}
} else {
// Only program functions have visibilities associated with their outputs.
Mode::None
};
format!(
" output {} as {};\n",
operand,
self.visit_type_with_visibility(&output.type_, visibility)
)
let visibility = if self.is_transition_function {
match self.in_finalize {
// If in finalize block, the default visibility is public.
true => match output.mode {
Mode::None => Mode::Public,
mode => mode,
},
// If not in finalize block, the default visibility is private.
false => match output.mode {
Mode::None => Mode::Private,
mode => mode,
},
}
Output::External(output) => {
format!(
" output {} as {}.aleo/{}.record;\n",
operand, output.program_name, output.record,
)
}
}
} else {
// Only program functions have visibilities associated with their outputs.
Mode::None
};
format!(
" output {} as {};\n",
operand,
self.visit_type_with_visibility(&output.type_, visibility)
)
})
.join("");

View File

@ -28,7 +28,7 @@ use std::cell::RefCell;
use leo_ast::{normalize_json_value, remove_key_from_json, Composite, Function};
use leo_errors::{AstError, Result};
use leo_span::Span;
use leo_span::{Span, Symbol};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
@ -62,11 +62,10 @@ impl SymbolTable {
pub fn check_shadowing(&self, location: &Location, span: Span) -> Result<()> {
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(location.name, span).into()),
false => Err(AstError::shadowed_struct(location.name, span).into()),
};
} else if self.structs.get(location).is_some() {
return Err(AstError::shadowed_record(location.name, span).into());
} else if self.structs.get(&Location::new(None, location.name)).is_some() {
return Err(AstError::shadowed_struct(location.name, span).into());
} else if self.variables.contains_key(location) {
return Err(AstError::shadowed_variable(location.name, span).into());
}
@ -92,13 +91,37 @@ impl SymbolTable {
/// Inserts a struct into the symbol table.
pub fn insert_struct(&mut self, location: Location, insert: &Composite) -> Result<()> {
match self.check_shadowing(&location, insert.span) {
Ok(_) => {
self.structs.insert(location, insert.clone());
Ok(())
// Check to see if the struct matches an existing one.
if let Some(existing) = self.structs.get(&location) {
if !self.check_eq_struct(insert, existing) {
return Err(AstError::redefining_external_struct(location.name, existing.span).into());
}
Err(e) => Err(e),
return Ok(());
} else {
// Don't need to check shadowing if the struct is already inserted.
self.check_shadowing(&location, insert.span)?;
}
// Insert the struct into the symbol table.
self.structs.insert(location, insert.clone());
Ok(())
}
/// Checks if two structs are equal.
fn check_eq_struct(&self, new: &Composite, old: &Composite) -> bool {
if new.is_record || old.is_record {
return false;
}
if new.members.len() != old.members.len() {
return false;
}
for (member1, member2) in new.members.iter().zip(old.members.iter()) {
if member1.name() != member2.name() || !member1.type_.eq_flat(&member2.type_) {
return false;
}
}
true
}
/// Inserts a variable into the symbol table.
@ -131,14 +154,15 @@ impl SymbolTable {
}
/// Attempts to lookup a struct in the symbol table.
pub fn lookup_struct(&self, location: Location) -> Option<&Composite> {
pub fn lookup_struct(&self, location: Location, main_program: Option<Symbol>) -> Option<&Composite> {
if let Some(struct_) = self.structs.get(&location) {
Some(struct_)
} else if let Some(parent) = self.parent.as_ref() {
parent.lookup_struct(location)
} else {
None
return Some(struct_);
} else if location.program.is_none() {
if let Some(struct_) = self.structs.get(&Location::new(main_program, location.name)) {
return Some(struct_);
}
}
if let Some(parent) = self.parent.as_ref() { parent.lookup_struct(location, main_program) } else { None }
}
/// Attempts to lookup a variable in the symbol table.

View File

@ -14,8 +14,9 @@
// 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 indexmap::IndexSet;
use leo_ast::*;
use leo_errors::emitter::Handler;
use leo_errors::{emitter::Handler, AstError, LeoError};
use leo_span::Symbol;
use crate::{Location, SymbolTable, VariableSymbol, VariableType};
@ -32,11 +33,13 @@ pub struct SymbolTableCreator<'a> {
program_name: Option<Symbol>,
/// Whether or not traversing stub.
is_stub: bool,
/// The set of local structs that have been successfully visited.
structs: IndexSet<Symbol>,
}
impl<'a> SymbolTableCreator<'a> {
pub fn new(handler: &'a Handler) -> Self {
Self { symbol_table: Default::default(), handler, program_name: None, is_stub: false }
Self { symbol_table: Default::default(), handler, program_name: None, is_stub: false, structs: IndexSet::new() }
}
}
@ -65,7 +68,13 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
}
fn visit_struct(&mut self, input: &'a Composite) {
if let Err(err) = self.symbol_table.insert_struct(Location::new(self.program_name, input.name()), input) {
if !input.is_record {
// Forbid redefinition of structs.
if !self.structs.insert(input.name()) {
return self.handler.emit_err::<LeoError>(AstError::shadowed_struct(input.name(), input.span).into());
}
}
if let Err(err) = self.symbol_table.insert_struct(Location::new(input.external, input.name()), input) {
self.handler.emit_err(err);
}
}
@ -115,7 +124,7 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
}
fn visit_struct_stub(&mut self, input: &'a Composite) {
if let Err(err) = self.symbol_table.insert_struct(Location::new(self.program_name, input.name()), input) {
if let Err(err) = self.symbol_table.insert_struct(Location::new(input.external, input.name()), input) {
self.handler.emit_err(err);
}
}