From 9933b8e3367371d9106fc76e181b4352de90b373 Mon Sep 17 00:00:00 2001 From: collin Date: Mon, 26 Oct 2020 12:24:24 -0700 Subject: [PATCH] impl dynamic checks for core packages --- Cargo.lock | 1 + compiler/src/compiler.rs | 5 +- compiler/src/import/store/import.rs | 15 ++-- imports/src/parser/import_parser.rs | 9 +++ static-check/Cargo.toml | 4 + static-check/src/errors/symbol_table.rs | 9 +++ static-check/src/static_check.rs | 30 +++++-- static-check/src/symbol_table.rs | 103 ++++++++++++++++++++---- 8 files changed, 140 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7998c227c..cacc1e9a1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1415,6 +1415,7 @@ name = "leo-static-check" version = "1.0.3" dependencies = [ "leo-ast", + "leo-core", "leo-imports", "leo-typed", "serde", diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index 8d07925ce6..c2d2a90ef9 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -162,15 +162,14 @@ impl> Compiler { let typed_tree = LeoTypedAst::new(&package_name, &ast); self.program = typed_tree.into_repr(); + self.imported_programs = ImportParser::parse(&self.program)?; // Run static check on program. - let symbol_table = StaticCheck::run(&self.program, &self.program_input)?; + let symbol_table = StaticCheck::run(&self.program, &self.program_input, &self.imported_programs)?; // Run dynamic check on program. DynamicCheck::run(&self.program, symbol_table)?; - self.imported_programs = ImportParser::parse(&self.program)?; - tracing::debug!("Program parsing complete\n{:#?}", self.program); Ok(()) diff --git a/compiler/src/import/store/import.rs b/compiler/src/import/store/import.rs index 833e60c98e..d25175d877 100644 --- a/compiler/src/import/store/import.rs +++ b/compiler/src/import/store/import.rs @@ -27,13 +27,10 @@ impl> ConstrainedProgram { import: &Import, imported_programs: &ImportParser, ) -> Result<(), ImportError> { - // Fetch core dependencies - let core_dependency = imported_programs - .core_packages() - .iter() - .find(|package| import.package.eq(package)); + // Fetch core packages + let core_package = imported_programs.get_core_package(&import.package); - if let Some(package) = core_dependency { + if let Some(package) = core_package { self.store_core_package(scope.clone(), package.clone())?; return Ok(()); @@ -42,17 +39,17 @@ impl> ConstrainedProgram { // Fetch dependencies for the current import let imported_symbols = ImportedSymbols::from(import); - for (package, symbol) in imported_symbols.symbols { + for (name, symbol) in imported_symbols.symbols { // Find imported program let program = imported_programs - .get_import(&package) + .get_import(&name) .ok_or(ImportError::unknown_package(import.package.name.clone()))?; // Parse imported program self.store_definitions(program.clone(), imported_programs)?; // Store the imported symbol - self.store_symbol(scope.clone(), package, &symbol, program)?; + self.store_symbol(scope.clone(), name, &symbol, program)?; } Ok(()) diff --git a/imports/src/parser/import_parser.rs b/imports/src/parser/import_parser.rs index 05bd601bc0..c8e3b41eae 100644 --- a/imports/src/parser/import_parser.rs +++ b/imports/src/parser/import_parser.rs @@ -90,6 +90,15 @@ impl ImportParser { self.imports.get(file_name) } + /// + /// Returns a reference to the core package corresponding to the given package. + /// + pub fn get_core_package(&self, package: &Package) -> Option<&Package> { + self.core_packages() + .iter() + .find(|core_package| core_package.eq(&package)) + } + /// /// Returns a reference to the vector of core packages. /// diff --git a/static-check/Cargo.toml b/static-check/Cargo.toml index b1d204b109..c79a458702 100644 --- a/static-check/Cargo.toml +++ b/static-check/Cargo.toml @@ -21,6 +21,10 @@ edition = "2018" path = "../ast" version = "1.0.3" +[dependencies.leo-core] +path = "../core" +version = "1.0.3" + [dependencies.leo-imports] path = "../imports" version = "1.0.3" diff --git a/static-check/src/errors/symbol_table.rs b/static-check/src/errors/symbol_table.rs index 59c722c1a1..36505648a5 100644 --- a/static-check/src/errors/symbol_table.rs +++ b/static-check/src/errors/symbol_table.rs @@ -15,6 +15,7 @@ // along with the Leo library. If not, see . use crate::TypeError; +use leo_core::{CorePackageListError, LeoCoreError}; use leo_typed::{Error as FormattedError, Identifier, Span}; use std::path::PathBuf; @@ -22,9 +23,15 @@ use std::path::PathBuf; /// Errors encountered when tracking variable, function, and circuit names in a program. #[derive(Debug, Error)] pub enum SymbolTableError { + #[error("{}", _0)] + CorePackageListError(#[from] CorePackageListError), + #[error("{}", _0)] Error(#[from] FormattedError), + #[error("{}", _0)] + LeoCoreError(#[from] LeoCoreError), + #[error("{}", _0)] TypeError(#[from] TypeError), } @@ -35,7 +42,9 @@ impl SymbolTableError { /// pub fn set_path(&mut self, path: PathBuf) { match self { + SymbolTableError::CorePackageListError(error) => error.set_path(path), SymbolTableError::Error(error) => error.set_path(path), + SymbolTableError::LeoCoreError(error) => error.set_path(path), SymbolTableError::TypeError(error) => error.set_path(path), } } diff --git a/static-check/src/static_check.rs b/static-check/src/static_check.rs index e00293760d..b9144edb05 100644 --- a/static-check/src/static_check.rs +++ b/static-check/src/static_check.rs @@ -15,6 +15,7 @@ // along with the Leo library. If not, see . use crate::{StaticCheckError, SymbolTable}; +use leo_imports::ImportParser; use leo_typed::{Input, Program}; /// Performs a static type check over a program. @@ -35,14 +36,21 @@ impl StaticCheck { /// /// Returns a new `SymbolTable` from a given program. /// - pub fn run(program: &Program, input: &Input) -> Result { + pub fn run( + program: &Program, + input: &Input, + import_parser: &ImportParser, + ) -> Result { let mut check = Self::new(); // Load program input types. - check.load_input(input)?; + check.insert_input(input)?; + + // // Load the program imports into the symbol table. + // check.insert_imports()?; // Run pass one checks - check.pass_one(program)?; + check.pass_one(program, import_parser)?; // Run pass two checks check.pass_two(program)?; @@ -51,21 +59,29 @@ impl StaticCheck { } /// - /// Loads the program input types into the symbol table. + /// Inserts the program input types into the symbol table. /// - pub fn load_input(&mut self, input: &Input) -> Result<(), StaticCheckError> { + pub fn insert_input(&mut self, input: &Input) -> Result<(), StaticCheckError> { self.table - .load_input(input) + .insert_input(input) .map_err(|err| StaticCheckError::SymbolTableError(err)) } + // /// + // /// Inserts the program imports into the symbol table. + // /// + // pub fn insert_imports(&mut self, imports: &ImportParser) -> Result<(), StaticCheckError> {} + /// /// Checks for duplicate circuit and function names given an unresolved program. /// /// If a circuit or function name has no duplicates, then it is inserted into the symbol table. /// Variables defined later in the unresolved program cannot have the same name. /// - pub fn pass_one(&mut self, program: &Program) -> Result<(), StaticCheckError> { + pub fn pass_one(&mut self, program: &Program, import_parser: &ImportParser) -> Result<(), StaticCheckError> { + // Check unresolved program import names. + self.table.check_imports(&program.imports, import_parser)?; + // Check unresolved program circuit names. self.table.check_duplicate_circuits(&program.circuits)?; diff --git a/static-check/src/symbol_table.rs b/static-check/src/symbol_table.rs index 5cccf4eeb2..985339de95 100644 --- a/static-check/src/symbol_table.rs +++ b/static-check/src/symbol_table.rs @@ -15,9 +15,10 @@ // along with the Leo library. If not, see . use crate::{CircuitType, CircuitVariableType, FunctionType, ParameterType, SymbolTableError}; -use leo_typed::{Circuit, Function, Identifier, Input}; - +use leo_core::CorePackageList; use leo_imports::ImportParser; +use leo_typed::{Circuit, Function, Identifier, Import, Input, Package}; + use std::collections::HashMap; pub const INPUT_VARIABLE_NAME: &str = "input"; @@ -149,9 +150,13 @@ impl SymbolTable { } /// - /// Loads function input types into symbol table. + /// Inserts function input types into symbol table. /// - pub fn load_input(&mut self, input: &Input) -> Result<(), SymbolTableError> { + /// Creates a new `CircuitType` to represent the input values. + /// The type contains register, record, state, and state leaf circuit variables. + /// This allows easy access to input types using dot syntax: `input.register.r0`. + /// + pub fn insert_input(&mut self, input: &Input) -> Result<(), SymbolTableError> { // Get values for each input section. let registers_values = input.get_registers().values(); let record_values = input.get_record().values(); @@ -194,13 +199,77 @@ impl SymbolTable { /// /// No type resolution performed at this step. /// - pub fn insert_imports(&mut self, _imports: ImportParser) {} + // pub fn insert_imports(&mut self, imports: ImportParser) -> Result<(), SymbolTableError> { + // // Iterate over each imported program. + // + // // Store separate symbol table for each program. + // + // // + // } /// - /// Checks for duplicate circuit names given a hashmap of unresolved circuits. + /// Inserts core package name and type information into the symbol table. + /// + pub fn insert_core_package(&mut self, package: &Package) -> Result<(), SymbolTableError> { + // Create list of imported core packages. + let list = CorePackageList::from_package_access(package.access.to_owned())?; + + // Fetch core package symbols from `leo-core`. + let symbol_list = list.to_symbols()?; + + // Insert name and type information for each core package symbol. + for (name, circuit) in symbol_list.symbols() { + // Store name of symbol. + self.insert_name(name, ParameterType::from(circuit.clone())); + + // Create new circuit type for symbol. + let circuit_type = CircuitType::new(&self, circuit)?; + + // Insert circuit type of symbol. + self.insert_circuit(circuit_type.identifier.clone(), circuit_type); + } + + Ok(()) + } + + /// + /// Checks that all given imported names exist in the list of imported programs. + /// + /// Additionally checks for duplicate imported names in the given vector of imports. + /// Types defined later in the program cannot have the same name. + /// + pub fn check_imports( + &mut self, + imports: &Vec, + import_parser: &ImportParser, + ) -> Result<(), SymbolTableError> { + // Iterate over imported names. + for import in imports.iter() { + // Check if the import name exists as core package. + let core_package = import_parser.get_core_package(&import.package); + + // If the core package exists, then attempt to insert the import into the symbol table. + match core_package { + Some(package) => self.insert_core_package(package)?, + None => { + // Check if the import name exists in the import parser. + + // Attempt to insert the imported name into the symbol table. + + // Check that the imported name is unique. + unimplemented!("normal imports not supported yet") + } + } + } + + Ok(()) + } + + /// + /// Checks for duplicate circuit names given a hashmap of circuits. /// /// If a circuit name has no duplicates, then it is inserted into the symbol table. - /// Variables defined later in the unresolved program cannot have the same name. + /// Types defined later in the program cannot have the same name. /// pub fn check_duplicate_circuits( &mut self, @@ -224,10 +293,10 @@ impl SymbolTable { } /// - /// Checks for duplicate function names given a hashmap of unresolved functions. + /// Checks for duplicate function names given a hashmap of functions. /// /// If a function name has no duplicates, then it is inserted into the symbol table. - /// Variables defined later in the unresolved program cannot have the same name. + /// Types defined later in the program cannot have the same name. /// pub fn check_duplicate_functions( &mut self, @@ -251,10 +320,10 @@ impl SymbolTable { } /// - /// Checks for unknown types in a circuit given a hashmap of unresolved circuits. + /// Checks for unknown types in a circuit given a hashmap of circuits. /// /// If a circuit definition only contains known types, then it is inserted into the - /// symbol table. Variables defined later in the unresolved program can lookup the definition + /// symbol table. Variables defined later in the program can lookup the definition /// and refer to its expected types /// pub fn check_unknown_types_circuits( @@ -263,10 +332,10 @@ impl SymbolTable { ) -> Result<(), SymbolTableError> { // Iterate over circuit names and definitions. for (_, circuit) in circuits.iter() { - // Get the identifier of the unresolved circuit. + // Get the identifier of the circuit. let identifier = circuit.circuit_name.clone(); - // Resolve unknown types in the unresolved circuit definition. + // Resolve unknown types in the circuit definition. let circuit_type = CircuitType::new(self, circuit.clone())?; // Attempt to insert the circuit definition into the symbol table. @@ -277,10 +346,10 @@ impl SymbolTable { } /// - /// Checks for unknown types in a function given a hashmap of unresolved functions. + /// Checks for unknown types in a function given a hashmap of functions. /// /// If a function definition only contains known types, then it is inserted into the - /// symbol table. Variables defined later in the unresolved program can lookup the definition + /// symbol table. Variables defined later in the program can lookup the definition /// and refer to its expected types /// pub fn check_unknown_types_functions( @@ -289,10 +358,10 @@ impl SymbolTable { ) -> Result<(), SymbolTableError> { // Iterate over function names and definitions. for (_, function) in functions.iter() { - // Get the identifier of the unresolved function. + // Get the identifier of the function. let identifier = function.identifier.clone(); - // Resolve unknown types in the unresolved function definition. + // Resolve unknown types in the function definition. let function_type = FunctionType::new(&self, function.clone())?; // Attempt to insert the function definition into the symbol table.