This commit is contained in:
evan-schott 2024-04-17 17:25:45 -07:00
parent 313add0493
commit 30cbed0e98
8 changed files with 34 additions and 34 deletions

View File

@ -291,18 +291,12 @@ impl ParserContext<'_> {
}
/// Returns a [`Output`] AST node if the next tokens represent a function output.
fn parse_function_output(&mut self) -> Result<Output> {
// TODO: Could this span be made more accurate?
fn parse_output(&mut self) -> Result<Output> {
let mode = self.parse_mode()?;
let (type_, span) = self.parse_type()?;
Ok(Output { mode, type_, span, id: self.node_builder.next_id() })
}
/// Returns a [`Output`] AST node if the next tokens represent a function output.
fn parse_output(&mut self) -> Result<Output> {
self.parse_function_output()
}
/// Returns an [`Annotation`] AST node if the next tokens represent an annotation.
fn parse_annotation(&mut self) -> Result<Annotation> {
// Parse the `@` symbol and identifier.

View File

@ -104,7 +104,7 @@ impl ParserContext<'_> {
}
}
Ok((Type::Composite(CompositeType { id: ident, program: None }), ident.span))
Ok((Type::Composite(CompositeType { id: ident, program: self.program_name }), ident.span))
} else if self.token.token == Token::LeftSquare {
// Parse the left bracket.
self.expect(&Token::LeftSquare)?;

View File

@ -59,17 +59,19 @@ 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, location: &Location, span: Span) -> Result<()> {
pub fn check_shadowing(&self, location: &Location, is_struct: bool, span: Span) -> Result<()> {
if self.functions.contains_key(location) {
return Err(AstError::shadowed_function(location.name, span).into());
} else if self.structs.get(location).is_some() {
} else if self.structs.get(location).is_some() && !(location.program.is_none() && is_struct) {
// The second half of the conditional makes sure that structs are only caught for shadowing local records during ST creation, not for redefinition of external structs.
return Err(AstError::shadowed_record(location.name, span).into());
} else if self.structs.get(&Location::new(None, location.name)).is_some() {
} else if self.structs.get(&Location::new(None, location.name)).is_some() && !is_struct {
// Struct redefinition is allowed. If there are more than one occurrences, the error will be caught in the creator pass.
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());
}
if let Some(parent) = self.parent.as_ref() { parent.check_shadowing(location, span) } else { Ok(()) }
if let Some(parent) = self.parent.as_ref() { parent.check_shadowing(location, is_struct, span) } else { Ok(()) }
}
/// Returns the current scope index.
@ -83,7 +85,7 @@ impl SymbolTable {
/// Inserts a function into the symbol table.
pub fn insert_fn(&mut self, location: Location, insert: &Function) -> Result<()> {
let id = self.scope_index();
self.check_shadowing(&location, insert.span)?;
self.check_shadowing(&location, false, insert.span)?;
self.functions.insert(location, Self::new_function_symbol(id, insert));
self.scopes.push(Default::default());
Ok(())
@ -91,20 +93,23 @@ impl SymbolTable {
/// Inserts a struct into the symbol table.
pub fn insert_struct(&mut self, location: Location, insert: &Composite) -> Result<()> {
// 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());
}
return Ok(());
// Check shadowing.
self.check_shadowing(&location, !insert.is_record, insert.span)?;
if insert.is_record {
// Insert the record into the symbol table.
self.structs.insert(location, insert.clone());
} else {
// Don't need to check shadowing if the struct is already inserted.
self.check_shadowing(&location, insert.span)?;
if let Some(struct_) = self.structs.get(&Location::new(None, location.name)) {
// Allow redefinition of external structs so long as the definitions match.
if !self.check_eq_struct(insert, struct_) {
return Err(AstError::redefining_external_struct(location.name, insert.span).into());
}
}
// Insert with program location set to `None` to reflect that in snarkVM structs are not attached to programs (unlike records).
self.structs.insert(Location::new(None, location.name), insert.clone());
}
// Insert the struct into the symbol table.
self.structs.insert(location, insert.clone());
Ok(())
}
@ -117,7 +122,7 @@ impl SymbolTable {
return false;
}
for (member1, member2) in new.members.iter().zip(old.members.iter()) {
if member1.name() != member2.name() || !member1.type_.eq_flat(&member2.type_) {
if member1.name() != member2.name() || !member1.type_.eq_flat_relax_struct(&member2.type_) {
return false;
}
}
@ -126,7 +131,7 @@ impl SymbolTable {
/// Inserts a variable into the symbol table.
pub fn insert_variable(&mut self, location: Location, insert: VariableSymbol) -> Result<()> {
self.check_shadowing(&location, insert.span)?;
self.check_shadowing(&location, false, insert.span)?;
self.variables.insert(location, insert);
Ok(())
}
@ -157,8 +162,8 @@ impl SymbolTable {
pub fn lookup_struct(&self, location: Location, main_program: Option<Symbol>) -> Option<&Composite> {
if let Some(struct_) = self.structs.get(&location) {
return Some(struct_);
} else if location.program.is_none() {
if let Some(struct_) = self.structs.get(&Location::new(main_program, location.name)) {
} else if location.program == main_program {
if let Some(struct_) = self.structs.get(&Location::new(None, location.name)) {
return Some(struct_);
}
}

View File

@ -254,7 +254,7 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> {
// Lookup the struct definition.
// Note that type checking guarantees that the correct struct definition exists.
let struct_definition: &Composite =
self.symbol_table.lookup_struct(Location::new(None, input.name.name), self.program).unwrap();
self.symbol_table.lookup_struct(Location::new(self.program, input.name.name), self.program).unwrap();
// Initialize the list of reordered members.
let mut reordered_members = Vec::with_capacity(members.len());

View File

@ -68,8 +68,8 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
}
fn visit_struct(&mut self, input: &'a Composite) {
// Allow up to one local redefinition for each external struct.
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());
}

View File

@ -647,8 +647,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
}
fn visit_struct_init(&mut self, input: &'a StructExpression, additional: &Self::AdditionalInput) -> Self::Output {
let struct_ = self.lookup_struct(None, input.name.name);
if let Some(struct_) = struct_ {
if let Some(struct_) = self.lookup_struct(self.program_name, input.name.name) {
// Check struct type name.
let ret = self.check_expected_struct(&struct_, additional, input.name.span());

View File

@ -1328,7 +1328,7 @@ impl<'a> TypeChecker<'a> {
self.assert_type_is_valid(&finalize.output_type, finalize.span);
}
/// Wrapper around lookup_struct that records all structs that are used in the program.
/// Wrapper around lookup_struct that additionally records all structs that are used in the program.
pub(crate) fn lookup_struct(&mut self, program: Option<Symbol>, name: Symbol) -> Option<Composite> {
let struct_ =
self.symbol_table.borrow().lookup_struct(Location::new(program, name), self.program_name).cloned();

View File

@ -36,7 +36,9 @@ impl<'a> Pass for TypeChecker<'a> {
let mut visitor = TypeChecker::new(st, tt, handler);
visitor.visit_program(ast.as_repr());
handler.last_err().map_err(|e| *e)?;
// Remove unused structs from the struct graph.
// Remove unused structs from the struct graph.
// This prevents unused struct definitions from being included in the generated bytecode.
visitor.struct_graph.retain_nodes(&visitor.used_structs);
Ok((visitor.symbol_table.take(), visitor.struct_graph, visitor.call_graph))
}