From 14f5f448be4431f84439164143a33687a41567f3 Mon Sep 17 00:00:00 2001 From: collin Date: Thu, 2 Jul 2020 03:56:52 -0700 Subject: [PATCH] resolve all definitions prior to program execution --- compiler/src/compiler.rs | 6 +- .../constraints/definitions/definitions.rs | 50 ++++++++++ .../src/constraints/definitions/import.rs | 43 +++++++++ .../definitions/imported_symbols.rs | 39 ++++++++ compiler/src/constraints/definitions/mod.rs | 9 ++ .../src/constraints/definitions/symbol.rs | 47 ++++++++++ compiler/src/constraints/function.rs | 35 +------ .../src/constraints/generate_constraints.rs | 70 ++++++++++++++ compiler/src/constraints/mod.rs | 93 +++---------------- compiler/src/errors/constraints/import.rs | 6 +- compiler/src/imports/import_symbol.rs | 24 ++++- compiler/src/imports/imported_programs.rs | 10 +- types/src/imports/import_symbol.rs | 28 ++++++ 13 files changed, 331 insertions(+), 129 deletions(-) create mode 100644 compiler/src/constraints/definitions/definitions.rs create mode 100644 compiler/src/constraints/definitions/import.rs create mode 100644 compiler/src/constraints/definitions/imported_symbols.rs create mode 100644 compiler/src/constraints/definitions/mod.rs create mode 100644 compiler/src/constraints/definitions/symbol.rs create mode 100644 compiler/src/constraints/generate_constraints.rs diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index fb589eed7b..d0a938aef2 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -82,7 +82,7 @@ impl> Compiler { let path = self.main_file_path; let inputs = self.program_inputs.get_inputs(); - generate_constraints(cs, self.program, inputs, self.imported_programs).map_err(|mut error| { + generate_constraints(cs, self.program, inputs, &self.imported_programs).map_err(|mut error| { error.set_path(path); error @@ -90,7 +90,7 @@ impl> Compiler { } pub fn compile_test_constraints(self, cs: &mut TestConstraintSystem) -> Result<(), CompilerError> { - generate_test_constraints::(cs, self.program, self.imported_programs) + generate_test_constraints::(cs, self.program, &self.imported_programs) } fn load_program(&mut self) -> Result { @@ -151,7 +151,7 @@ impl> ConstraintSynthesizer for Compil cs, self.program, self.program_inputs.get_inputs(), - self.imported_programs, + &self.imported_programs, ) .map_err(|e| { log::error!("{}", e); diff --git a/compiler/src/constraints/definitions/definitions.rs b/compiler/src/constraints/definitions/definitions.rs new file mode 100644 index 0000000000..5e85392016 --- /dev/null +++ b/compiler/src/constraints/definitions/definitions.rs @@ -0,0 +1,50 @@ +use crate::{ + constraints::{new_scope, ConstrainedProgram, ConstrainedValue}, + errors::ImportError, + GroupType, + ImportedPrograms, +}; +use leo_types::Program; + +use snarkos_models::curves::{Field, PrimeField}; + +impl> ConstrainedProgram { + pub(crate) fn store_definitions( + &mut self, + program: Program, + imported_programs: &ImportedPrograms, + ) -> Result<(), ImportError> { + let program_name = program.name.clone(); + + // evaluate all import statements and store imported definitions + program + .imports + .iter() + .map(|import| self.store_import(program_name.clone(), import, imported_programs)) + .collect::, ImportError>>()?; + + self.store_all(program_name.clone(), &program); + + Ok(()) + } + + pub(crate) fn store_all(&mut self, scope: String, program: &Program) { + // evaluate and store all circuit definitions + program.circuits.iter().for_each(|(identifier, circuit)| { + let resolved_circuit_name = new_scope(scope.clone(), identifier.to_string()); + self.store( + resolved_circuit_name, + ConstrainedValue::CircuitDefinition(circuit.clone()), + ); + }); + + // evaluate and store all function definitions + program.functions.iter().for_each(|(function_name, function)| { + let resolved_function_name = new_scope(scope.clone(), function_name.to_string()); + self.store( + resolved_function_name, + ConstrainedValue::Function(None, function.clone()), + ); + }); + } +} diff --git a/compiler/src/constraints/definitions/import.rs b/compiler/src/constraints/definitions/import.rs new file mode 100644 index 0000000000..701b02f0c5 --- /dev/null +++ b/compiler/src/constraints/definitions/import.rs @@ -0,0 +1,43 @@ +use crate::{errors::ImportError, ConstrainedProgram, GroupType, ImportedPrograms, ImportedSymbols}; +use leo_types::Import; + +use snarkos_models::curves::{Field, PrimeField}; + +impl> ConstrainedProgram { + pub(crate) fn store_import( + &mut self, + scope: String, + import: &Import, + imported_programs: &ImportedPrograms, + ) -> Result<(), ImportError> { + println!("program name {}", scope); + println!("import {}", import); + + // get imported program name from import + // get imported symbols from from import + let imported_symbols = ImportedSymbols::from(import); + let program_name = imported_symbols.name.clone(); + println!("symbols {:?}", imported_symbols); + + // get imported program from hashmap + let program = imported_programs + .get(&program_name) + .ok_or(ImportError::unknown_package(import.package.name.clone()))?; + + // resolve imported program's import statements + program + .imports + .iter() + .map(|import| self.store_import(program_name.clone(), import, imported_programs)) + .collect::, ImportError>>()?; + + // store imported symbols in constrained program + imported_symbols + .symbols + .iter() + .map(|symbol| self.store_symbol(scope.clone(), symbol, program)) + .collect::, ImportError>>()?; + + Ok(()) + } +} diff --git a/compiler/src/constraints/definitions/imported_symbols.rs b/compiler/src/constraints/definitions/imported_symbols.rs new file mode 100644 index 0000000000..9d292091b8 --- /dev/null +++ b/compiler/src/constraints/definitions/imported_symbols.rs @@ -0,0 +1,39 @@ +use leo_types::{Import, ImportSymbol, Package, PackageAccess}; + +#[derive(Debug)] +pub(crate) struct ImportedSymbols { + pub name: String, + pub symbols: Vec, +} + +impl ImportedSymbols { + fn new(name: String) -> Self { + Self { name, symbols: vec![] } + } + + pub(crate) fn from(import: &Import) -> Self { + let mut symbols = Self::new(import.package.name.name.clone()); + + symbols.from_package_access(&import.package.access); + + symbols + } + + fn from_package(&mut self, package: &Package) { + self.name = package.name.name.clone(); + + self.from_package_access(&package.access); + } + + fn from_package_access(&mut self, access: &PackageAccess) { + match access { + PackageAccess::SubPackage(package) => self.from_package(package), + PackageAccess::Star(span) => { + let star = ImportSymbol::star(span); + self.symbols.push(star); + } + PackageAccess::Symbol(symbol) => self.symbols.push(symbol.clone()), + PackageAccess::Multiple(packages) => packages.iter().for_each(|access| self.from_package_access(access)), + } + } +} diff --git a/compiler/src/constraints/definitions/mod.rs b/compiler/src/constraints/definitions/mod.rs new file mode 100644 index 0000000000..08d5c11244 --- /dev/null +++ b/compiler/src/constraints/definitions/mod.rs @@ -0,0 +1,9 @@ +pub mod import; +pub use self::import::*; + +pub mod imported_symbols; +pub(crate) use self::imported_symbols::*; + +pub mod definitions; + +pub mod symbol; diff --git a/compiler/src/constraints/definitions/symbol.rs b/compiler/src/constraints/definitions/symbol.rs new file mode 100644 index 0000000000..55b98b0336 --- /dev/null +++ b/compiler/src/constraints/definitions/symbol.rs @@ -0,0 +1,47 @@ +use crate::{errors::ImportError, new_scope, ConstrainedProgram, ConstrainedValue, GroupType}; +use leo_types::{ImportSymbol, Program}; + +use snarkos_models::curves::{Field, PrimeField}; + +impl> ConstrainedProgram { + pub(crate) fn store_symbol( + &mut self, + scope: String, + symbol: &ImportSymbol, + program: &Program, + ) -> Result<(), ImportError> { + if symbol.is_star() { + self.store_all(scope, program); + } else { + let matched_circuit = program + .circuits + .iter() + .find(|(circuit_name, _circuit_def)| symbol.symbol == **circuit_name); + + let value = match matched_circuit { + Some((_circuit_name, circuit_def)) => ConstrainedValue::CircuitDefinition(circuit_def.clone()), + None => { + // see if the imported symbol is a function + let matched_function = program + .functions + .iter() + .find(|(function_name, _function)| symbol.symbol == **function_name); + + match matched_function { + Some((_function_name, function)) => ConstrainedValue::Function(None, function.clone()), + None => return Err(ImportError::unknown_symbol(symbol.to_owned(), scope)), + } + } + }; + + // take the alias if it is present + let name = symbol.alias.clone().unwrap_or(symbol.symbol.clone()); + let resolved_name = new_scope(scope, name.to_string()); + + // store imported circuit under resolved name + self.store(resolved_name, value); + } + + Ok(()) + } +} diff --git a/compiler/src/constraints/function.rs b/compiler/src/constraints/function.rs index 8c669eb8b5..5a5dee6b0d 100644 --- a/compiler/src/constraints/function.rs +++ b/compiler/src/constraints/function.rs @@ -4,15 +4,13 @@ use crate::{ bool_from_input, constraints::{new_scope, ConstrainedProgram, ConstrainedValue}, - errors::{FunctionError, ImportError}, + errors::{FunctionError, StatementError}, field_from_input, group_from_input, GroupType, - ImportedPrograms, }; -use leo_types::{Expression, Function, InputValue, Integer, Program, Span, Type}; +use leo_types::{Expression, Function, InputValue, Integer, Span, Type}; -use crate::errors::StatementError; use snarkos_models::{ curves::{Field, PrimeField}, gadgets::{ @@ -272,33 +270,4 @@ impl> ConstrainedProgram { self.enforce_function(cs, scope, function_name, function, input_variables) } - - pub(crate) fn resolve_definitions( - &mut self, - program: Program, - _imported_programs: ImportedPrograms, - ) -> Result<(), ImportError> { - let program_name = program.name.clone(); - - // evaluate all import statements and store imported definitions - // program - // .imports - // .into_iter() - // .map(|import| self.store_import(program_name.clone(), import)) - // .collect::, ImportError>>()?; - - // evaluate and store all circuit definitions - program.circuits.into_iter().for_each(|(identifier, circuit)| { - let resolved_circuit_name = new_scope(program_name.clone(), identifier.to_string()); - self.store(resolved_circuit_name, ConstrainedValue::CircuitDefinition(circuit)); - }); - - // evaluate and store all function definitions - program.functions.into_iter().for_each(|(function_name, function)| { - let resolved_function_name = new_scope(program_name.clone(), function_name.to_string()); - self.store(resolved_function_name, ConstrainedValue::Function(None, function)); - }); - - Ok(()) - } } diff --git a/compiler/src/constraints/generate_constraints.rs b/compiler/src/constraints/generate_constraints.rs new file mode 100644 index 0000000000..70984ef254 --- /dev/null +++ b/compiler/src/constraints/generate_constraints.rs @@ -0,0 +1,70 @@ +use crate::{errors::CompilerError, new_scope, ConstrainedProgram, ConstrainedValue, GroupType, ImportedPrograms}; +use leo_types::{InputValue, Program}; + +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::r1cs::{ConstraintSystem, TestConstraintSystem}, +}; + +pub fn generate_constraints, CS: ConstraintSystem>( + cs: &mut CS, + program: Program, + parameters: Vec>, + imported_programs: &ImportedPrograms, +) -> Result, CompilerError> { + let mut resolved_program = ConstrainedProgram::new(); + let program_name = program.get_name(); + let main_function_name = new_scope(program_name.clone(), "main".into()); + + resolved_program.store_definitions(program, imported_programs)?; + + let main = resolved_program + .get(&main_function_name) + .ok_or_else(|| CompilerError::NoMain)?; + + match main.clone() { + ConstrainedValue::Function(_circuit_identifier, function) => { + let result = resolved_program.enforce_main_function(cs, program_name, function, parameters)?; + Ok(result) + } + _ => Err(CompilerError::NoMainFunction), + } +} + +pub fn generate_test_constraints>( + cs: &mut TestConstraintSystem, + program: Program, + imported_programs: &ImportedPrograms, +) -> Result<(), CompilerError> { + let mut resolved_program = ConstrainedProgram::::new(); + let program_name = program.get_name(); + + let tests = program.tests.clone(); + + resolved_program.store_definitions(program, imported_programs)?; + + log::info!("Running {} tests", tests.len()); + + for (test_name, test_function) in tests.into_iter() { + let full_test_name = format!("{}::{}", program_name.clone(), test_name.to_string()); + + let result = resolved_program.enforce_main_function( + cs, + program_name.clone(), + test_function.0, + vec![], // test functions should not take any inputs + ); + + if result.is_ok() { + log::info!( + "test {} passed. Constraint system satisfied: {}", + full_test_name, + cs.is_satisfied() + ); + } else { + log::error!("test {} errored: {}", full_test_name, result.unwrap_err()); + } + } + + Ok(()) +} diff --git a/compiler/src/constraints/mod.rs b/compiler/src/constraints/mod.rs index c9900ebf37..7b2c617b38 100644 --- a/compiler/src/constraints/mod.rs +++ b/compiler/src/constraints/mod.rs @@ -1,96 +1,31 @@ //! Module containing methods to enforce constraints in an Leo program pub(crate) mod boolean; -pub(crate) use boolean::*; +pub(crate) use self::boolean::*; pub mod function; -pub use function::*; +pub use self::function::*; pub mod expression; -pub use expression::*; +pub use self::expression::*; pub(crate) mod field; -pub(crate) use field::*; +pub(crate) use self::field::*; + +pub mod generate_constraints; +pub use self::generate_constraints::*; pub(crate) mod group; -pub(crate) use group::*; +pub(crate) use self::group::*; + +pub(crate) mod definitions; +pub(crate) use self::definitions::*; pub mod program; -pub use program::*; +pub use self::program::*; pub mod value; -pub use value::*; +pub use self::value::*; pub mod statement; -pub use statement::*; - -use crate::{errors::CompilerError, GroupType, ImportedPrograms}; -use leo_types::{InputValue, Program}; - -use snarkos_models::{ - curves::{Field, PrimeField}, - gadgets::r1cs::{ConstraintSystem, TestConstraintSystem}, -}; - -pub fn generate_constraints, CS: ConstraintSystem>( - cs: &mut CS, - program: Program, - parameters: Vec>, - imported_programs: ImportedPrograms, -) -> Result, CompilerError> { - let mut resolved_program = ConstrainedProgram::new(); - let program_name = program.get_name(); - let main_function_name = new_scope(program_name.clone(), "main".into()); - - resolved_program.resolve_definitions(program, imported_programs)?; - - let main = resolved_program - .get(&main_function_name) - .ok_or_else(|| CompilerError::NoMain)?; - - match main.clone() { - ConstrainedValue::Function(_circuit_identifier, function) => { - let result = resolved_program.enforce_main_function(cs, program_name, function, parameters)?; - Ok(result) - } - _ => Err(CompilerError::NoMainFunction), - } -} - -pub fn generate_test_constraints>( - cs: &mut TestConstraintSystem, - program: Program, - imported_programs: ImportedPrograms, -) -> Result<(), CompilerError> { - let mut resolved_program = ConstrainedProgram::::new(); - let program_name = program.get_name(); - - let tests = program.tests.clone(); - - resolved_program.resolve_definitions(program, imported_programs)?; - - log::info!("Running {} tests", tests.len()); - - for (test_name, test_function) in tests.into_iter() { - let full_test_name = format!("{}::{}", program_name.clone(), test_name.to_string()); - - let result = resolved_program.enforce_main_function( - cs, - program_name.clone(), - test_function.0, - vec![], // test functions should not take any inputs - ); - - if result.is_ok() { - log::info!( - "test {} passed. Constraint system satisfied: {}", - full_test_name, - cs.is_satisfied() - ); - } else { - log::error!("test {} errored: {}", full_test_name, result.unwrap_err()); - } - } - - Ok(()) -} +pub use self::statement::*; diff --git a/compiler/src/errors/constraints/import.rs b/compiler/src/errors/constraints/import.rs index aabe8b2018..2da0f83197 100644 --- a/compiler/src/errors/constraints/import.rs +++ b/compiler/src/errors/constraints/import.rs @@ -75,11 +75,9 @@ impl ImportError { Self::new_from_span(message, identifier.span) } - pub fn unknown_symbol(symbol: ImportSymbol, file: String, file_path: &PathBuf) -> Self { + pub fn unknown_symbol(symbol: ImportSymbol, file: String) -> Self { let message = format!("cannot find imported symbol `{}` in imported file `{}`", symbol, file); - let mut error = FormattedError::new_from_span(message, symbol.span); - - error.path = Some(format!("{:?}", file_path)); + let error = FormattedError::new_from_span(message, symbol.span); ImportError::Error(error) } diff --git a/compiler/src/imports/import_symbol.rs b/compiler/src/imports/import_symbol.rs index 435d9a5750..a4ae9e6e9e 100644 --- a/compiler/src/imports/import_symbol.rs +++ b/compiler/src/imports/import_symbol.rs @@ -2,7 +2,7 @@ use crate::{errors::constraints::ImportError, ImportedPrograms}; use leo_ast::LeoParser; use leo_types::{ImportSymbol, Program, Span}; -use std::{ffi::OsString, fs::DirEntry}; +use std::{ffi::OsString, fs::DirEntry, path::PathBuf}; static LIBRARY_FILE: &str = "src/lib.leo"; static FILE_EXTENSION: &str = "leo"; @@ -54,7 +54,6 @@ impl ImportedPrograms { // import * can only be invoked on a package with a library file or a leo file if is_package || is_leo_file { // Generate aleo program from file - let name = format!("{:?}", entry.path()); let program = parse_import_file(entry, &span)?; // Store program's imports in imports hashmap @@ -65,7 +64,15 @@ impl ImportedPrograms { .collect::, ImportError>>()?; // Store program in imports hashmap - self.store(name, program); + let file_name_path = PathBuf::from(entry.file_name()); + let file_name = file_name_path + .file_stem() + .unwrap() + .to_os_string() + .into_string() + .unwrap(); // the file exists so these will not fail + + self.store(file_name, program); Ok(()) } else { @@ -76,7 +83,6 @@ impl ImportedPrograms { pub fn parse_import_symbol(&mut self, entry: &DirEntry, symbol: &ImportSymbol) -> Result<(), ImportError> { // Generate aleo program from file - let name = format!("{:?}", entry.path()); let program = parse_import_file(entry, &symbol.span)?; // Store program's imports in imports hashmap @@ -87,7 +93,15 @@ impl ImportedPrograms { .collect::, ImportError>>()?; // Store program in imports hashmap - self.store(name, program); + let file_name_path = PathBuf::from(entry.file_name()); + let file_name = file_name_path + .file_stem() + .unwrap() + .to_os_string() + .into_string() + .unwrap(); // the file exists so these will not fail + + self.store(file_name, program); Ok(()) } diff --git a/compiler/src/imports/imported_programs.rs b/compiler/src/imports/imported_programs.rs index bbfc068cf0..d6c78b79ca 100644 --- a/compiler/src/imports/imported_programs.rs +++ b/compiler/src/imports/imported_programs.rs @@ -15,14 +15,14 @@ impl ImportedPrograms { } } - pub(crate) fn store(&mut self, name: String, program: Program) { + pub(crate) fn store(&mut self, file_name: String, program: Program) { // todo: handle conflicting versions for duplicate imports here - println!("{}, {:?}", name, program); - let _res = self.imports.insert(name, program); + println!("storing: {},\n {:?}", file_name, program); + let _res = self.imports.insert(file_name, program); } - pub fn get(&self, name: &String) -> Option<&Program> { - self.imports.get(name) + pub fn get(&self, file_name: &String) -> Option<&Program> { + self.imports.get(file_name) } pub fn from_program(program: &Program) -> Result { diff --git a/types/src/imports/import_symbol.rs b/types/src/imports/import_symbol.rs index eaef622992..e8b57b3999 100644 --- a/types/src/imports/import_symbol.rs +++ b/types/src/imports/import_symbol.rs @@ -30,3 +30,31 @@ impl fmt::Display for ImportSymbol { } } } + +// todo: remove this +impl fmt::Debug for ImportSymbol { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.alias.is_some() { + write!(f, "{} as {}", self.symbol, self.alias.as_ref().unwrap()) + } else { + write!(f, "{}", self.symbol) + } + } +} + +impl ImportSymbol { + pub fn star(span: &Span) -> Self { + Self { + symbol: Identifier { + name: "*".to_string(), + span: span.clone(), + }, + alias: None, + span: span.clone(), + } + } + + pub fn is_star(&self) -> bool { + self.symbol.name.eq("*") + } +}