impl dynamic checks for core packages

This commit is contained in:
collin 2020-10-26 12:24:24 -07:00
parent 54bf6ca42d
commit 9933b8e336
8 changed files with 140 additions and 36 deletions

1
Cargo.lock generated
View File

@ -1415,6 +1415,7 @@ name = "leo-static-check"
version = "1.0.3"
dependencies = [
"leo-ast",
"leo-core",
"leo-imports",
"leo-typed",
"serde",

View File

@ -162,15 +162,14 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
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(())

View File

@ -27,13 +27,10 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
// 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(())

View File

@ -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.
///

View File

@ -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"

View File

@ -15,6 +15,7 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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),
}
}

View File

@ -15,6 +15,7 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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<SymbolTable, StaticCheckError> {
pub fn run(
program: &Program,
input: &Input,
import_parser: &ImportParser,
) -> Result<SymbolTable, StaticCheckError> {
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)?;

View File

@ -15,9 +15,10 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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>,
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.