diff --git a/zokrates_cli/src/bin.rs b/zokrates_cli/src/bin.rs index 06150c04..a3cb2ed6 100644 --- a/zokrates_cli/src/bin.rs +++ b/zokrates_cli/src/bin.rs @@ -13,7 +13,7 @@ use std::io::{stdin, BufReader, BufWriter, Read, Write}; use std::path::{Path, PathBuf}; use std::string::String; use zokrates_abi::Encode; -use zokrates_core::compile::{compile, CompilationArtifacts}; +use zokrates_core::compile::{compile, CompilationArtifacts, CompileError}; use zokrates_core::ir; use zokrates_core::proof_system::*; use zokrates_core::typed_absy::abi::Abi; @@ -271,14 +271,6 @@ fn cli() -> Result<(), String> { let path = PathBuf::from(sub_matches.value_of("input").unwrap()); - let location = path - .parent() - .unwrap() - .to_path_buf() - .into_os_string() - .into_string() - .unwrap(); - let light = sub_matches.occurrences_of("light") > 0; let bin_output_path = Path::new(sub_matches.value_of("output").unwrap()); @@ -287,17 +279,36 @@ fn cli() -> Result<(), String> { let hr_output_path = bin_output_path.to_path_buf().with_extension("ztf"); - let file = File::open(path.clone()).unwrap(); + let file = File::open(path.clone()) + .map_err(|why| format!("Couldn't open input file {}: {}", path.display(), why))?; let mut reader = BufReader::new(file); let mut source = String::new(); - reader - .read_to_string(&mut source) - .map_err(|why| format!("couldn't open input file {}: {}", path.display(), why))?; + reader.read_to_string(&mut source).unwrap(); + + let fmt_error = |e: &CompileError| { + format!( + "{}:{}", + e.file() + .canonicalize() + .unwrap() + .strip_prefix(std::env::current_dir().unwrap()) + .unwrap() + .display(), + e.value() + ) + }; let artifacts: CompilationArtifacts = - compile(source, location, Some(&fs_resolve)) - .map_err(|e| format!("Compilation failed:\n\n {}", e))?; + compile(source, path, Some(&fs_resolve)).map_err(|e| { + format!( + "Compilation failed:\n\n{}", + e.0.iter() + .map(|e| fmt_error(e)) + .collect::>() + .join("\n\n") + ) + })?; let program_flattened = artifacts.prog(); @@ -306,7 +317,7 @@ fn cli() -> Result<(), String> { // serialize flattened program and write to binary file let bin_output_file = File::create(&bin_output_path) - .map_err(|why| format!("couldn't create {}: {}", bin_output_path.display(), why))?; + .map_err(|why| format!("Couldn't create {}: {}", bin_output_path.display(), why))?; let mut writer = BufWriter::new(bin_output_file); @@ -315,7 +326,7 @@ fn cli() -> Result<(), String> { // serialize ABI spec and write to JSON file let abi_spec_file = File::create(&abi_spec_path) - .map_err(|why| format!("couldn't create {}: {}", abi_spec_path.display(), why))?; + .map_err(|why| format!("Couldn't create {}: {}", abi_spec_path.display(), why))?; let abi = artifacts.abi(); @@ -666,19 +677,12 @@ mod tests { let file = File::open(path.clone()).unwrap(); let mut reader = BufReader::new(file); - let location = path - .parent() - .unwrap() - .to_path_buf() - .into_os_string() - .into_string() - .unwrap(); let mut source = String::new(); reader.read_to_string(&mut source).unwrap(); let _: CompilationArtifacts = - compile(source, location, Some(&fs_resolve)).unwrap(); + compile(source, path, Some(&fs_resolve)).unwrap(); } } @@ -694,20 +698,12 @@ mod tests { let file = File::open(path.clone()).unwrap(); - let location = path - .parent() - .unwrap() - .to_path_buf() - .into_os_string() - .into_string() - .unwrap(); - let mut reader = BufReader::new(file); let mut source = String::new(); reader.read_to_string(&mut source).unwrap(); let artifacts: CompilationArtifacts = - compile(source, location, Some(&fs_resolve)).unwrap(); + compile(source, path, Some(&fs_resolve)).unwrap(); let _ = artifacts .prog() @@ -729,20 +725,12 @@ mod tests { let file = File::open(path.clone()).unwrap(); - let location = path - .parent() - .unwrap() - .to_path_buf() - .into_os_string() - .into_string() - .unwrap(); - let mut reader = BufReader::new(file); let mut source = String::new(); reader.read_to_string(&mut source).unwrap(); let artifacts: CompilationArtifacts = - compile(source, location, Some(&fs_resolve)).unwrap(); + compile(source, path, Some(&fs_resolve)).unwrap(); let _ = artifacts .prog() diff --git a/zokrates_core/src/absy/from_ast.rs b/zokrates_core/src/absy/from_ast.rs index 75bc949e..7325cc0c 100644 --- a/zokrates_core/src/absy/from_ast.rs +++ b/zokrates_core/src/absy/from_ast.rs @@ -25,13 +25,13 @@ impl<'ast> From> for absy::ImportNode<'ast> { match import { pest::ImportDirective::Main(import) => { - imports::Import::new(None, import.source.span.as_str()) + imports::Import::new(None, std::path::Path::new(import.source.span.as_str())) .alias(import.alias.map(|a| a.span.as_str())) .span(import.span) } pest::ImportDirective::From(import) => imports::Import::new( Some(import.symbol.span.as_str()), - import.source.span.as_str(), + std::path::Path::new(import.source.span.as_str()), ) .alias(import.alias.map(|a| a.span.as_str())) .span(import.span), diff --git a/zokrates_core/src/absy/mod.rs b/zokrates_core/src/absy/mod.rs index 3ea31136..f1384df7 100644 --- a/zokrates_core/src/absy/mod.rs +++ b/zokrates_core/src/absy/mod.rs @@ -16,6 +16,7 @@ pub use crate::absy::parameter::{Parameter, ParameterNode}; use crate::absy::types::{FunctionIdentifier, UnresolvedSignature, UnresolvedType, UserTypeId}; pub use crate::absy::variable::{Variable, VariableNode}; use embed::FlatEmbed; +use std::path::PathBuf; use crate::imports::ImportNode; use std::fmt; @@ -27,7 +28,7 @@ use std::collections::HashMap; pub type Identifier<'ast> = &'ast str; /// The identifier of a `Module`, typically a path or uri -pub type ModuleId = String; +pub type ModuleId = PathBuf; /// A collection of `Module`s pub type Modules<'ast, T> = HashMap>; @@ -160,7 +161,12 @@ impl<'ast> SymbolImport<'ast> { impl<'ast> fmt::Display for SymbolImport<'ast> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} from {}", self.symbol_id, self.module_id) + write!( + f, + "{} from {}", + self.symbol_id, + self.module_id.display().to_string() + ) } } diff --git a/zokrates_core/src/compile.rs b/zokrates_core/src/compile.rs index f01f3e80..d22863d3 100644 --- a/zokrates_core/src/compile.rs +++ b/zokrates_core/src/compile.rs @@ -13,6 +13,7 @@ use static_analysis::Analyse; use std::collections::HashMap; use std::fmt; use std::io; +use std::path::PathBuf; use typed_absy::abi::Abi; use typed_arena::Arena; use zokrates_field::field::Field; @@ -35,7 +36,7 @@ impl CompilationArtifacts { } #[derive(Debug)] -pub struct CompileErrors(Vec); +pub struct CompileErrors(pub Vec); impl From for CompileErrors { fn from(e: CompileError) -> CompileErrors { @@ -43,50 +44,46 @@ impl From for CompileErrors { } } -impl fmt::Display for CompileErrors { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}", - self.0 - .iter() - .map(|e| format!("{}", e)) - .collect::>() - .join("\n\n") - ) - } -} - #[derive(Debug)] pub enum CompileErrorInner { ParserError(pest::Error), ImportError(imports::Error), - SemanticError(semantics::Error), + SemanticError(semantics::ErrorInner), ReadError(io::Error), } impl CompileErrorInner { - pub fn with_context(self, context: &String) -> CompileError { + pub fn in_file(self, context: &PathBuf) -> CompileError { CompileError { value: self, - context: context.clone(), + file: context.clone(), } } } #[derive(Debug)] pub struct CompileError { - context: String, + file: PathBuf, value: CompileErrorInner, } +impl CompileError { + pub fn file(&self) -> &PathBuf { + &self.file + } + + pub fn value(&self) -> &CompileErrorInner { + &self.value + } +} + impl CompileErrors { - pub fn with_context(self, context: String) -> Self { + pub fn with_context(self, file: PathBuf) -> Self { CompileErrors( self.0 .into_iter() .map(|e| CompileError { - context: context.clone(), + file: file.clone(), ..e }) .collect(), @@ -94,12 +91,6 @@ impl CompileErrors { } } -impl fmt::Display for CompileError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}:{}", self.context, self.value) - } -} - impl From for CompileErrorInner { fn from(error: pest::Error) -> Self { CompileErrorInner::ParserError(error) @@ -118,29 +109,34 @@ impl From for CompileErrorInner { } } -impl From for CompileErrorInner { +impl From for CompileError { fn from(error: semantics::Error) -> Self { - CompileErrorInner::SemanticError(error) + CompileError { + value: CompileErrorInner::SemanticError(error.inner), + file: error.module_id, + } } } impl fmt::Display for CompileErrorInner { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let res = match *self { - CompileErrorInner::ParserError(ref e) => format!("{}", e), - CompileErrorInner::SemanticError(ref e) => format!("{}", e), - CompileErrorInner::ReadError(ref e) => format!("{}", e), - CompileErrorInner::ImportError(ref e) => format!("{}", e), - }; - write!(f, "{}", res) + match *self { + CompileErrorInner::ParserError(ref e) => write!(f, "{}", e), + CompileErrorInner::SemanticError(ref e) => write!(f, "{}", e), + CompileErrorInner::ReadError(ref e) => write!(f, "{}", e), + CompileErrorInner::ImportError(ref e) => write!(f, "{}", e), + } } } -pub type Resolve<'a, E> = &'a dyn Fn(String, String) -> Result<(String, String), E>; +// See zokrates_fs_resolver for the spec +pub type Resolve<'a, E> = &'a dyn Fn(PathBuf, PathBuf) -> Result<(String, PathBuf), E>; + +type FilePath = PathBuf; pub fn compile>( source: String, - location: String, + location: FilePath, resolve_option: Option>, ) -> Result, CompileErrors> { let arena = Arena::new(); @@ -150,12 +146,7 @@ pub fn compile>( // check semantics let typed_ast = Checker::check(compiled).map_err(|errors| { - CompileErrors( - errors - .into_iter() - .map(|e| CompileErrorInner::from(e).with_context(&location)) - .collect(), - ) + CompileErrors(errors.into_iter().map(|e| CompileError::from(e)).collect()) })?; let abi = typed_ast.abi(); @@ -183,7 +174,7 @@ pub fn compile>( pub fn compile_program<'ast, T: Field, E: Into>( source: &'ast str, - location: String, + location: FilePath, resolve_option: Option>, arena: &'ast Arena, ) -> Result, CompileErrors> { @@ -207,13 +198,13 @@ pub fn compile_program<'ast, T: Field, E: Into>( pub fn compile_module<'ast, T: Field, E: Into>( source: &'ast str, - location: String, + location: FilePath, resolve_option: Option>, modules: &mut HashMap>, arena: &'ast Arena, ) -> Result, CompileErrors> { let ast = pest::generate_ast(&source) - .map_err(|e| CompileErrors::from(CompileErrorInner::from(e).with_context(&location)))?; + .map_err(|e| CompileErrors::from(CompileErrorInner::from(e).in_file(&location)))?; let module_without_imports: Module = Module::from(ast); Importer::new().apply_imports( @@ -238,13 +229,10 @@ mod test { return foo() "# .to_string(); - let res: Result, CompileErrors> = compile( - source, - String::from("./path/to/file"), - None::>, - ); - assert!(res - .unwrap_err() + let res: Result, CompileErrors> = + compile(source, "./path/to/file".into(), None::>); + assert!(res.unwrap_err().0[0] + .value() .to_string() .contains(&"Can't resolve import without a resolver")); } @@ -256,11 +244,8 @@ mod test { return 1 "# .to_string(); - let res: Result, CompileErrors> = compile( - source, - String::from("./path/to/file"), - None::>, - ); + let res: Result, CompileErrors> = + compile(source, "./path/to/file".into(), None::>); assert!(res.is_ok()); } } diff --git a/zokrates_core/src/imports.rs b/zokrates_core/src/imports.rs index b5b3548c..3140ad3f 100644 --- a/zokrates_core/src/imports.rs +++ b/zokrates_core/src/imports.rs @@ -12,6 +12,7 @@ use crate::parser::Position; use std::collections::HashMap; use std::fmt; use std::io; +use std::path::{Path, PathBuf}; use typed_arena::Arena; use zokrates_field::field::Field; @@ -54,9 +55,11 @@ impl From for Error { } } +type ImportPath<'ast> = &'ast Path; + #[derive(PartialEq, Clone)] pub struct Import<'ast> { - source: Identifier<'ast>, + source: ImportPath<'ast>, symbol: Option>, alias: Option>, } @@ -64,7 +67,7 @@ pub struct Import<'ast> { pub type ImportNode<'ast> = Node>; impl<'ast> Import<'ast> { - pub fn new(symbol: Option>, source: Identifier<'ast>) -> Import<'ast> { + pub fn new(symbol: Option>, source: ImportPath<'ast>) -> Import<'ast> { Import { symbol, source, @@ -78,7 +81,7 @@ impl<'ast> Import<'ast> { pub fn new_with_alias( symbol: Option>, - source: Identifier<'ast>, + source: ImportPath<'ast>, alias: Identifier<'ast>, ) -> Import<'ast> { Import { @@ -93,7 +96,7 @@ impl<'ast> Import<'ast> { self } - pub fn get_source(&self) -> &Identifier<'ast> { + pub fn get_source(&self) -> &ImportPath<'ast> { &self.source } } @@ -101,8 +104,8 @@ impl<'ast> Import<'ast> { impl<'ast> fmt::Display for Import<'ast> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.alias { - Some(ref alias) => write!(f, "import {} as {}", self.source, alias), - None => write!(f, "import {}", self.source), + Some(ref alias) => write!(f, "import {} as {}", self.source.display(), alias), + None => write!(f, "import {}", self.source.display()), } } } @@ -110,8 +113,13 @@ impl<'ast> fmt::Display for Import<'ast> { impl<'ast> fmt::Debug for Import<'ast> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.alias { - Some(ref alias) => write!(f, "import(source: {}, alias: {})", self.source, alias), - None => write!(f, "import(source: {})", self.source), + Some(ref alias) => write!( + f, + "import(source: {}, alias: {})", + self.source.display(), + alias + ), + None => write!(f, "import(source: {})", self.source.display()), } } } @@ -126,7 +134,7 @@ impl Importer { pub fn apply_imports<'ast, T: Field, E: Into>( &self, destination: Module<'ast, T>, - location: String, + location: PathBuf, resolve_option: Option>, modules: &mut HashMap>, arena: &'ast Arena, @@ -139,7 +147,7 @@ impl Importer { let alias = import.alias; // handle the case of special bellman and packing imports if import.source.starts_with("EMBED") { - match import.source.as_ref() { + match import.source.to_str().unwrap() { "EMBED/sha256round" => { let alias = alias.unwrap_or("sha256round"); @@ -166,17 +174,15 @@ impl Importer { return Err(CompileErrorInner::ImportError( Error::new(format!("Embed {} not found. Options are \"EMBED/sha256round\", \"EMBED/unpack\"", s)).with_pos(Some(pos)), ) - .with_context(&location) + .in_file(&location) .into()); } } } else { // to resolve imports, we need a resolver match resolve_option { - Some(resolve) => match resolve(location.clone(), import.source.to_string()) { - Ok((source, location)) => { - let source = arena.alloc(source); - + Some(resolve) => match resolve(location.clone(), import.source.to_path_buf()) { + Ok((source, new_location)) => { // generate an alias from the imported path if none was given explicitely let alias = import.alias.unwrap_or( std::path::Path::new(import.source) @@ -184,19 +190,32 @@ impl Importer { .ok_or(CompileErrors::from( CompileErrorInner::ImportError(Error::new(format!( "Could not determine alias for import {}", - import.source + import.source.display() ))) - .with_context(&location), + .in_file(&location), ))? .to_str() .unwrap(), ); - let compiled = - compile_module(source, location, resolve_option, modules, &arena) - .map_err(|e| e.with_context(import.source.to_string()))?; + match modules.get(&new_location) { + Some(_) => {} + None => { + let source = arena.alloc(source); - modules.insert(import.source.to_string(), compiled); + let compiled = compile_module( + source, + new_location.clone(), + resolve_option, + modules, + &arena, + )?; + + assert!(modules + .insert(new_location.clone(), compiled) + .is_none()); + } + }; symbols.push( SymbolDeclaration { @@ -204,7 +223,7 @@ impl Importer { symbol: Symbol::There( SymbolImport::with_id_in_module( import.symbol.unwrap_or("main"), - import.source.clone(), + new_location.display().to_string(), ) .start_end(pos.0, pos.1), ), @@ -216,7 +235,7 @@ impl Importer { return Err(CompileErrorInner::ImportError( err.into().with_pos(Some(pos)), ) - .with_context(&location) + .in_file(&location) .into()); } }, @@ -224,7 +243,7 @@ impl Importer { return Err(CompileErrorInner::from(Error::new( "Can't resolve import without a resolver", )) - .with_context(&location) + .in_file(&location) .into()); } } @@ -249,10 +268,10 @@ mod tests { #[test] fn create_with_no_alias() { assert_eq!( - Import::new(None, "./foo/bar/baz.zok"), + Import::new(None, Path::new("./foo/bar/baz.zok")), Import { symbol: None, - source: "./foo/bar/baz.zok", + source: Path::new("./foo/bar/baz.zok"), alias: None, } ); @@ -261,10 +280,10 @@ mod tests { #[test] fn create_with_alias() { assert_eq!( - Import::new_with_alias(None, "./foo/bar/baz.zok", &"myalias"), + Import::new_with_alias(None, Path::new("./foo/bar/baz.zok"), &"myalias"), Import { symbol: None, - source: "./foo/bar/baz.zok", + source: Path::new("./foo/bar/baz.zok"), alias: Some("myalias"), } ); diff --git a/zokrates_core/src/semantics.rs b/zokrates_core/src/semantics.rs index 325efe23..6402b52e 100644 --- a/zokrates_core/src/semantics.rs +++ b/zokrates_core/src/semantics.rs @@ -10,6 +10,7 @@ use crate::typed_absy::*; use crate::typed_absy::{Parameter, Variable}; use std::collections::{hash_map::Entry, BTreeSet, HashMap, HashSet}; use std::fmt; +use std::path::PathBuf; use zokrates_field::field::Field; use crate::parser::Position; @@ -21,11 +22,26 @@ use std::hash::{Hash, Hasher}; use typed_absy::types::{ArrayType, StructMember}; #[derive(PartialEq, Debug)] -pub struct Error { +pub struct ErrorInner { pos: Option<(Position, Position)>, message: String, } +#[derive(PartialEq, Debug)] +pub struct Error { + pub inner: ErrorInner, + pub module_id: PathBuf, +} + +impl ErrorInner { + fn in_file(self, id: &ModuleId) -> Error { + Error { + inner: self, + module_id: id.clone(), + } + } +} + type TypeMap = HashMap>; /// The global state of the program during semantic checks @@ -97,7 +113,7 @@ impl<'ast, T: Field> State<'ast, T> { } } -impl fmt::Display for Error { +impl fmt::Display for ErrorInner { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let location = self .pos @@ -237,8 +253,16 @@ impl<'ast> Checker<'ast> { return Err(errors); } - Checker::check_single_main(state.typed_modules.get(&program.main).unwrap()) - .map_err(|e| vec![e])?; + let main_id = program.main.clone(); + + Checker::check_single_main(state.typed_modules.get(&program.main).unwrap()).map_err( + |inner| { + vec![Error { + inner, + module_id: main_id, + }] + }, + )?; Ok(TypedProgram { main: program.main, @@ -251,7 +275,7 @@ impl<'ast> Checker<'ast> { s: StructTypeNode<'ast>, module_id: &ModuleId, types: &TypeMap, - ) -> Result> { + ) -> Result> { let pos = s.pos(); let s = s.value; @@ -267,7 +291,7 @@ impl<'ast> Checker<'ast> { { Ok(f) => match fields_set.insert(f.0.clone()) { true => fields.push(f), - false => errors.push(Error { + false => errors.push(ErrorInner { pos: Some(pos), message: format!("Duplicate key {} in struct definition", f.0,), }), @@ -298,7 +322,7 @@ impl<'ast> Checker<'ast> { functions: &mut HashMap, TypedFunctionSymbol<'ast, T>>, symbol_unifier: &mut SymbolUnifier, ) -> Result<(), Vec> { - let mut errors = vec![]; + let mut errors: Vec = vec![]; let pos = declaration.pos(); let declaration = declaration.value; @@ -308,13 +332,16 @@ impl<'ast> Checker<'ast> { match self.check_struct_type_declaration(t.clone(), module_id, &state.types) { Ok(ty) => { match symbol_unifier.insert_type(declaration.id) { - false => errors.push(Error { - pos: Some(pos), - message: format!( - "{} conflicts with another symbol", - declaration.id, - ), - }), + false => errors.push( + ErrorInner { + pos: Some(pos), + message: format!( + "{} conflicts with another symbol", + declaration.id, + ), + } + .in_file(module_id), + ), true => {} }; state @@ -323,16 +350,25 @@ impl<'ast> Checker<'ast> { .or_default() .insert(declaration.id.to_string(), ty); } - Err(e) => errors.extend(e), + Err(e) => errors.extend(e.into_iter().map(|inner| Error { + inner, + module_id: module_id.clone(), + })), } } Symbol::HereFunction(f) => match self.check_function(f, module_id, &state.types) { Ok(funct) => { match symbol_unifier.insert_function(declaration.id, funct.signature.clone()) { - false => errors.push(Error { - pos: Some(pos), - message: format!("{} conflicts with another symbol", declaration.id,), - }), + false => errors.push( + ErrorInner { + pos: Some(pos), + message: format!( + "{} conflicts with another symbol", + declaration.id, + ), + } + .in_file(module_id), + ), true => {} }; @@ -347,7 +383,7 @@ impl<'ast> Checker<'ast> { ); } Err(e) => { - errors.extend(e); + errors.extend(e.into_iter().map(|inner| inner.in_file(module_id))); } }, Symbol::There(import) => { @@ -384,12 +420,14 @@ impl<'ast> Checker<'ast> { match symbol_unifier.insert_type(declaration.id) { false => { errors.push(Error { + module_id: module_id.clone(), + inner: ErrorInner { pos: Some(pos), message: format!( "{} conflicts with another symbol", declaration.id, ), - }); + }}); } true => {} }; @@ -400,13 +438,13 @@ impl<'ast> Checker<'ast> { .insert(import.symbol_id.to_string(), t.clone()); } (0, None) => { - errors.push(Error { + errors.push(ErrorInner { pos: Some(pos), message: format!( "Could not find symbol {} in module {}", - import.symbol_id, import.module_id, + import.symbol_id, import.module_id.display(), ), - }); + }.in_file(module_id)); } (_, Some(_)) => unreachable!("collision in module we're importing from should have been caught when checking it"), _ => { @@ -414,13 +452,13 @@ impl<'ast> Checker<'ast> { match symbol_unifier.insert_function(declaration.id, candidate.signature.clone()) { false => { - errors.push(Error { + errors.push(ErrorInner { pos: Some(pos), message: format!( "{} conflicts with another symbol", declaration.id, ), - }); + }.in_file(module_id)); }, true => {} }; @@ -445,10 +483,16 @@ impl<'ast> Checker<'ast> { Symbol::Flat(funct) => { match symbol_unifier.insert_function(declaration.id, funct.signature::()) { false => { - errors.push(Error { - pos: Some(pos), - message: format!("{} conflicts with another symbol", declaration.id,), - }); + errors.push( + ErrorInner { + pos: Some(pos), + message: format!( + "{} conflicts with another symbol", + declaration.id, + ), + } + .in_file(module_id), + ); } true => {} }; @@ -537,7 +581,7 @@ impl<'ast> Checker<'ast> { Ok(()) } - fn check_single_main(module: &TypedModule) -> Result<(), Error> { + fn check_single_main(module: &TypedModule) -> Result<(), ErrorInner> { match module .functions .iter() @@ -545,21 +589,21 @@ impl<'ast> Checker<'ast> { .count() { 1 => Ok(()), - 0 => Err(Error { + 0 => Err(ErrorInner { pos: None, message: format!("No main function found"), }), - n => Err(Error { + n => Err(ErrorInner { pos: None, message: format!("Only one main function allowed, found {}", n), }), } } - fn check_for_var(&self, var: &VariableNode) -> Result<(), Error> { + fn check_for_var(&self, var: &VariableNode) -> Result<(), ErrorInner> { match var.value.get_type() { UnresolvedType::FieldElement => Ok(()), - t => Err(Error { + t => Err(ErrorInner { pos: Some(var.pos()), message: format!("Variable in for loop cannot have type {}", t), }), @@ -571,7 +615,7 @@ impl<'ast> Checker<'ast> { funct_node: FunctionNode<'ast, T>, module_id: &ModuleId, types: &TypeMap, - ) -> Result, Vec> { + ) -> Result, Vec> { self.enter_scope(); let mut errors = vec![]; @@ -606,7 +650,7 @@ impl<'ast> Checker<'ast> { == s.outputs { true => {} - false => errors.push(Error { + false => errors.push(ErrorInner { pos: Some(pos), message: format!( "Expected ({}) in return statement, found ({})", @@ -658,7 +702,7 @@ impl<'ast> Checker<'ast> { p: ParameterNode<'ast>, module_id: &ModuleId, types: &TypeMap, - ) -> Result, Vec> { + ) -> Result, Vec> { let var = self.check_variable(p.value.id, module_id, types)?; Ok(Parameter { @@ -672,7 +716,7 @@ impl<'ast> Checker<'ast> { signature: UnresolvedSignature, module_id: &ModuleId, types: &TypeMap, - ) -> Result> { + ) -> Result> { let mut errors = vec![]; let mut inputs = vec![]; let mut outputs = vec![]; @@ -711,7 +755,7 @@ impl<'ast> Checker<'ast> { ty: UnresolvedTypeNode, module_id: &ModuleId, types: &TypeMap, - ) -> Result { + ) -> Result { let pos = ty.pos(); let ty = ty.value; @@ -728,7 +772,7 @@ impl<'ast> Checker<'ast> { .unwrap() .get(&id) .cloned() - .ok_or_else(|| Error { + .ok_or_else(|| ErrorInner { pos: Some(pos), message: format!("Undefined type {}", id), }) @@ -741,7 +785,7 @@ impl<'ast> Checker<'ast> { v: crate::absy::VariableNode<'ast>, module_id: &ModuleId, types: &TypeMap, - ) -> Result, Vec> { + ) -> Result, Vec> { Ok(Variable::with_id_and_type( v.value.id.into(), self.check_type(v.value._type, module_id, types) @@ -754,7 +798,7 @@ impl<'ast> Checker<'ast> { stat: StatementNode<'ast, T>, module_id: &ModuleId, types: &TypeMap, - ) -> Result, Vec> { + ) -> Result, Vec> { let pos = stat.pos(); match stat.value { @@ -773,7 +817,7 @@ impl<'ast> Checker<'ast> { let var = self.check_variable(var, module_id, types)?; match self.insert_into_scope(var.clone()) { true => Ok(TypedStatement::Declaration(var)), - false => Err(Error { + false => Err(ErrorInner { pos: Some(pos), message: format!("Duplicate declaration for variable named {}", var.id), }), @@ -804,7 +848,7 @@ impl<'ast> Checker<'ast> { // make sure the assignee has the same type as the rhs match var_type == expression_type { true => Ok(TypedStatement::Definition(var, checked_expr)), - false => Err(Error { + false => Err(ErrorInner { pos: Some(pos), message: format!( "Expression {} of type {} cannot be assigned to {} of type {}", @@ -825,7 +869,7 @@ impl<'ast> Checker<'ast> { if checked_lhs.get_type() == checked_rhs.get_type() { Ok(TypedStatement::Condition(checked_lhs, checked_rhs)) } else { - Err(Error { + Err(ErrorInner { pos: Some(pos), message: format!( "Cannot compare {} of type {:?} to {} of type {:?}", @@ -854,7 +898,7 @@ impl<'ast> Checker<'ast> { let from = match from { TypedExpression::FieldElement(e) => Ok(e), - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!( "Expected lower loop bound to be of type field, found {}", @@ -866,7 +910,7 @@ impl<'ast> Checker<'ast> { let to = match to { TypedExpression::FieldElement(e) => Ok(e), - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!( "Expected higher loop bound to be of type field, found {}", @@ -903,7 +947,7 @@ impl<'ast> Checker<'ast> { Some(sv) => Some(sv.id.get_type()) })) } - ref a => Err(Error { + ref a => Err(ErrorInner { pos: Some(pos), message: format!("Left hand side of function return assignment must be a list of identifiers, found {}", a)}) }.map_err(|e| vec![e])?; @@ -943,13 +987,13 @@ impl<'ast> Checker<'ast> { Ok(TypedStatement::MultipleDefinition(assignees, call)) }, - 0 => Err(Error { pos: Some(pos), + 0 => Err(ErrorInner { pos: Some(pos), message: format!("Function definition for function {} with signature {} not found.", fun_id, query) }), - _ => Err(Error { pos: Some(pos), + _ => Err(ErrorInner { pos: Some(pos), message: format!("Function call for function {} with arguments {:?} is ambiguous.", fun_id, arguments_types) }), } } - _ => Err(Error { + _ => Err(ErrorInner { pos: Some(pos), message: format!("{} should be a FunctionCall", rhs), }), @@ -963,7 +1007,7 @@ impl<'ast> Checker<'ast> { assignee: AssigneeNode<'ast, T>, module_id: &ModuleId, types: &TypeMap, - ) -> Result, Error> { + ) -> Result, ErrorInner> { let pos = assignee.pos(); // check that the assignee is declared match assignee.value { @@ -972,7 +1016,7 @@ impl<'ast> Checker<'ast> { variable_name.into(), var.id._type.clone(), ))), - None => Err(Error { + None => Err(ErrorInner { pos: Some(assignee.pos()), message: format!("Undeclared variable: {:?}", variable_name), }), @@ -995,7 +1039,7 @@ impl<'ast> Checker<'ast> { let checked_typed_index = match checked_index { TypedExpression::FieldElement(e) => Ok(e), - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1011,7 +1055,7 @@ impl<'ast> Checker<'ast> { box checked_typed_index, )) } - ty => Err(Error { + ty => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1028,12 +1072,12 @@ impl<'ast> Checker<'ast> { match &ty { Type::Struct(members) => match members.iter().find(|m| m.id == member) { Some(_) => Ok(TypedAssignee::Member(box checked_assignee, member.into())), - None => Err(Error { + None => Err(ErrorInner { pos: Some(pos), message: format!("{} doesn't have member {}", ty, member), }), }, - ty => Err(Error { + ty => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1051,7 +1095,7 @@ impl<'ast> Checker<'ast> { spread_or_expression: SpreadOrExpression<'ast, T>, module_id: &ModuleId, types: &TypeMap, - ) -> Result>, Error> { + ) -> Result>, ErrorInner> { match spread_or_expression { SpreadOrExpression::Spread(s) => { let pos = s.pos(); @@ -1097,7 +1141,7 @@ impl<'ast> Checker<'ast> { .collect()), } } - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1118,7 +1162,7 @@ impl<'ast> Checker<'ast> { expr: ExpressionNode<'ast, T>, module_id: &ModuleId, types: &TypeMap, - ) -> Result, Error> { + ) -> Result, ErrorInner> { let pos = expr.pos(); match expr.value { @@ -1140,7 +1184,7 @@ impl<'ast> Checker<'ast> { .annotate(members) .into()), }, - None => Err(Error { + None => Err(ErrorInner { pos: Some(pos), message: format!("Identifier \"{}\" is undefined", name), }), @@ -1154,7 +1198,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(FieldElementExpression::Add(box e1, box e2).into()) } - (t1, t2) => Err(Error { + (t1, t2) => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1173,7 +1217,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(FieldElementExpression::Sub(box e1, box e2).into()) } - (t1, t2) => Err(Error { + (t1, t2) => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1192,7 +1236,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(FieldElementExpression::Mult(box e1, box e2).into()) } - (t1, t2) => Err(Error { + (t1, t2) => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1211,7 +1255,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(FieldElementExpression::Div(box e1, box e2).into()) } - (t1, t2) => Err(Error { + (t1, t2) => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1230,7 +1274,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => Ok( TypedExpression::FieldElement(FieldElementExpression::Pow(box e1, box e2)), ), - (t1, t2) => Err(Error { + (t1, t2) => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1273,13 +1317,13 @@ impl<'ast> Checker<'ast> { }, _ => unreachable!("types should match here as we checked them explicitly") } - false => Err(Error { + false => Err(ErrorInner { pos: Some(pos), message: format!("{{consequence}} and {{alternative}} in `if/else` expression should have the same type, found {}, {}", consequence_type, alternative_type) }) } } - c => Err(Error { + c => Err(ErrorInner { pos: Some(pos), message: format!( "{{condition}} after `if` should be a boolean, found {}", @@ -1350,7 +1394,7 @@ impl<'ast> Checker<'ast> { .annotate(*array_type.ty.clone(), array_type.size.clone()) .into()), }, - n => Err(Error { + n => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1360,7 +1404,7 @@ impl<'ast> Checker<'ast> { }), } } - 0 => Err(Error { + 0 => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1380,7 +1424,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(BooleanExpression::Lt(box e1, box e2).into()) } - (e1, e2) => Err(Error { + (e1, e2) => Err(ErrorInner { pos: Some(pos), message: format!( "Cannot compare {} of type {} to {} of type {}", @@ -1399,7 +1443,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(BooleanExpression::Le(box e1, box e2).into()) } - (e1, e2) => Err(Error { + (e1, e2) => Err(ErrorInner { pos: Some(pos), message: format!( "Cannot compare {} of type {} to {} of type {}", @@ -1421,7 +1465,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::Boolean(e1), TypedExpression::Boolean(e2)) => { Ok(BooleanExpression::BoolEq(box e1, box e2).into()) } - (e1, e2) => Err(Error { + (e1, e2) => Err(ErrorInner { pos: Some(pos), message: format!( "Cannot compare {} of type {} to {} of type {}", @@ -1440,7 +1484,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(BooleanExpression::Ge(box e1, box e2).into()) } - (e1, e2) => Err(Error { + (e1, e2) => Err(ErrorInner { pos: Some(pos), message: format!( "Cannot compare {} of type {} to {} of type {}", @@ -1459,7 +1503,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(BooleanExpression::Gt(box e1, box e2).into()) } - (e1, e2) => Err(Error { + (e1, e2) => Err(ErrorInner { pos: Some(pos), message: format!( "Cannot compare {} of type {} to {} of type {}", @@ -1493,21 +1537,21 @@ impl<'ast> Checker<'ast> { .unwrap_or(array_size); match (from, to, array_size) { - (f, _, s) if f > s => Err(Error { + (f, _, s) if f > s => Err(ErrorInner { pos: Some(pos), message: format!( "Lower range bound {} is out of array bounds [0, {}]", f, s, ), }), - (_, t, s) if t > s => Err(Error { + (_, t, s) if t > s => Err(ErrorInner { pos: Some(pos), message: format!( "Higher range bound {} is out of array bounds [0, {}]", t, s, ), }), - (f, t, _) if f > t => Err(Error { + (f, t, _) if f > t => Err(ErrorInner { pos: Some(pos), message: format!( "Lower range bound {} is larger than higher range bound {}", @@ -1548,7 +1592,7 @@ impl<'ast> Checker<'ast> { .into()), } } - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!( "Cannot access slice of expression {} of type {}", @@ -1582,7 +1626,7 @@ impl<'ast> Checker<'ast> { } } } - (a, e) => Err(Error { + (a, e) => Err(ErrorInner { pos: Some(pos), message: format!( "Cannot access element {} on expression of type {}", @@ -1621,13 +1665,13 @@ impl<'ast> Checker<'ast> { .into()) } }, - None => Err(Error { + None => Err(ErrorInner { pos: Some(pos), message: format!("{} doesn't have member {}", s.get_type(), id,), }), } } - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!( "Cannot access member {} on expression of type {}", @@ -1656,7 +1700,7 @@ impl<'ast> Checker<'ast> { for e in expressions_checked { let unwrapped_e = match e { TypedExpression::FieldElement(e) => Ok(e), - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1683,7 +1727,7 @@ impl<'ast> Checker<'ast> { for e in expressions_checked { let unwrapped_e = match e { TypedExpression::Boolean(e) => Ok(e), - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1713,7 +1757,7 @@ impl<'ast> Checker<'ast> { if e.get_type() == ty { Ok(e) } else { - Err(Error { + Err(ErrorInner { pos: Some(pos), message: format!( @@ -1725,7 +1769,7 @@ impl<'ast> Checker<'ast> { }) } } - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1755,7 +1799,7 @@ impl<'ast> Checker<'ast> { if e.get_type() == ty { Ok(e) } else { - Err(Error { + Err(ErrorInner { pos: Some(pos), message: format!( @@ -1767,7 +1811,7 @@ impl<'ast> Checker<'ast> { }) } } - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1803,7 +1847,7 @@ impl<'ast> Checker<'ast> { // check that we provided the required number of values if members.len() != inline_members.len() { - return Err(Error { + return Err(ErrorInner { pos: Some(pos), message: format!( "Inline struct {} does not match {} : {}", @@ -1831,7 +1875,7 @@ impl<'ast> Checker<'ast> { self.check_expression(value, module_id, &types)?; let checked_type = expression_checked.get_type(); if checked_type != *member.ty { - return Err(Error { + return Err(ErrorInner { pos: Some(pos), message: format!( "Member {} of struct {} has type {}, found {} of type {}", @@ -1847,7 +1891,7 @@ impl<'ast> Checker<'ast> { } } None => { - return Err(Error { + return Err(ErrorInner { pos: Some(pos), message: format!( "Member {} of struct {} : {} not found in value {}", @@ -1872,7 +1916,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::Boolean(e1), TypedExpression::Boolean(e2)) => { Ok(BooleanExpression::And(box e1, box e2).into()) } - (e1, e2) => Err(Error { + (e1, e2) => Err(ErrorInner { pos: Some(pos), message: format!( @@ -1890,7 +1934,7 @@ impl<'ast> Checker<'ast> { (TypedExpression::Boolean(e1), TypedExpression::Boolean(e2)) => { Ok(BooleanExpression::Or(box e1, box e2).into()) } - (e1, e2) => Err(Error { + (e1, e2) => Err(ErrorInner { pos: Some(pos), message: format!("cannot compare {} to {}", e1.get_type(), e2.get_type()), @@ -1901,7 +1945,7 @@ impl<'ast> Checker<'ast> { let e_checked = self.check_expression(e, module_id, &types)?; match e_checked { TypedExpression::Boolean(e) => Ok(BooleanExpression::Not(box e).into()), - e => Err(Error { + e => Err(ErrorInner { pos: Some(pos), message: format!("cannot negate {}", e.get_type()), @@ -1959,7 +2003,7 @@ mod tests { #[test] fn element_type_mismatch() { let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); // [3, true] let a = Expression::InlineArray(vec![ Expression::FieldConstant(FieldPrime::from(3)).mock().into(), @@ -2121,25 +2165,22 @@ mod tests { }; let mut state = State::new( - vec![(String::from("foo"), foo), (String::from("bar"), bar)] + vec![("foo".into(), foo), ("bar".into(), bar)] .into_iter() .collect(), ); let mut checker = Checker::new(); + assert_eq!(checker.check_module(&"bar".into(), &mut state), Ok(())); assert_eq!( - checker.check_module(&String::from("bar"), &mut state), - Ok(()) - ); - assert_eq!( - state.typed_modules.get(&String::from("bar")), + state.typed_modules.get(&PathBuf::from("bar")), Some(&TypedModule { functions: vec![( FunctionKey::with_id("main").signature(Signature::new()), TypedFunctionSymbol::There( FunctionKey::with_id("main").signature(Signature::new()), - "foo".to_string() + "foo".into() ) )] .into_iter() @@ -2173,13 +2214,18 @@ mod tests { imports: vec![], }; - let mut state = State::new(vec![(MODULE_ID.to_string(), module)].into_iter().collect()); + let mut state = State::new( + vec![(PathBuf::from(MODULE_ID).into(), module)] + .into_iter() + .collect(), + ); let mut checker = Checker::new(); assert_eq!( checker - .check_module(&MODULE_ID.to_string(), &mut state) + .check_module(&PathBuf::from(MODULE_ID).into(), &mut state) .unwrap_err()[0] + .inner .message, "foo conflicts with another symbol" ); @@ -2210,22 +2256,26 @@ mod tests { imports: vec![], }; - let mut state = State::new(vec![(MODULE_ID.to_string(), module)].into_iter().collect()); + let mut state = State::new( + vec![(PathBuf::from(MODULE_ID), module)] + .into_iter() + .collect(), + ); let mut checker = Checker::new(); assert_eq!( - checker.check_module(&MODULE_ID.to_string(), &mut state), + checker.check_module(&PathBuf::from(MODULE_ID), &mut state), Ok(()) ); assert!(state .typed_modules - .get(&MODULE_ID.to_string()) + .get(&PathBuf::from(MODULE_ID)) .unwrap() .functions .contains_key(&FunctionKey::with_id("foo").signature(Signature::new()))); assert!(state .typed_modules - .get(&MODULE_ID.to_string()) + .get(&PathBuf::from(MODULE_ID)) .unwrap() .functions .contains_key( @@ -2257,13 +2307,14 @@ mod tests { imports: vec![], }; - let mut state = State::new(vec![(String::from("main"), module)].into_iter().collect()); + let mut state = State::new(vec![("main".into(), module)].into_iter().collect()); let mut checker = Checker::new(); assert_eq!( checker - .check_module(&String::from("main"), &mut state) + .check_module(&"main".into(), &mut state) .unwrap_err()[0] + .inner .message, "foo conflicts with another symbol" ); @@ -2293,13 +2344,14 @@ mod tests { imports: vec![], }; - let mut state = State::new(vec![(String::from("main"), module)].into_iter().collect()); + let mut state = State::new(vec![("main".into(), module)].into_iter().collect()); let mut checker = Checker::new(); assert_eq!( checker - .check_module(&String::from("main"), &mut state) + .check_module(&"main".into(), &mut state) .unwrap_err()[0] + .inner .message, "foo conflicts with another symbol" ); @@ -2329,7 +2381,7 @@ mod tests { SymbolDeclaration { id: "foo", symbol: Symbol::There( - SymbolImport::with_id_in_module("main", "bar".to_string()).mock(), + SymbolImport::with_id_in_module("main", "bar").mock(), ), } .mock(), @@ -2343,7 +2395,7 @@ mod tests { }; let mut state = State::new( - vec![(MODULE_ID.to_string(), main), ("bar".to_string(), bar)] + vec![(PathBuf::from(MODULE_ID), main), ("bar".into(), bar)] .into_iter() .collect(), ); @@ -2351,8 +2403,9 @@ mod tests { let mut checker = Checker::new(); assert_eq!( checker - .check_module(&MODULE_ID.to_string(), &mut state) + .check_module(&PathBuf::from(MODULE_ID), &mut state) .unwrap_err()[0] + .inner .message, "foo conflicts with another symbol" ); @@ -2384,7 +2437,7 @@ mod tests { SymbolDeclaration { id: "foo", symbol: Symbol::There( - SymbolImport::with_id_in_module("main", "bar".to_string()).mock(), + SymbolImport::with_id_in_module("main", "bar").mock(), ), } .mock(), @@ -2393,7 +2446,7 @@ mod tests { }; let mut state = State::new( - vec![(MODULE_ID.to_string(), main), ("bar".to_string(), bar)] + vec![(PathBuf::from(MODULE_ID), main), ("bar".into(), bar)] .into_iter() .collect(), ); @@ -2401,8 +2454,9 @@ mod tests { let mut checker = Checker::new(); assert_eq!( checker - .check_module(&MODULE_ID.to_string(), &mut state) + .check_module(&PathBuf::from(MODULE_ID), &mut state) .unwrap_err()[0] + .inner .message, "foo conflicts with another symbol" ); @@ -2432,14 +2486,14 @@ mod tests { .mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = Checker::new(); assert_eq!( checker.check_statement(statement, &module_id, &types), - Err(vec![Error { + Err(vec![ErrorInner { pos: Some((Position::mock(), Position::mock())), - message: "Identifier \"b\" is undefined".to_string() + message: "Identifier \"b\" is undefined".into() }]) ); } @@ -2455,7 +2509,7 @@ mod tests { .mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut scope = HashSet::new(); scope.insert(ScopedVariable { @@ -2541,14 +2595,17 @@ mod tests { imports: vec![], }; - let mut state = State::new(vec![(String::from("main"), module)].into_iter().collect()); + let mut state = State::new(vec![("main".into(), module)].into_iter().collect()); let mut checker = Checker::new(); assert_eq!( - checker.check_module(&String::from("main"), &mut state), + checker.check_module(&"main".into(), &mut state), Err(vec![Error { - pos: Some((Position::mock(), Position::mock())), - message: "Identifier \"a\" is undefined".to_string() + inner: ErrorInner { + pos: Some((Position::mock(), Position::mock())), + message: "Identifier \"a\" is undefined".into() + }, + module_id: "main".into() }]) ); } @@ -2656,12 +2713,10 @@ mod tests { imports: vec![], }; - let mut state = State::new(vec![(String::from("main"), module)].into_iter().collect()); + let mut state = State::new(vec![("main".into(), module)].into_iter().collect()); let mut checker = Checker::new(); - assert!(checker - .check_module(&String::from("main"), &mut state) - .is_ok()); + assert!(checker.check_module(&"main".into(), &mut state).is_ok()); } #[test] @@ -2698,14 +2753,14 @@ mod tests { .mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = Checker::new(); assert_eq!( checker.check_function(foo, &module_id, &types), - Err(vec![Error { + Err(vec![ErrorInner { pos: Some((Position::mock(), Position::mock())), - message: "Identifier \"i\" is undefined".to_string() + message: "Identifier \"i\" is undefined".into() }]) ); } @@ -2773,7 +2828,7 @@ mod tests { }; let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = Checker::new(); assert_eq!( @@ -2822,16 +2877,16 @@ mod tests { .mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = new_with_args(HashSet::new(), 0, functions); assert_eq!( checker.check_function(bar, &module_id, &types), - Err(vec![Error { + Err(vec![ErrorInner { pos: Some((Position::mock(), Position::mock())), message: "Function definition for function foo with signature () -> (field) not found." - .to_string() + .into() }]) ); } @@ -2870,15 +2925,15 @@ mod tests { .mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = new_with_args(HashSet::new(), 0, functions); assert_eq!( checker.check_function(bar, &module_id, &types), - Err(vec![Error { + Err(vec![ErrorInner { pos: Some((Position::mock(), Position::mock())), message: "Function definition for function foo with signature () -> (_) not found." - .to_string() + .into() }]) ); } @@ -2911,17 +2966,17 @@ mod tests { .mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = new_with_args(HashSet::new(), 0, HashSet::new()); assert_eq!( checker.check_function(bar, &module_id, &types), - Err(vec![Error { + Err(vec![ErrorInner { pos: Some((Position::mock(), Position::mock())), message: "Function definition for function foo with signature () -> (field) not found." - .to_string() + .into() }]) ); } @@ -3015,14 +3070,17 @@ mod tests { imports: vec![], }; - let mut state = State::new(vec![(String::from("main"), module)].into_iter().collect()); + let mut state = State::new(vec![("main".into(), module)].into_iter().collect()); let mut checker = new_with_args(HashSet::new(), 0, HashSet::new()); assert_eq!( - checker.check_module(&String::from("main"), &mut state), + checker.check_module(&"main".into(), &mut state), Err(vec![Error { - pos: Some((Position::mock(), Position::mock())), - message: "Identifier \"x\" is undefined".to_string() + inner: ErrorInner { + pos: Some((Position::mock(), Position::mock())), + message: "Identifier \"x\" is undefined".into() + }, + module_id: "main".into() }]) ); } @@ -3049,16 +3107,16 @@ mod tests { .mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = new_with_args(HashSet::new(), 0, HashSet::new()); assert_eq!( checker.check_function(bar, &module_id, &types), - Err(vec![Error { + Err(vec![ErrorInner { pos: Some((Position::mock(), Position::mock())), message: "Function definition for function foo with signature () -> (_) not found." - .to_string() + .into() }]) ); } @@ -3093,14 +3151,14 @@ mod tests { .mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = new_with_args(HashSet::new(), 0, HashSet::new()); assert_eq!( checker.check_function(bar, &module_id, &types), - Err(vec![Error { + Err(vec![ErrorInner { pos: Some((Position::mock(), Position::mock())), - message: "Identifier \"a\" is undefined".to_string() + message: "Identifier \"a\" is undefined".into() }]) ); } @@ -3198,7 +3256,7 @@ mod tests { }; let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = new_with_args(HashSet::new(), 0, functions); assert_eq!( @@ -3278,18 +3336,19 @@ mod tests { }; let program = Program { - modules: vec![(String::from("main"), main_module)] - .into_iter() - .collect(), - main: String::from("main"), + modules: vec![("main".into(), main_module)].into_iter().collect(), + main: "main".into(), }; let mut checker = Checker::new(); assert_eq!( checker.check_program(program), Err(vec![Error { - pos: None, - message: "Only one main function allowed, found 2".to_string() + inner: ErrorInner { + pos: None, + message: "Only one main function allowed, found 2".into() + }, + module_id: "main".into() }]) ); } @@ -3302,17 +3361,9 @@ mod tests { // should fail let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = Checker::new(); - let _: Result, Vec> = checker.check_statement( - Statement::Declaration( - absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), - ) - .mock(), - &module_id, - &types, - ); - let s2_checked: Result, Vec> = checker.check_statement( + let _: Result, Vec> = checker.check_statement( Statement::Declaration( absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), ) @@ -3320,11 +3371,20 @@ mod tests { &module_id, &types, ); + let s2_checked: Result, Vec> = checker + .check_statement( + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), + &module_id, + &types, + ); assert_eq!( s2_checked, - Err(vec![Error { + Err(vec![ErrorInner { pos: Some((Position::mock(), Position::mock())), - message: "Duplicate declaration for variable named a".to_string() + message: "Duplicate declaration for variable named a".into() }]) ); } @@ -3337,10 +3397,10 @@ mod tests { // should fail let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker = Checker::new(); - let _: Result, Vec> = checker.check_statement( + let _: Result, Vec> = checker.check_statement( Statement::Declaration( absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), ) @@ -3348,17 +3408,20 @@ mod tests { &module_id, &types, ); - let s2_checked: Result, Vec> = checker.check_statement( - Statement::Declaration(absy::Variable::new("a", UnresolvedType::Boolean.mock()).mock()) + let s2_checked: Result, Vec> = checker + .check_statement( + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::Boolean.mock()).mock(), + ) .mock(), - &module_id, - &types, - ); + &module_id, + &types, + ); assert_eq!( s2_checked, - Err(vec![Error { + Err(vec![ErrorInner { pos: Some((Position::mock(), Position::mock())), - message: "Duplicate declaration for variable named a".to_string() + message: "Duplicate declaration for variable named a".into() }]) ); } @@ -3370,7 +3433,7 @@ mod tests { fn create_module_with_foo( s: StructType<'static>, ) -> (Checker<'static>, State<'static, FieldPrime>) { - let module_id = "".to_string(); + let module_id: PathBuf = "".into(); let module: Module = Module { imports: vec![], @@ -3397,7 +3460,7 @@ mod tests { #[test] fn empty_def() { // an empty struct should be allowed to be defined - let module_id = "".to_string(); + let module_id = "".into(); let types = HashMap::new(); let declaration = StructType { fields: vec![] }.mock(); @@ -3412,7 +3475,7 @@ mod tests { #[test] fn valid_def() { // a valid struct should be allowed to be defined - let module_id = "".to_string(); + let module_id = "".into(); let types = HashMap::new(); let declaration = StructType { fields: vec![ @@ -3431,8 +3494,8 @@ mod tests { .mock(); let expected_type = Type::Struct(vec![ - StructMember::new("foo".to_string(), Type::FieldElement), - StructMember::new("bar".to_string(), Type::Boolean), + StructMember::new("foo".into(), Type::FieldElement), + StructMember::new("bar".into(), Type::Boolean), ]); assert_eq!( @@ -3444,7 +3507,7 @@ mod tests { #[test] fn preserve_order() { // two structs with inverted members are not equal - let module_id = "".to_string(); + let module_id = "".into(); let types = HashMap::new(); let declaration0 = StructType { @@ -3488,7 +3551,7 @@ mod tests { #[test] fn duplicate_member_def() { // definition of a struct with a duplicate member should be rejected - let module_id = "".to_string(); + let module_id = "".into(); let types = HashMap::new(); let declaration = StructType { @@ -3523,7 +3586,7 @@ mod tests { // struct Foo = { foo: field } // struct Bar = { foo: Foo } - let module_id = "".to_string(); + let module_id: PathBuf = "".into(); let module: Module = Module { imports: vec![], @@ -3548,7 +3611,7 @@ mod tests { StructType { fields: vec![StructField { id: "foo", - ty: UnresolvedType::User("Foo".to_string()).mock(), + ty: UnresolvedType::User("Foo".into()).mock(), } .mock()], } @@ -3565,16 +3628,13 @@ mod tests { assert_eq!( state .types - .get(&"".to_string()) + .get(&module_id) .unwrap() .get(&"Bar".to_string()) .unwrap(), &Type::Struct(vec![StructMember::new( - "foo".to_string(), - Type::Struct(vec![StructMember::new( - "foo".to_string(), - Type::FieldElement - )]) + "foo".into(), + Type::Struct(vec![StructMember::new("foo".into(), Type::FieldElement)]) )]) ); } @@ -3585,7 +3645,7 @@ mod tests { // struct Bar = { foo: Foo } - let module_id = "".to_string(); + let module_id: PathBuf = "".into(); let module: Module = Module { imports: vec![], @@ -3595,7 +3655,7 @@ mod tests { StructType { fields: vec![StructField { id: "foo", - ty: UnresolvedType::User("Foo".to_string()).mock(), + ty: UnresolvedType::User("Foo".into()).mock(), } .mock()], } @@ -3616,7 +3676,7 @@ mod tests { // struct Foo = { foo: Foo } - let module_id = "".to_string(); + let module_id: PathBuf = "".into(); let module: Module = Module { imports: vec![], @@ -3626,7 +3686,7 @@ mod tests { StructType { fields: vec![StructField { id: "foo", - ty: UnresolvedType::User("Foo".to_string()).mock(), + ty: UnresolvedType::User("Foo".into()).mock(), } .mock()], } @@ -3648,7 +3708,7 @@ mod tests { // struct Foo = { bar: Bar } // struct Bar = { foo: Foo } - let module_id = "".to_string(); + let module_id: PathBuf = "".into(); let module: Module = Module { imports: vec![], @@ -3659,7 +3719,7 @@ mod tests { StructType { fields: vec![StructField { id: "bar", - ty: UnresolvedType::User("Bar".to_string()).mock(), + ty: UnresolvedType::User("Bar".into()).mock(), } .mock()], } @@ -3673,7 +3733,7 @@ mod tests { StructType { fields: vec![StructField { id: "foo", - ty: UnresolvedType::User("Foo".to_string()).mock(), + ty: UnresolvedType::User("Foo".into()).mock(), } .mock()], } @@ -3713,12 +3773,12 @@ mod tests { assert_eq!( checker.check_type( - UnresolvedType::User("Foo".to_string()).mock(), - &MODULE_ID.to_string(), + UnresolvedType::User("Foo".into()).mock(), + &PathBuf::from(MODULE_ID).into(), &state.types ), Ok(Type::Struct(vec![StructMember::new( - "foo".to_string(), + "foo".into(), Type::FieldElement )])) ); @@ -3726,8 +3786,8 @@ mod tests { assert_eq!( checker .check_type( - UnresolvedType::User("Bar".to_string()).mock(), - &MODULE_ID.to_string(), + UnresolvedType::User("Bar".into()).mock(), + &PathBuf::from(MODULE_ID).into(), &state.types ) .unwrap_err() @@ -3753,24 +3813,19 @@ mod tests { assert_eq!( checker.check_parameter( absy::Parameter { - id: absy::Variable::new( - "a", - UnresolvedType::User("Foo".to_string()).mock(), - ) - .mock(), + id: + absy::Variable::new("a", UnresolvedType::User("Foo".into()).mock(),) + .mock(), private: true, } .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types, ), Ok(Parameter { id: Variable::with_id_and_type( "a".into(), - Type::Struct(vec![StructMember::new( - "foo".to_string(), - Type::FieldElement - )]) + Type::Struct(vec![StructMember::new("foo".into(), Type::FieldElement)]) ), private: true }) @@ -3782,13 +3837,13 @@ mod tests { absy::Parameter { id: absy::Variable::new( "a", - UnresolvedType::User("Bar".to_string()).mock(), + UnresolvedType::User("Bar".into()).mock(), ) .mock(), private: true, } .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types, ) .unwrap_err()[0] @@ -3814,22 +3869,16 @@ mod tests { assert_eq!( checker.check_statement::( Statement::Declaration( - absy::Variable::new( - "a", - UnresolvedType::User("Foo".to_string()).mock(), - ) - .mock() + absy::Variable::new("a", UnresolvedType::User("Foo".into()).mock(),) + .mock() ) .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types, ), Ok(TypedStatement::Declaration(Variable::with_id_and_type( "a".into(), - Type::Struct(vec![StructMember::new( - "foo".to_string(), - Type::FieldElement - )]) + Type::Struct(vec![StructMember::new("foo".into(), Type::FieldElement)]) ))) ); @@ -3839,13 +3888,13 @@ mod tests { absy::Parameter { id: absy::Variable::new( "a", - UnresolvedType::User("Bar".to_string()).mock(), + UnresolvedType::User("Bar".into()).mock(), ) .mock(), private: true, } .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types, ) .unwrap_err()[0] @@ -3878,7 +3927,7 @@ mod tests { checker.check_expression( Expression::Member( box Expression::InlineStruct( - "Foo".to_string(), + "Foo".into(), vec![( "foo", Expression::FieldConstant(FieldPrime::from(42)).mock() @@ -3888,7 +3937,7 @@ mod tests { "foo".into() ) .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types ), Ok(FieldElementExpression::Member( @@ -3896,11 +3945,8 @@ mod tests { FieldPrime::from(42) ) .into()]) - .annotate(vec![StructMember::new( - "foo".to_string(), - Type::FieldElement - )]), - "foo".to_string() + .annotate(vec![StructMember::new("foo".into(), Type::FieldElement)]), + "foo".into() ) .into()) ); @@ -3926,7 +3972,7 @@ mod tests { .check_expression( Expression::Member( box Expression::InlineStruct( - "Foo".to_string(), + "Foo".into(), vec![( "foo", Expression::FieldConstant(FieldPrime::from(42)).mock() @@ -3936,7 +3982,7 @@ mod tests { "bar".into() ) .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types ) .unwrap_err() @@ -3966,14 +4012,14 @@ mod tests { checker .check_expression( Expression::InlineStruct( - "Bar".to_string(), + "Bar".into(), vec![( "foo", Expression::FieldConstant(FieldPrime::from(42)).mock() )] ) .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types ) .unwrap_err() @@ -4007,7 +4053,7 @@ mod tests { assert_eq!( checker.check_expression( Expression::InlineStruct( - "Foo".to_string(), + "Foo".into(), vec![ ( "foo", @@ -4017,7 +4063,7 @@ mod tests { ] ) .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types ), Ok(StructExpressionInner::Value(vec![ @@ -4025,8 +4071,8 @@ mod tests { BooleanExpression::Value(true).into() ]) .annotate(vec![ - StructMember::new("foo".to_string(), Type::FieldElement), - StructMember::new("bar".to_string(), Type::Boolean) + StructMember::new("foo".into(), Type::FieldElement), + StructMember::new("bar".into(), Type::Boolean) ]) .into()) ); @@ -4057,7 +4103,7 @@ mod tests { assert_eq!( checker.check_expression( Expression::InlineStruct( - "Foo".to_string(), + "Foo".into(), vec![ ("bar", Expression::BooleanConstant(true).mock()), ( @@ -4067,7 +4113,7 @@ mod tests { ] ) .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types ), Ok(StructExpressionInner::Value(vec![ @@ -4075,8 +4121,8 @@ mod tests { BooleanExpression::Value(true).into() ]) .annotate(vec![ - StructMember::new("foo".to_string(), Type::FieldElement), - StructMember::new("bar".to_string(), Type::Boolean) + StructMember::new("foo".into(), Type::FieldElement), + StructMember::new("bar".into(), Type::Boolean) ]) .into()) ); @@ -4108,14 +4154,14 @@ mod tests { checker .check_expression( Expression::InlineStruct( - "Foo".to_string(), + "Foo".into(), vec![( "foo", Expression::FieldConstant(FieldPrime::from(42)).mock() )] ) .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types ) .unwrap_err() @@ -4152,7 +4198,7 @@ mod tests { checker .check_expression( Expression::InlineStruct( - "Foo".to_string(), + "Foo".into(), vec![( "baz", Expression::BooleanConstant(true).mock() @@ -4162,7 +4208,7 @@ mod tests { )] ) .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types ).unwrap_err() .message, @@ -4173,7 +4219,7 @@ mod tests { checker .check_expression( Expression::InlineStruct( - "Foo".to_string(), + "Foo".into(), vec![ ( "bar", @@ -4186,7 +4232,7 @@ mod tests { ] ) .mock(), - &MODULE_ID.to_string(), + &PathBuf::from(MODULE_ID).into(), &state.types ) .unwrap_err() @@ -4206,7 +4252,7 @@ mod tests { let a = Assignee::Identifier::("a").mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker: Checker = Checker::new(); checker .check_statement::( @@ -4240,7 +4286,7 @@ mod tests { .mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker: Checker = Checker::new(); checker @@ -4289,7 +4335,7 @@ mod tests { .mock(); let types = HashMap::new(); - let module_id = String::from(""); + let module_id = "".into(); let mut checker: Checker = Checker::new(); checker .check_statement::( diff --git a/zokrates_core/src/static_analysis/inline.rs b/zokrates_core/src/static_analysis/inline.rs index 198443d7..4acda308 100644 --- a/zokrates_core/src/static_analysis/inline.rs +++ b/zokrates_core/src/static_analysis/inline.rs @@ -27,8 +27,8 @@ pub struct Inliner<'ast, T: Field> { modules: TypedModules<'ast, T>, // the modules in which to look for functions when inlining module_id: TypedModuleId, // the current module we're visiting statement_buffer: Vec>, // a buffer of statements to be added to the inlined statements - stack: Vec<(String, FunctionKey<'ast>, usize)>, // the current call stack - call_count: HashMap<(String, FunctionKey<'ast>), usize>, // the call count for each function + stack: Vec<(TypedModuleId, FunctionKey<'ast>, usize)>, // the current call stack + call_count: HashMap<(TypedModuleId, FunctionKey<'ast>), usize>, // the call count for each function } impl<'ast, T: Field> Inliner<'ast, T> { @@ -74,9 +74,9 @@ impl<'ast, T: Field> Inliner<'ast, T> { // return a program with a single module containing `main`, `_UNPACK`, and `_SHA256_ROUND TypedProgram { - main: String::from("main"), + main: "main".into(), modules: vec![( - String::from("main"), + "main".into(), TypedModule { functions: vec![ (unpack_key, TypedFunctionSymbol::Flat(unpack)), @@ -311,6 +311,7 @@ impl<'ast, T: Field> Folder<'ast, T> for Inliner<'ast, T> { #[cfg(test)] mod tests { use super::*; + use std::path::PathBuf; use typed_absy::types::{FunctionKey, Signature, Type}; use zokrates_field::field::FieldPrime; @@ -354,7 +355,7 @@ mod tests { TypedFunctionSymbol::There( FunctionKey::with_id("foo") .signature(Signature::new().outputs(vec![Type::FieldElement])), - String::from("foo"), + "foo".into(), ), ), ] @@ -378,12 +379,12 @@ mod tests { .collect(), }; - let modules: HashMap<_, _> = vec![(String::from("main"), main), (String::from("foo"), foo)] + let modules: HashMap<_, _> = vec![("main".into(), main), ("foo".into(), foo)] .into_iter() .collect(); let program = TypedProgram { - main: String::from("main"), + main: "main".into(), modules, }; @@ -393,7 +394,7 @@ mod tests { assert_eq!( program .modules - .get(&String::from("main")) + .get(&PathBuf::from("main")) .unwrap() .functions .get( @@ -469,7 +470,7 @@ mod tests { .inputs(vec![Type::FieldElement]) .outputs(vec![Type::FieldElement]), ), - String::from("foo"), + "foo".into(), ), ), ] @@ -500,12 +501,12 @@ mod tests { .collect(), }; - let modules: HashMap<_, _> = vec![(String::from("main"), main), (String::from("foo"), foo)] + let modules: HashMap<_, _> = vec![("main".into(), main), ("foo".into(), foo)] .into_iter() .collect(); let program: TypedProgram = TypedProgram { - main: String::from("main"), + main: "main".into(), modules, }; @@ -514,7 +515,7 @@ mod tests { assert_eq!(program.modules.len(), 1); let stack = vec![( - String::from("foo"), + "foo".into(), FunctionKey::with_id("foo").signature( Signature::new() .inputs(vec![Type::FieldElement]) @@ -526,7 +527,7 @@ mod tests { assert_eq!( program .modules - .get(&String::from("main")) + .get(&PathBuf::from("main")) .unwrap() .functions .get( @@ -614,7 +615,7 @@ mod tests { TypedFunctionSymbol::There( FunctionKey::with_id("foo") .signature(Signature::new().outputs(vec![Type::FieldElement])), - String::from("foo"), + "foo".into(), ), ), ] @@ -638,12 +639,12 @@ mod tests { .collect(), }; - let modules: HashMap<_, _> = vec![(String::from("main"), main), (String::from("foo"), foo)] + let modules: HashMap<_, _> = vec![("main".into(), main), ("foo".into(), foo)] .into_iter() .collect(); let program = TypedProgram { - main: String::from("main"), + main: "main".into(), modules, }; @@ -653,7 +654,7 @@ mod tests { assert_eq!( program .modules - .get(&String::from("main")) + .get(&PathBuf::from("main")) .unwrap() .functions .get( @@ -733,10 +734,10 @@ mod tests { .collect(), }; - let modules: HashMap<_, _> = vec![(String::from("main"), main)].into_iter().collect(); + let modules: HashMap<_, _> = vec![("main".into(), main)].into_iter().collect(); let program = TypedProgram { - main: String::from("main"), + main: "main".into(), modules, }; @@ -746,7 +747,7 @@ mod tests { assert_eq!( program .modules - .get(&String::from("main")) + .get(&PathBuf::from("main")) .unwrap() .functions .get( @@ -832,7 +833,7 @@ mod tests { .inputs(vec![Type::FieldElement]) .outputs(vec![Type::FieldElement]), ), - String::from("id"), + "id".into(), ), ), ] @@ -861,19 +862,19 @@ mod tests { .collect(), }; - let modules = vec![(String::from("main"), main), (String::from("id"), id)] + let modules = vec![("main".into(), main), ("id".into(), id)] .into_iter() .collect(); let program: TypedProgram = TypedProgram { - main: String::from("main"), + main: "main".into(), modules, }; let program = Inliner::inline(program); let stack0 = vec![( - String::from("id"), + "id".into(), FunctionKey::with_id("main").signature( Signature::new() .inputs(vec![Type::FieldElement]) @@ -882,7 +883,7 @@ mod tests { 1, )]; let stack1 = vec![( - String::from("id"), + "id".into(), FunctionKey::with_id("main").signature( Signature::new() .inputs(vec![Type::FieldElement]) @@ -895,7 +896,7 @@ mod tests { assert_eq!( program .modules - .get(&String::from("main")) + .get(&PathBuf::from("main")) .unwrap() .functions .get( diff --git a/zokrates_core/src/static_analysis/propagate_unroll.rs b/zokrates_core/src/static_analysis/propagate_unroll.rs index d12dd4a1..2bc8fd66 100644 --- a/zokrates_core/src/static_analysis/propagate_unroll.rs +++ b/zokrates_core/src/static_analysis/propagate_unroll.rs @@ -78,7 +78,7 @@ mod tests { let p = TypedProgram { modules: vec![( - "main".to_string(), + "main".into(), TypedModule { functions: vec![( FunctionKey::with_id("main"), @@ -94,7 +94,7 @@ mod tests { )] .into_iter() .collect(), - main: "main".to_string(), + main: "main".into(), }; assert!(PropagatedUnroller::unroll(p).is_err()); @@ -203,7 +203,7 @@ mod tests { let p = TypedProgram { modules: vec![( - "main".to_string(), + "main".into(), TypedModule { functions: vec![( FunctionKey::with_id("main"), @@ -219,11 +219,12 @@ mod tests { )] .into_iter() .collect(), - main: "main".to_string(), + main: "main".into(), }; - let statements = match PropagatedUnroller::unroll(p).unwrap().modules["main"].functions - [&FunctionKey::with_id("main")] + let statements = match PropagatedUnroller::unroll(p).unwrap().modules + [std::path::Path::new("main")] + .functions[&FunctionKey::with_id("main")] .clone() { TypedFunctionSymbol::Here(f) => f.statements, diff --git a/zokrates_core/src/typed_absy/abi.rs b/zokrates_core/src/typed_absy/abi.rs index eb9a22e4..a5274796 100644 --- a/zokrates_core/src/typed_absy/abi.rs +++ b/zokrates_core/src/typed_absy/abi.rs @@ -69,10 +69,10 @@ mod tests { ); let mut modules = HashMap::new(); - modules.insert(String::from("main"), TypedModule { functions }); + modules.insert("main".into(), TypedModule { functions }); let typed_ast: TypedProgram = TypedProgram { - main: String::from("main"), + main: "main".into(), modules, }; diff --git a/zokrates_core/src/typed_absy/mod.rs b/zokrates_core/src/typed_absy/mod.rs index 1dc31570..05a910ce 100644 --- a/zokrates_core/src/typed_absy/mod.rs +++ b/zokrates_core/src/typed_absy/mod.rs @@ -14,6 +14,7 @@ mod variable; pub use crate::typed_absy::parameter::Parameter; pub use crate::typed_absy::types::{Signature, Type}; pub use crate::typed_absy::variable::Variable; +use std::path::PathBuf; use crate::typed_absy::types::{FunctionKey, MemberId}; use embed::FlatEmbed; @@ -38,7 +39,7 @@ pub struct Identifier<'ast> { } /// An identifier for a `TypedModule`. Typically a path or uri. -pub type TypedModuleId = String; +pub type TypedModuleId = PathBuf; /// A collection of `TypedModule`s pub type TypedModules<'ast, T> = HashMap>; @@ -90,7 +91,7 @@ impl<'ast, T: Field> fmt::Display for TypedProgram<'ast, T> { writeln!( f, "| {}: |{}", - module_id, + module_id.display(), if *module_id == self.main { "<---- main" } else { @@ -123,7 +124,12 @@ impl<'ast> fmt::Display for Identifier<'ast> { "{}_{}_{}", self.stack .iter() - .map(|(name, sig, count)| format!("{}_{}_{}", name, sig.to_slug(), count)) + .map(|(name, sig, count)| format!( + "{}_{}_{}", + name.display(), + sig.to_slug(), + count + )) .collect::>() .join("_"), self.id, @@ -189,7 +195,10 @@ impl<'ast, T: Field> fmt::Display for TypedModule<'ast, T> { TypedFunctionSymbol::Here(ref function) => format!("def {}{}", key.id, function), TypedFunctionSymbol::There(ref fun_key, ref module_id) => format!( "import {} from \"{}\" as {} // with signature {}", - fun_key.id, module_id, key.id, key.signature + fun_key.id, + module_id.display(), + key.id, + key.signature ), TypedFunctionSymbol::Flat(ref flat_fun) => { format!("def {}{}:\n\t// hidden", key.id, flat_fun.signature::()) diff --git a/zokrates_core_test/tests/tests/import/dep/dep/foo.zok b/zokrates_core_test/tests/tests/import/dep/dep/foo.zok new file mode 100644 index 00000000..4498c0fd --- /dev/null +++ b/zokrates_core_test/tests/tests/import/dep/dep/foo.zok @@ -0,0 +1,2 @@ +def foo() -> (field): + return 1 \ No newline at end of file diff --git a/zokrates_core_test/tests/tests/import/dep/foo.zok b/zokrates_core_test/tests/tests/import/dep/foo.zok new file mode 100644 index 00000000..93e89cff --- /dev/null +++ b/zokrates_core_test/tests/tests/import/dep/foo.zok @@ -0,0 +1,4 @@ +from "./dep/foo" import foo as bar + +def foo() -> (field): + return 2 + bar() \ No newline at end of file diff --git a/zokrates_core_test/tests/tests/import/import.json b/zokrates_core_test/tests/tests/import/import.json new file mode 100644 index 00000000..5a27ad8c --- /dev/null +++ b/zokrates_core_test/tests/tests/import/import.json @@ -0,0 +1,15 @@ +{ + "entry_point": "./tests/tests/import/import.zok", + "tests": [ + { + "input": { + "values": [] + }, + "output": { + "Ok": { + "values": ["3"] + } + } + } + ] +} \ No newline at end of file diff --git a/zokrates_core_test/tests/tests/import/import.zok b/zokrates_core_test/tests/tests/import/import.zok new file mode 100644 index 00000000..cfe5e851 --- /dev/null +++ b/zokrates_core_test/tests/tests/import/import.zok @@ -0,0 +1,4 @@ +from "./dep/foo" import foo + +def main() -> (field): + return foo() \ No newline at end of file diff --git a/zokrates_fs_resolver/src/lib.rs b/zokrates_fs_resolver/src/lib.rs index bf1f32a5..5d853a41 100644 --- a/zokrates_fs_resolver/src/lib.rs +++ b/zokrates_fs_resolver/src/lib.rs @@ -6,43 +6,72 @@ use std::path::{Component, PathBuf}; const ZOKRATES_HOME: &str = &"ZOKRATES_HOME"; -type CurrentLocation = String; -type ImportLocation<'a> = String; +// path to the current file we're importing into +type CurrentLocation = PathBuf; +// path we're importing from +type ImportLocation<'a> = PathBuf; type SourceCode = String; +/// Returns the source code and the new location from a file path and import path +/// +/// # Arguments +/// +/// * `current_location` - Path to the file we're importing into +/// * `import_location` - Path to the file we're importing from +/// +/// # Returns +/// * The content of the file we're importing from +/// * The path to the file we're importing from +/// +/// # Remarks +/// +/// * `current_location* must point to a file +/// * `import_location` and the returned path are both relative to the directory in which `current_location` is, unless it's an absolute +/// path, in which case they are relative to the root of the ZoKrates stdlib at `$ZOKRATES_HOME` +/// pub fn resolve<'a>( current_location: CurrentLocation, import_location: ImportLocation<'a>, ) -> Result<(SourceCode, CurrentLocation), io::Error> { let source = Path::new(&import_location); + if !current_location.is_file() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("{} was expected to be a file", current_location.display()), + )); + } + // paths starting with `./` or `../` are interpreted relative to the current file // other paths `abc/def` are interpreted relative to $ZOKRATES_HOME let base = match source.components().next() { - Some(Component::CurDir) | Some(Component::ParentDir) => PathBuf::from(current_location), - _ => PathBuf::from( - std::env::var(ZOKRATES_HOME).expect("$ZOKRATES_HOME is not set, please set it"), - ), - }; + Some(Component::CurDir) | Some(Component::ParentDir) => { + Ok(PathBuf::from(current_location).parent().unwrap().into()) + } + _ => std::env::var(ZOKRATES_HOME) + .map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + "$ZOKRATES_HOME is not set, please set it", + ) + }) + .map(PathBuf::from), + }?; let path_owned = base - .join(PathBuf::from(import_location)) + .join(PathBuf::from(import_location.clone())) .with_extension("zok"); - if path_owned.is_dir() { - return Err(io::Error::new(io::ErrorKind::Other, "Not a file")); + if !path_owned.is_file() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("No file found at {}", import_location.display()), + )); } - let next_location = generate_next_location(&path_owned)?; - let source = read_to_string(path_owned)?; + let source = read_to_string(&path_owned)?; - Ok((source, next_location)) -} - -fn generate_next_location<'a>(path: &'a PathBuf) -> Result { - path.parent() - .ok_or(io::Error::new(io::ErrorKind::Other, "Invalid path")) - .map(|v| v.to_path_buf().into_os_string().into_string().unwrap()) + Ok((source, path_owned)) } #[cfg(test)] @@ -52,39 +81,44 @@ mod tests { #[test] fn valid_path() { - use std::io::Write; - // create a source folder with a zok file let folder = tempfile::tempdir().unwrap(); let file_path = folder.path().join("bar.zok"); - let mut file = File::create(file_path).unwrap(); - writeln!(file, "some code").unwrap(); - let (_, next_location) = - resolve(folder.path().to_str().unwrap().to_string(), "./bar".into()).unwrap(); - assert_eq!(next_location, folder.path().to_str().unwrap().to_string()); + File::create(file_path.clone()).unwrap(); + let (_, next_location) = resolve(file_path.clone(), "./bar.zok".into()).unwrap(); + assert_eq!(next_location, file_path); } #[test] fn non_existing_file() { - let res = resolve(String::from("./src"), "./rubbish".into()); + let res = resolve("./source.zok".into(), "./rubbish".into()); assert!(res.is_err()); } #[test] fn invalid_location() { - let res = resolve(String::from(",8!-$2abc"), "./foo".into()); + let res = resolve(",8!-$2abc".into(), "./foo".into()); assert!(res.is_err()); } #[test] fn not_a_file() { - let res = resolve(String::from("."), "./src/".into()); + // create a source folder with a zok file + let folder = tempfile::tempdir().unwrap(); + let dir_path = folder.path().join("dir"); + std::fs::create_dir(dir_path.clone()).unwrap(); + + let res = resolve(".".into(), "./dir/".into()); assert!(res.is_err()); } #[test] fn no_parent() { - let res = resolve(String::from("."), ".".into()); + // create a source folder with a zok file + let folder = tempfile::tempdir().unwrap(); + let file_path = folder.path().join("foo.zok"); + File::create(file_path.clone()).unwrap(); + let res = resolve(file_path, ".".into()); assert!(res.is_err()); } @@ -101,20 +135,13 @@ mod tests { // create a user folder with a code file let source_folder = tempfile::tempdir().unwrap(); let file_path = source_folder.path().join("bar.zok"); - let mut file = File::create(file_path).unwrap(); + let mut file = File::create(file_path.clone()).unwrap(); writeln!(file, "").unwrap(); // assign HOME folder to ZOKRATES_HOME std::env::set_var(ZOKRATES_HOME, zokrates_home_folder.path()); - let result = resolve( - source_folder - .path() - .to_path_buf() - .to_string_lossy() - .to_string(), - "./bar.zok".into(), - ); + let result = resolve(file_path, "./bar.zok".into()); assert!(result.is_ok()); // the imported file should be the user's assert_eq!(result.unwrap().0, String::from("\n")); @@ -133,20 +160,13 @@ mod tests { // create a user folder with a code file let source_folder = tempfile::tempdir().unwrap(); let file_path = source_folder.path().join("bar.zok"); - let mut file = File::create(file_path).unwrap(); + let mut file = File::create(file_path.clone()).unwrap(); writeln!(file, "").unwrap(); // assign HOME folder to ZOKRATES_HOME std::env::set_var(ZOKRATES_HOME, zokrates_home_folder.path()); - let result = resolve( - source_folder - .path() - .to_path_buf() - .to_string_lossy() - .to_string(), - "bar.zok".into(), - ); + let result = resolve(file_path.clone(), "bar.zok".into()); assert!(result.is_ok()); // the imported file should be the user's assert_eq!(result.unwrap().0, String::from("\n")); @@ -162,13 +182,11 @@ mod tests { let file_path = source_folder.path().join("bar.zok"); let mut file = File::create(file_path).unwrap(); writeln!(file, "").unwrap(); + let origin_path = source_subfolder.path().join("foo.zok"); + File::create(origin_path).unwrap(); let result = resolve( - source_subfolder - .path() - .to_path_buf() - .to_string_lossy() - .to_string(), + source_subfolder.path().to_path_buf().join("foo.zok"), "../bar.zok".into(), ); assert!(result.is_ok()); @@ -189,21 +207,21 @@ mod tests { // assign HOME folder to ZOKRATES_HOME std::env::set_var(ZOKRATES_HOME, zokrates_home_folder.path()); - let result = resolve("/path/to/user/folder".to_string(), "./bar.zok".into()); + let result = resolve("/path/to/source.zok".into(), "./bar.zok".into()); assert!(result.is_err()); } #[test] fn fail_if_not_found_in_std() { std::env::set_var(ZOKRATES_HOME, ""); - let result = resolve("/path/to/source".to_string(), "bar.zok".into()); + let result = resolve("/path/to/source.zok".into(), "bar.zok".into()); assert!(result.is_err()); } #[test] - #[should_panic] fn panic_if_home_not_set() { std::env::remove_var(ZOKRATES_HOME); - let _ = resolve("/path/to/source".to_string(), "bar.zok".into()); + let result = resolve("/path/to/source.zok".into(), "bar.zok".into()); + assert!(result.is_err()); } } diff --git a/zokrates_js/src/lib.rs b/zokrates_js/src/lib.rs index 686f9642..81fd8338 100644 --- a/zokrates_js/src/lib.rs +++ b/zokrates_js/src/lib.rs @@ -1,9 +1,10 @@ use bincode::{deserialize, serialize}; use serde::{Deserialize, Serialize}; use serde_json::to_string_pretty; +use std::path::PathBuf; use wasm_bindgen::prelude::*; use zokrates_abi::{parse_strict, Encode, Decode, Inputs}; -use zokrates_core::compile::{compile as core_compile, CompilationArtifacts}; +use zokrates_core::compile::{compile as core_compile, CompilationArtifacts, CompileError}; use zokrates_core::imports::Error; use zokrates_core::ir; use zokrates_core::proof_system::{self, ProofSystem}; @@ -30,8 +31,8 @@ pub struct ComputationResult { } impl ResolverResult { - fn into_tuple(self) -> (String, String) { - (self.source, self.location) + fn into_tuple(self) -> (String, PathBuf) { + (self.source, PathBuf::from(self.location)) } } @@ -53,30 +54,38 @@ pub fn compile( location: JsValue, resolve: &js_sys::Function, ) -> Result { - let closure = |l: String, p: String| { + let closure = |l: PathBuf, p: PathBuf| { let value = resolve - .call2(&JsValue::UNDEFINED, &l.into(), &p.clone().into()) + .call2(&JsValue::UNDEFINED, &l.display().to_string().into(), &p.clone().display().to_string().into()) .map_err(|_| { Error::new(format!( "Error thrown in callback: Could not resolve `{}`", - p + p.display() )) })?; if value.is_null() || value.is_undefined() { - Err(Error::new(format!("Could not resolve `{}`", p))) + Err(Error::new(format!("Could not resolve `{}`", p.display()))) } else { let result: ResolverResult = value.into_serde().unwrap(); Ok(result.into_tuple()) } }; + let fmt_error = |e: &CompileError| { + format!( + "{}:{}", + e.file().display(), + e.value() + ) + }; + let artifacts: CompilationArtifacts = core_compile( source.as_string().unwrap(), - location.as_string().unwrap(), + PathBuf::from(location.as_string().unwrap()), Some(&closure), ) - .map_err(|ce| JsValue::from_str(&format!("{}", ce)))?; + .map_err(|ce| JsValue::from_str(&format!("{}", ce.0.iter().map(|e| fmt_error(e)).collect::>().join("\n"))))?; let result = CompilationResult { program: serialize_program(artifacts.prog())?, diff --git a/zokrates_test/src/lib.rs b/zokrates_test/src/lib.rs index bc6f7eda..275b6612 100644 --- a/zokrates_test/src/lib.rs +++ b/zokrates_test/src/lib.rs @@ -77,17 +77,7 @@ pub fn test_inner(test_path: &str) { let code = std::fs::read_to_string(&t.entry_point).unwrap(); - let artifacts = compile( - code, - t.entry_point - .parent() - .unwrap() - .to_str() - .unwrap() - .to_string(), - Some(&resolve), - ) - .unwrap(); + let artifacts = compile(code, t.entry_point.clone(), Some(&resolve)).unwrap(); let bin = artifacts.prog();