Implement check for nested import

This commit is contained in:
Pranav Gaddamadugu 2022-10-07 15:09:29 -07:00 committed by d0cd
parent 41a8bc5a73
commit 7e70da2326
6 changed files with 45 additions and 6 deletions

View File

@ -71,10 +71,11 @@ impl ParserContext<'_> {
)
}
// TODO: remove import resolution from parser.
/// Parses an import statement `import foo.leo;`.
pub(super) fn parse_import(&mut self) -> Result<(Identifier, Program)> {
pub(super) fn parse_import(&mut self) -> Result<(Identifier, (Program, Span))> {
// Parse `import`.
let _start = self.expect(&Token::Import)?;
let start = self.expect(&Token::Import)?;
// Parse `foo`.
let import_name = self.expect_identifier()?;
@ -86,7 +87,7 @@ impl ParserContext<'_> {
return Err(ParserError::leo_imports_only(self.token.span).into());
}
let _end = self.expect(&Token::Semicolon)?;
let end = self.expect(&Token::Semicolon)?;
// Tokenize and parse import file.
// Todo: move this to a different module.
@ -114,7 +115,7 @@ impl ParserContext<'_> {
// Use the parser to construct the imported abstract syntax tree (ast).
let program_ast = parse_ast(self.handler, &prg_sf.src, prg_sf.start_pos)?;
Ok((import_name, program_ast.into_repr()))
Ok((import_name, (program_ast.into_repr(), start + end)))
}
/// Parsers a program scope `program foo.aleo { ... }`.

View File

@ -34,7 +34,7 @@ impl<'a> CodeGenerator<'a> {
&input
.imports
.iter()
.map(|(identifier, imported_program)| self.visit_import(identifier, imported_program))
.map(|(identifier, (imported_program, _))| self.visit_import(identifier, imported_program))
.join("\n"),
);

View File

@ -152,7 +152,7 @@ impl ProgramConsumer for StaticSingleAssigner<'_> {
imports: input
.imports
.into_iter()
.map(|(name, import)| (name, self.consume_program(import)))
.map(|(name, (import, span))| (name, (self.consume_program(import), span)))
.collect(),
program_scopes: input
.program_scopes

View File

@ -287,4 +287,32 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
// Unset `is_transition_function` flag.
self.is_transition_function = false;
}
fn visit_program(&mut self, input: &'a Program) {
match self.is_imported {
// If the program is imported, then it is not allowed to import any other programs.
true => {
input.imports.values().for_each(|(_, span)| {
self.emit_err(TypeCheckerError::imported_program_cannot_import_program(*span))
});
}
// Otherwise, typecheck the imported programs.
false => {
// Set `self.is_imported`.
let previous_is_imported = core::mem::replace(&mut self.is_imported, true);
// Typecheck the imported programs.
input.imports.values().for_each(|import| self.visit_import(&import.0));
// Set `self.is_imported` to its previous state.
self.is_imported = previous_is_imported;
}
}
// Typecheck the program scopes.
input
.program_scopes
.values()
.for_each(|scope| self.visit_program_scope(scope));
}
}

View File

@ -39,6 +39,8 @@ pub struct TypeChecker<'a> {
pub(crate) is_transition_function: bool,
/// Whether or not we are currently traversing a finalize block.
pub(crate) is_finalize: bool,
/// Whether or not we are currently traversing an imported program.
pub(crate) is_imported: bool,
}
const BOOLEAN_TYPE: Type = Type::Boolean;
@ -95,6 +97,7 @@ impl<'a> TypeChecker<'a> {
has_return: false,
has_finalize: false,
is_finalize: false,
is_imported: false,
}
}

View File

@ -430,4 +430,11 @@ create_messages!(
msg: format!("Loop bound must be a literal."),
help: None,
}
@formatted
imported_program_cannot_import_program {
args: (),
msg: format!("An imported program cannot import another program."),
help: None,
}
);