From e756992e4336096898dfcee32de4e40da96d4854 Mon Sep 17 00:00:00 2001 From: Ricardo Maurizio Paul Date: Tue, 20 Sep 2022 09:59:49 +0200 Subject: [PATCH 01/70] Prepare parser_logic to report multiple errors --- parser/src/errors.rs | 187 ++++++++++++++++++++++++------------ parser/src/include_logic.rs | 61 ++++++------ parser/src/lang.lalrpop | 3 +- parser/src/lib.rs | 138 ++++++++++++++------------ parser/src/parser_logic.rs | 50 ++++++---- 5 files changed, 266 insertions(+), 173 deletions(-) diff --git a/parser/src/errors.rs b/parser/src/errors.rs index e589c28f8..da203910a 100644 --- a/parser/src/errors.rs +++ b/parser/src/errors.rs @@ -3,85 +3,144 @@ use program_structure::error_definition::Report; use program_structure::file_definition::{FileID, FileLocation}; use program_structure::abstract_syntax_tree::ast::Version; -pub struct UnclosedCommentError { - pub location: FileLocation, - pub file_id: FileID, +pub enum Error { + UnclosedComment { + location: FileLocation, + file_id: FileID, + }, + GenericParsing { + location: FileLocation, + file_id: FileID, + msg: String, + }, + FileOs { + path: String, + }, + NoMain, + MultipleMain, + CompilerVersion { + path: String, + required_version: Version, + version: Version, + }, + MissingSemicolon, + UnrecognizedInclude { + location: FileLocation, + file_id: FileID, + }, + UnrecognizedVersion { + location: FileLocation, + file_id: FileID, + }, + UnrecognizedPragma { + location: FileLocation, + file_id: FileID, + }, + IllegalExpression { + location: FileLocation, + file_id: FileID, + }, + IllegalStatement { + location: FileLocation, + file_id: FileID, + }, } -impl UnclosedCommentError { - pub fn produce_report(error: Self) -> Report { - let mut report = Report::error(format!("unterminated /* */"), ReportCode::ParseFail); - report.add_primary(error.location, error.file_id, format!("Comment starts here")); - report - } -} +impl Error { + pub fn produce_report(self) -> Report { + match self { + Error::UnclosedComment { location, file_id } => { + let mut report = + Report::error("unterminated /* */".to_string(), ReportCode::ParseFail); + report.add_primary(location, file_id, "Comment starts here".to_string()); + report + } + Error::GenericParsing { + location, + file_id, + msg, + } => { + let mut report = Report::error(msg, ReportCode::ParseFail); + report.add_primary(location, file_id, "Invalid syntax".to_string()); + report + } + Error::FileOs { path } => Report::error( + format!("Could not open file {}", path), + ReportCode::ParseFail, + ), + Error::NoMain => Report::error( + "No main specified in the project structure".to_string(), + ReportCode::NoMainFoundInProject, + ), + Error::MultipleMain =>{ + Report::error( + "Multiple main components in the project structure".to_string(), + ReportCode::MultipleMainInComponent, + ) + } + Error::CompilerVersion { + path, + required_version, + version, + } => { + Report::error( + format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", path, required_version, version ), + ReportCode::CompilerVersionError, + ) + } + Error::MissingSemicolon => { + Report::error("missing semicolon".to_owned(), ReportCode::ParseFail) + } + Error::UnrecognizedInclude{location, file_id} => { + let mut report = + Report::error("unrecognized argument in include directive".to_string(), ReportCode::ParseFail); + report.add_primary(location, file_id, "this argument".to_string()); + report -pub struct ParsingError { - pub location: FileLocation, - pub file_id: FileID, - pub msg: String, -} - -impl ParsingError { - pub fn produce_report(error: Self) -> Report { - let mut report = Report::error(error.msg, ReportCode::ParseFail); - report.add_primary(error.location, error.file_id, format!("Invalid syntax")); - report - } -} - -pub struct FileOsError { - pub path: String, -} -impl FileOsError { - pub fn produce_report(error: Self) -> Report { - Report::error(format!("Could not open file {}", error.path), ReportCode::ParseFail) - } -} + } + Error::UnrecognizedPragma{location, file_id} => { + let mut report = + Report::error("unrecognized argument in pragma directive".to_string(), ReportCode::ParseFail); + report.add_primary(location, file_id, "this argument".to_string()); + report -pub struct NoMainError; -impl NoMainError { - pub fn produce_report() -> Report { - Report::error( - format!("No main specified in the project structure"), - ReportCode::NoMainFoundInProject, - ) - } -} + } + Error::UnrecognizedVersion{location, file_id} => { + let mut report = + Report::error("unrecognized version argument in pragma directive".to_string(), ReportCode::ParseFail); + report.add_primary(location, file_id, "this argument".to_string()); + report + } + Error::IllegalExpression{location, file_id} => { + let mut report = + Report::error("illegal expression".to_string(), ReportCode::ParseFail); + report.add_primary(location, file_id, "here".to_string()); + report + } -pub struct MultipleMainError; -impl MultipleMainError { - pub fn produce_report() -> Report { - Report::error( - format!("Multiple main components in the project structure"), - ReportCode::MultipleMainInComponent, - ) + Error::IllegalStatement{location, file_id} => { + let mut report = + Report::error("illegal statement".to_string(), ReportCode::ParseFail); + report.add_primary(location, file_id, "here".to_string()); + report + } + } } } -pub struct CompilerVersionError{ +pub struct NoCompilerVersionWarning { pub path: String, - pub required_version: Version, pub version: Version, } -impl CompilerVersionError { - pub fn produce_report(error: Self) -> Report { - Report::error( - format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", error.path, error.required_version, error.version ), - ReportCode::CompilerVersionError, - ) - } -} -pub struct NoCompilerVersionWarning{ - pub path: String, - pub version: Version, -} impl NoCompilerVersionWarning { pub fn produce_report(error: Self) -> Report { Report::warning( - format!("File {} does not include pragma version. Assuming pragma version {:?}", error.path, error.version), + format!( + "File {} does not include pragma version. Assuming pragma version {:?}", + error.path, error.version + ), ReportCode::NoCompilerVersionWarning, ) } -} \ No newline at end of file +} diff --git a/parser/src/include_logic.rs b/parser/src/include_logic.rs index 4a9540052..f4e5a8ea1 100644 --- a/parser/src/include_logic.rs +++ b/parser/src/include_logic.rs @@ -1,8 +1,8 @@ -use super::errors::FileOsError; use program_structure::error_code::ReportCode; use program_structure::error_definition::Report; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; +use crate::errors::Error; pub struct FileStack { current_location: PathBuf, @@ -17,25 +17,28 @@ impl FileStack { FileStack { current_location: location, black_paths: HashSet::new(), stack: vec![src] } } - pub fn add_include(f_stack: &mut FileStack, name: String, libraries : &Vec) -> Result { + pub fn add_include( + f_stack: &mut FileStack, + name: String, + libraries: &Vec, + ) -> Result { let mut libraries2 = Vec::new(); libraries2.push(f_stack.current_location.clone()); libraries2.append(&mut libraries.clone()); for lib in libraries2 { - let mut path = PathBuf::new(); + let mut path = PathBuf::new(); path.push(lib); path.push(name.clone()); let path = std::fs::canonicalize(path); match path { Err(_) => {} - , - Ok(path ) => { + Ok(path) => { if path.is_file() { - if !f_stack.black_paths.contains(&path) { - f_stack.stack.push(path.clone()); - } - return Result::Ok(path.to_str().unwrap().to_string()); - } + if !f_stack.black_paths.contains(&path) { + f_stack.stack.push(path.clone()); + } + return Result::Ok(path.to_str().unwrap().to_string()); + } } } } @@ -78,12 +81,7 @@ impl IncludesGraph { IncludesGraph::default() } - pub fn add_node( - &mut self, - path: PathBuf, - custom_gates_pragma: bool, - custom_gates_usage: bool - ) { + pub fn add_node(&mut self, path: PathBuf, custom_gates_pragma: bool, custom_gates_usage: bool) { self.nodes.push(IncludesNode { path, custom_gates_pragma }); if custom_gates_usage { self.custom_gates_nodes.push(self.nodes.len() - 1); @@ -94,8 +92,8 @@ impl IncludesGraph { let mut crr = PathBuf::new(); crr.push(old_path.clone()); let path = std::fs::canonicalize(crr) - .map_err(|_| FileOsError { path: old_path }) - .map_err(|e| FileOsError::produce_report(e))?; + .map_err(|_| Error::FileOs { path: old_path }) + .map_err(|e| Error::produce_report(e))?; let edges = self.adjacency.entry(path).or_insert(vec![]); edges.push(self.nodes.len() - 1); Ok(()) @@ -104,9 +102,7 @@ impl IncludesGraph { pub fn get_problematic_paths(&self) -> Vec> { let mut problematic_paths = Vec::new(); for from in &self.custom_gates_nodes { - problematic_paths.append( - &mut self.traverse(*from, Vec::new(), HashSet::new()) - ); + problematic_paths.append(&mut self.traverse(*from, Vec::new(), HashSet::new())); } problematic_paths } @@ -115,7 +111,7 @@ impl IncludesGraph { &self, from: usize, path: Vec, - traversed_edges: HashSet<(usize, usize)> + traversed_edges: HashSet<(usize, usize)>, ) -> Vec> { let mut problematic_paths = Vec::new(); let (from_path, using_pragma) = { @@ -139,9 +135,11 @@ impl IncludesGraph { new_traversed_edges.insert(edge); new_traversed_edges }; - problematic_paths.append( - &mut self.traverse(*to, new_path.clone(), new_traversed_edges) - ); + problematic_paths.append(&mut self.traverse( + *to, + new_path.clone(), + new_traversed_edges, + )); } } } @@ -149,11 +147,14 @@ impl IncludesGraph { } pub fn display_path(path: &Vec) -> String { - let path = path.iter().map(|file| -> String { - let file = format!("{}", file.display()); - let (_, file) = file.rsplit_once("/").unwrap(); - file.clone().to_string() - }).collect::>(); + let path = path + .iter() + .map(|file| -> String { + let file = format!("{}", file.display()); + let (_, file) = file.rsplit_once("/").unwrap(); + file.clone().to_string() + }) + .collect::>(); let mut path_covered = format!("{}", path[0]); for file in &path[1..] { path_covered = format!("{} -> {}", path_covered, file); diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 425cdf64e..97bc88540 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -4,8 +4,9 @@ use program_structure::expression_builders::*; use program_structure::ast::*; use program_structure::ast_shortcuts::{self,Symbol}; use std::str::FromStr; +use crate::errors::Error; -grammar; +grammar<'err>(file_id: usize, errors:&'err mut Vec); // ==================================================================== // Body diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 920c82267..abebf9610 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -17,12 +17,17 @@ use program_structure::file_definition::{FileLibrary}; use program_structure::program_archive::ProgramArchive; use std::path::{PathBuf, Path}; use std::str::FromStr; +use errors::Error; pub type Version = (usize, usize, usize); -pub fn find_file(crr_file : PathBuf, ext_link_libraries : Vec ) -> (bool, String, String, PathBuf, Vec){ +pub fn find_file( + crr_file: PathBuf, + ext_link_libraries: Vec, +) -> (bool, String, String, PathBuf, Vec) { let mut found = false; - let mut path = "".to_string(); let mut src = "".to_string(); + let mut path = "".to_string(); + let mut src = "".to_string(); let mut crr_str_file = crr_file.clone(); let mut reports = Vec::new(); let mut i = 0; @@ -32,15 +37,16 @@ pub fn find_file(crr_file : PathBuf, ext_link_libraries : Vec ) -> (boo p.push(aux); p.push(crr_file.clone()); crr_str_file = p; - match open_file(crr_str_file.clone()){ - Ok( (new_path, new_src)) => { - path = new_path; src = new_src; - found = true; - }, - Err(e ) => { - reports.push(e); + match open_file(crr_str_file.clone()) { + Ok((new_path, new_src)) => { + path = new_path; + src = new_src; + found = true; + } + Err(e) => { + reports.push(e); i = i + 1; - }, + } } } (found, path, src, crr_str_file, reports) @@ -61,14 +67,14 @@ pub fn run_parser( let mut ext_link_libraries = vec![Path::new("").to_path_buf()]; ext_link_libraries.append(&mut link_libraries2); while let Some(crr_file) = FileStack::take_next(&mut file_stack) { - let (found, path, src, crr_str_file, - reports) = find_file(crr_file, ext_link_libraries.clone()); + let (found, path, src, crr_str_file, reports) = + find_file(crr_file, ext_link_libraries.clone()); if !found { - return Result::Err((file_library.clone(),reports)); + return Result::Err((file_library.clone(), reports)); } let file_id = file_library.add_file(path.clone(), src.clone()); - let program = parser_logic::parse_file(&src, file_id) - .map_err(|e| (file_library.clone(), vec![e]))?; + let program = + parser_logic::parse_file(&src, file_id).map_err(|e| (file_library.clone(), e))?; if let Some(main) = program.main_component { main_components.push((file_id, main, program.custom_gates)); } @@ -76,31 +82,34 @@ pub fn run_parser( let includes = program.includes; definitions.push((file_id, program.definitions)); for include in includes { - let path_include = FileStack::add_include(&mut file_stack, include.clone(), &link_libraries.clone()) - .map_err(|e| (file_library.clone(), vec![e]))?; + let path_include = + FileStack::add_include(&mut file_stack, include.clone(), &link_libraries.clone()) + .map_err(|e| (file_library.clone(), vec![e]))?; includes_graph.add_edge(path_include).map_err(|e| (file_library.clone(), vec![e]))?; } warnings.append( &mut check_number_version( path.clone(), program.compiler_version, - parse_number_version(version) - ).map_err(|e| (file_library.clone(), vec![e]))? + parse_number_version(version), + ) + .map_err(|e| (file_library.clone(), vec![e]))?, ); if program.custom_gates { check_custom_gates_version( path, program.compiler_version, - parse_number_version(version) - ).map_err(|e| (file_library.clone(), vec![e]))? + parse_number_version(version), + ) + .map_err(|e| (file_library.clone(), vec![e]))? } } if main_components.len() == 0 { - let report = errors::NoMainError::produce_report(); + let report = Error::NoMain.produce_report(); Err((file_library, vec![report])) } else if main_components.len() > 1 { - let report = errors::MultipleMainError::produce_report(); + let report = Error::MultipleMain.produce_report(); Err((file_library, vec![report])) } else { let errors: ReportCollection = includes_graph.get_problematic_paths().iter().map(|path| @@ -117,63 +126,66 @@ pub fn run_parser( Err((file_library, errors)) } else { let (main_id, main_component, custom_gates) = main_components.pop().unwrap(); - let result_program_archive = - ProgramArchive::new(file_library, main_id, main_component, definitions, custom_gates); + let result_program_archive = ProgramArchive::new( + file_library, + main_id, + main_component, + definitions, + custom_gates, + ); match result_program_archive { - Err((lib, rep)) => { - Err((lib, rep)) - } - Ok(program_archive) => { - Ok((program_archive, warnings)) - } + Err((lib, rep)) => Err((lib, rep)), + Ok(program_archive) => Ok((program_archive, warnings)), } } } } fn open_file(path: PathBuf) -> Result<(String, String), Report> /* path, src */ { - use errors::FileOsError; use std::fs::read_to_string; let path_str = format!("{:?}", path); read_to_string(path) .map(|contents| (path_str.clone(), contents)) - .map_err(|_| FileOsError { path: path_str.clone() }) - .map_err(|e| FileOsError::produce_report(e)) + .map_err(|_| Error::FileOs { path: path_str.clone() }) + .map_err(|e| e.produce_report()) } fn parse_number_version(version: &str) -> Version { let version_splitted: Vec<&str> = version.split(".").collect(); - (usize::from_str(version_splitted[0]).unwrap(), usize::from_str(version_splitted[1]).unwrap(), usize::from_str(version_splitted[2]).unwrap()) + ( + usize::from_str(version_splitted[0]).unwrap(), + usize::from_str(version_splitted[1]).unwrap(), + usize::from_str(version_splitted[2]).unwrap(), + ) } fn check_number_version( file_path: String, version_file: Option, - version_compiler: Version + version_compiler: Version, ) -> Result { - use errors::{CompilerVersionError, NoCompilerVersionWarning}; if let Some(required_version) = version_file { - if required_version.0 == version_compiler.0 - && (required_version.1 < version_compiler.1 || - (required_version.1 == version_compiler.1 && required_version.2 <= version_compiler.2)) { + if required_version.0 == version_compiler.0 + && (required_version.1 < version_compiler.1 + || (required_version.1 == version_compiler.1 + && required_version.2 <= version_compiler.2)) + { Ok(vec![]) } else { - let report = CompilerVersionError::produce_report( - CompilerVersionError{ - path: file_path, - required_version, - version: version_compiler - } - ); + let report = Error::CompilerVersion { + path: file_path, + required_version, + version: version_compiler, + } + .produce_report(); Err(report) } } else { - let report = NoCompilerVersionWarning::produce_report( - NoCompilerVersionWarning{ + let report = + errors::NoCompilerVersionWarning::produce_report(errors::NoCompilerVersionWarning { path: file_path, - version: version_compiler - } - ); + version: version_compiler, + }); Ok(vec![report]) } } @@ -181,13 +193,17 @@ fn check_number_version( fn check_custom_gates_version( file_path: String, version_file: Option, - version_compiler: Version + version_compiler: Version, ) -> Result<(), Report> { let custom_gates_version: Version = (2, 0, 6); if let Some(required_version) = version_file { - if required_version.0 < custom_gates_version.0 || - (required_version.0 == custom_gates_version.0 && required_version.1 < custom_gates_version.1) || - (required_version.0 == custom_gates_version.0 && required_version.1 == custom_gates_version.1 && required_version.2 < custom_gates_version.2) { + if required_version.0 < custom_gates_version.0 + || (required_version.0 == custom_gates_version.0 + && required_version.1 < custom_gates_version.1) + || (required_version.0 == custom_gates_version.0 + && required_version.1 == custom_gates_version.1 + && required_version.2 < custom_gates_version.2) + { let report = Report::error( format!( "File {} requires at least version {:?} to use custom templates (currently {:?})", @@ -200,9 +216,13 @@ fn check_custom_gates_version( return Err(report); } } else { - if version_compiler.0 < custom_gates_version.0 || - (version_compiler.0 == custom_gates_version.0 && version_compiler.1 < custom_gates_version.1) || - (version_compiler.0 == custom_gates_version.0 && version_compiler.1 == custom_gates_version.1 && version_compiler.2 < custom_gates_version.2) { + if version_compiler.0 < custom_gates_version.0 + || (version_compiler.0 == custom_gates_version.0 + && version_compiler.1 < custom_gates_version.1) + || (version_compiler.0 == custom_gates_version.0 + && version_compiler.1 == custom_gates_version.1 + && version_compiler.2 < custom_gates_version.2) + { let report = Report::error( format!( "File {} does not include pragma version and the compiler version (currently {:?}) should be at least {:?} to use custom templates", diff --git a/parser/src/parser_logic.rs b/parser/src/parser_logic.rs index d697f6dba..435b91961 100644 --- a/parser/src/parser_logic.rs +++ b/parser/src/parser_logic.rs @@ -1,10 +1,10 @@ -use super::errors::{ParsingError, UnclosedCommentError}; +use super::errors::Error; use super::lang; use program_structure::ast::AST; -use program_structure::error_definition::Report; +use program_structure::error_definition::{ReportCollection}; use program_structure::file_definition::FileID; -pub fn preprocess(expr: &str, file_id: FileID) -> Result { +pub fn preprocess(expr: &str, file_id: FileID) -> Result { let mut pp = String::new(); let mut state = 0; let mut loc = 0; @@ -63,8 +63,7 @@ pub fn preprocess(expr: &str, file_id: FileID) -> Result { pp.push(' '); } } - None => { - } + None => {} } } (_, c) => { @@ -74,37 +73,50 @@ pub fn preprocess(expr: &str, file_id: FileID) -> Result { } } } - if state == 2{ - let error = - UnclosedCommentError { location: block_start..block_start, file_id }; - Err(UnclosedCommentError::produce_report(error)) - } - else{ + if state == 2 { + Err(vec![ + Error::UnclosedComment { location: block_start..block_start, file_id }.produce_report() + ]) + } else { Ok(pp) } } -pub fn parse_file(src: &str, file_id: FileID) -> Result { +pub fn parse_file(src: &str, file_id: FileID) -> Result { use lalrpop_util::ParseError::*; - lang::ParseAstParser::new() - .parse(&preprocess(src, file_id)?) + + let mut errors = Vec::new(); + let preprocess = preprocess(src, file_id)?; + + let ast = lang::ParseAstParser::new() + .parse(file_id, &mut errors, &preprocess) + // TODO: is this always fatal? .map_err(|parse_error| match parse_error { - InvalidToken { location } => ParsingError { + InvalidToken { location } => Error::GenericParsing { file_id, msg: format!("{:?}", parse_error), location: location..location, }, - UnrecognizedToken { ref token, .. } => ParsingError { + UnrecognizedToken { ref token, .. } => Error::GenericParsing { file_id, msg: format!("{:?}", parse_error), location: token.0..token.2, }, - ExtraToken { ref token } => ParsingError { + ExtraToken { ref token } => Error::GenericParsing { file_id, msg: format!("{:?}", parse_error), location: token.0..token.2, }, - _ => ParsingError { file_id, msg: format!("{:?}", parse_error), location: 0..0 }, + _ => { + Error::GenericParsing { file_id, msg: format!("{:?}", parse_error), location: 0..0 } + } }) - .map_err(|parsing_error| ParsingError::produce_report(parsing_error)) + .map_err(Error::produce_report) + .map_err(|e| vec![e])?; + + if !errors.is_empty() { + return Err(errors.into_iter().map(|e| e.produce_report()).collect()); + } + + Ok(ast) } From 3094e44e684cb1dc9383c3563d58a2dd0a56ca39 Mon Sep 17 00:00:00 2001 From: Ricardo Maurizio Paul Date: Tue, 20 Sep 2022 10:13:49 +0200 Subject: [PATCH 02/70] Add pragma and include error recovery --- parser/src/errors.rs | 13 +-- parser/src/lang.lalrpop | 82 +++++++++++++------ .../src/abstract_syntax_tree/ast.rs | 61 +++++++++----- 3 files changed, 99 insertions(+), 57 deletions(-) diff --git a/parser/src/errors.rs b/parser/src/errors.rs index da203910a..5e1c286b7 100644 --- a/parser/src/errors.rs +++ b/parser/src/errors.rs @@ -40,10 +40,6 @@ pub enum Error { location: FileLocation, file_id: FileID, }, - IllegalStatement { - location: FileLocation, - file_id: FileID, - }, } impl Error { @@ -116,14 +112,7 @@ impl Error { Report::error("illegal expression".to_string(), ReportCode::ParseFail); report.add_primary(location, file_id, "here".to_string()); report - } - - Error::IllegalStatement{location, file_id} => { - let mut report = - Report::error("illegal statement".to_string(), ReportCode::ParseFail); - report.add_primary(location, file_id, "here".to_string()); - report - } + } } } } diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 97bc88540..13cc31606 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -5,12 +5,16 @@ use program_structure::ast::*; use program_structure::ast_shortcuts::{self,Symbol}; use std::str::FromStr; use crate::errors::Error; +use lalrpop_util::ParseError; grammar<'err>(file_id: usize, errors:&'err mut Vec); -// ==================================================================== -// Body -// ==================================================================== +CommaSepList:Vec = { + ",")*> => { + e.push(t); + e + }, +}; // A identifier list is a comma separated list of identifiers IdentifierListDef : Vec = { @@ -21,34 +25,66 @@ IdentifierListDef : Vec = { } }; -// Pragma is included at the start of the file. -// Their structure is the following: pragma circom "version of the compiler" -ParsePragma : Version = { // maybe change to usize instead of BigInt - "pragma circom" ";" - => version, -}; - -// Pragma to indicate that we are allowing the definition of custom templates. -ParseCustomGates : () = { - "pragma" "custom_templates" ";" => () +Semicolon:() = { + ";" => (), + => errors.push(Error::MissingSemicolon) } +// ==================================================================== +// Body +// ==================================================================== + +// Pragmas are included at the start of the file, +// their structure is the following: +// 'pragma circom "version of the compiler"' to indicate the version +// or +// 'pragma custom_templates' to indicate that we are allowing the definition of custom gates. + +ParsePragma:Pragma = { + // version + "pragma" "circom" Semicolon => Pragma::Version(<>), + "pragma" "circom" Semicolon => match <>.error { + ParseError::UnrecognizedToken { ref token, .. } => { + errors.push(Error::UnrecognizedVersion{location: token.0..token.2, file_id}); + Pragma::Unrecognized + } + _ => unreachable!(), + }, + // custom templates + "pragma" "custom_templates" Semicolon => Pragma::CustomGates, + // unrecognized + "pragma" Semicolon => match <>.error { + ParseError::UnrecognizedToken { ref token, .. } => { + errors.push(Error::UnrecognizedPragma{location: token.0..token.2, file_id}); + Pragma::Unrecognized + } + _ => unreachable!(), + }, +}; + // Includes are added at the start of the file. -// Their structure is the following: #include "path to the file" -ParseInclude : String = { - "include" ";" - => file, +// Their structure is the following:#include "path to the file" +ParseInclude:String = { + "include" Semicolon => <>, + "include" Semicolon => { + match <>.error { + ParseError::UnrecognizedToken { ref token, .. } => { + errors.push(Error::UnrecognizedInclude{location: token.0..token.2, file_id}); + } + _ => unreachable!(), + } + "".to_owned() + }, }; // Parsing a program requires: -// Parsing the version pragma, if there is one -// Parsing the custom templates pragma, if there is one -// Parsing "includes" instructions, if there is anyone +// Parsing pragmas +// Parsing "includes" // Parsing function and template definitions // Parsing the declaration of the main component -pub ParseAst : AST = { - - => AST::new(Meta::new(s,e), version, custom_gates.is_some(), includes, definitions, main), +pub ParseAst:AST = { + + => AST::new(Meta::new(s,e), pragmas, includes, definitions, main), }; // ==================================================================== diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index 8c6fe2d77..a2ccefb1f 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -2,6 +2,13 @@ use crate::file_definition::FileLocation; use num_bigint::BigInt; use serde_derive::{Deserialize, Serialize}; +#[derive(Clone, Debug)] +pub enum Pragma { + Version(Version), + CustomGates, + Unrecognized, +} + pub trait FillMeta { fn fill(&mut self, file_id: usize, elem_id: &mut usize); } @@ -11,10 +18,8 @@ pub fn build_main_component(public: Vec, call: Expression) -> MainCompon (public, call) } - pub type Version = (usize, usize, usize); - #[derive(Clone)] pub struct Meta { pub elem_id: usize, @@ -86,26 +91,47 @@ pub struct AST { pub definitions: Vec, pub main_component: Option, } + impl AST { pub fn new( meta: Meta, - compiler_version: Option, - custom_gates: bool, + pragmas: Vec, includes: Vec, definitions: Vec, main_component: Option, ) -> AST { - let custom_gates_declared = definitions.iter().any( - |definition| matches!(definition, Definition::Template { is_custom_gate: true, .. }) - ); + let mut custom_gates = None; + let mut compiler_version = None; + + for p in pragmas { + match p { + // TODO: don't panic + Pragma::Version(ver) => match compiler_version { + Some(_) => panic!("multiple circom pragmas"), + None => compiler_version = Some(ver), + }, + Pragma::CustomGates => match custom_gates { + Some(_) => panic!("multiple custom gates pragmas"), + None => custom_gates = Some(true), + }, + // unrecognized + // TODO: maybe warn? + _ => (), + } + } + + let custom_gates_declared = definitions.iter().any(|definition| { + matches!(definition, Definition::Template { is_custom_gate: true, .. }) + }); + AST { meta, compiler_version, - custom_gates, + custom_gates: custom_gates.unwrap_or(false), custom_gates_declared, includes, definitions, - main_component + main_component, } } } @@ -138,15 +164,7 @@ pub fn build_template( parallel: bool, is_custom_gate: bool, ) -> Definition { - Definition::Template { - meta, - name, - args, - arg_location, - body, - parallel, - is_custom_gate, - } + Definition::Template { meta, name, args, arg_location, body, parallel, is_custom_gate } } pub fn build_function( @@ -254,7 +272,7 @@ pub enum Expression { if_true: Box, if_false: Box, }, - ParallelOp{ + ParallelOp { meta: Meta, rhe: Box, }, @@ -275,9 +293,9 @@ pub enum Expression { }, UniformArray { meta: Meta, - value: Box, + value: Box, dimension: Box, - } + }, } #[derive(Clone)] @@ -351,7 +369,6 @@ pub fn build_log_expression(expr: Expression) -> LogArgument { LogArgument::LogExp(expr) } - #[derive(Default, Clone)] pub struct TypeKnowledge { reduces_to: Option, From b684a36b5f89c561568c485345bdaad224d16a17 Mon Sep 17 00:00:00 2001 From: Ricardo Maurizio Paul Date: Tue, 20 Sep 2022 10:19:54 +0200 Subject: [PATCH 03/70] Expression and semicolon recovery --- parser/src/lang.lalrpop | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 13cc31606..2d999d3ce 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -27,7 +27,7 @@ IdentifierListDef : Vec = { Semicolon:() = { ";" => (), - => errors.push(Error::MissingSemicolon) + ! => errors.push(Error::MissingSemicolon) } // ==================================================================== @@ -98,7 +98,7 @@ ParsePublicList : Vec = { }; pub ParseMainComponent : MainComponent = { - "component" "main" "=" ";" + "component" "main" "=" Semicolon => match public_list { None => build_main_component(Vec::new(),init), Some(list) => build_main_component(list,init) @@ -332,42 +332,42 @@ ParseStatement1 : Statement = { ParseStatement2 }; ParseStatement2 : Statement = { - "for" "(" ";" ";" ")" + "for" "(" Semicolon Semicolon ")" => ast_shortcuts::for_into_while(Meta::new(s,e),init,cond,step,body), - "for" "(" ";" ";" ")" + "for" "(" Semicolon Semicolon ")" => ast_shortcuts::for_into_while(Meta::new(s,e),init,cond,step,body), "while" "(" ")" => build_while_block(Meta::new(s,e),cond,stmt), - "return" ";" + "return" Semicolon => build_return(Meta::new(s,e),value), - ";" + Semicolon => subs, - "===" ";" + "===" Semicolon => build_constraint_equality(Meta::new(s,e),lhe,rhe), ParseStatementLog, - "assert" "(" ")" ";" + "assert" "(" ")" Semicolon => build_assert(Meta::new(s,e),arg), ParseBlock }; ParseStatementLog : Statement = { - "log" "(" ")" ";" + "log" "(" ")" Semicolon => build_log_call(Meta::new(s,e),args), - "log" "(" ")" ";" + "log" "(" ")" Semicolon => build_log_call(Meta::new(s,e),Vec::new()), }; ParseStatement3 : Statement = { - ";" + Semicolon => dec, ParseStatement @@ -535,9 +535,17 @@ Expression0: Expression = { => build_number(Meta::new(s,e),value), - "(" ")" -}; + "(" ")", + => match <>.error { + ParseError::UnrecognizedToken { ref token, .. } => { + errors.push(Error::IllegalExpression{location: token.0..token.2, file_id}); + // doesn't matter + build_number(Meta::new(0,0),BigInt::from(0)) + } + _ => unreachable!(), + } +}; // ==================================================================== // Terminals From d27b1e95f3cd5bbdcd6f2ea529b5684215f31a0d Mon Sep 17 00:00:00 2001 From: ermyas Date: Wed, 11 Jan 2023 13:25:49 +1100 Subject: [PATCH 04/70] Fix broken link --- mkdocs/docs/circom-language/circom-insight/unknowns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs/docs/circom-language/circom-insight/unknowns.md b/mkdocs/docs/circom-language/circom-insight/unknowns.md index 029b67661..20f08e7fc 100644 --- a/mkdocs/docs/circom-language/circom-insight/unknowns.md +++ b/mkdocs/docs/circom-language/circom-insight/unknowns.md @@ -106,4 +106,4 @@ component main = A(); In the code above, a constraint is defined in a for-loop with a counting condition to an unknown value `in` (as signals are always considered unknown). -For additional details, see [Control Flow](../control-flow). +For additional details, see [Control Flow](../../control-flow). From b2c24b04119d41c65b3bb1f196ea319dd6eab856 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Wed, 11 Jan 2023 17:42:53 +0100 Subject: [PATCH 05/70] Adding check to detect components that are created but do not receive all their inputs --- .../component_representation.rs | 10 +++++++ .../src/environment_utils/environment.rs | 15 +++++++++++ constraint_generation/src/execute.rs | 26 ++++++++++++++++++- program_structure/src/utils/environment.rs | 3 +++ program_structure/src/utils/memory_slice.rs | 11 ++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/constraint_generation/src/environment_utils/component_representation.rs b/constraint_generation/src/environment_utils/component_representation.rs index c841fb431..cb1d853ea 100644 --- a/constraint_generation/src/environment_utils/component_representation.rs +++ b/constraint_generation/src/environment_utils/component_representation.rs @@ -2,10 +2,12 @@ use super::slice_types::{MemoryError, SignalSlice, SliceCapacity,TagInfo}; use crate::execution_data::type_definitions::NodePointer; use crate::execution_data::ExecutedProgram; use std::collections::{BTreeMap,HashMap, HashSet}; +use crate::ast::Meta; pub struct ComponentRepresentation { pub node_pointer: Option, is_parallel: bool, + pub meta: Option, unassigned_inputs: HashMap, unassigned_tags: HashSet, to_assign_inputs: Vec<(String, Vec, Vec)>, @@ -29,6 +31,7 @@ impl Default for ComponentRepresentation { outputs: HashMap::new(), outputs_tags: BTreeMap::new(), is_initialized: false, + meta: Option::None, } } } @@ -45,6 +48,7 @@ impl Clone for ComponentRepresentation { outputs: self.outputs.clone(), outputs_tags: self.outputs_tags.clone(), is_initialized: self.is_initialized, + meta : self.meta.clone(), } } } @@ -56,6 +60,7 @@ impl ComponentRepresentation { prenode_pointer: NodePointer, scheme: &ExecutedProgram, is_anonymous_component: bool, + meta: &Meta, ) -> Result<(), MemoryError>{ if !is_anonymous_component && component.is_preinitialized() { return Result::Err(MemoryError::AssignmentError); @@ -97,6 +102,7 @@ impl ComponentRepresentation { outputs: HashMap::new(), is_initialized: false, is_parallel, + meta: Some(meta.clone()), }; Result::Ok(()) } @@ -302,4 +308,8 @@ impl ComponentRepresentation { self.unassigned_tags.is_empty() } + pub fn has_unassigned_inputs(&self) -> bool{ + !self.unassigned_inputs.is_empty() + } + } diff --git a/constraint_generation/src/environment_utils/environment.rs b/constraint_generation/src/environment_utils/environment.rs index 0b2684480..45bcc56de 100644 --- a/constraint_generation/src/environment_utils/environment.rs +++ b/constraint_generation/src/environment_utils/environment.rs @@ -7,6 +7,8 @@ use super::slice_types::{ TagInfo }; use super::{ArithmeticExpression, CircomEnvironment, CircomEnvironmentError}; +use program_structure::memory_slice::MemoryError; +use crate::ast::Meta; pub type ExecutionEnvironmentError = CircomEnvironmentError; pub type ExecutionEnvironment = CircomEnvironment; @@ -54,3 +56,16 @@ pub fn environment_shortcut_add_variable( let slice = AExpressionSlice::new_with_route(dimensions, &ArithmeticExpression::default()); environment.add_variable(variable_name, (TagInfo::new(), slice)); } + +pub fn environment_check_all_components_assigned(environment: &ExecutionEnvironment)-> Result<(), (MemoryError, Meta)>{ + use program_structure::memory_slice::MemorySlice; + for (name, slice) in environment.get_components_ref(){ + for i in 0..MemorySlice::get_number_of_cells(slice){ + let component = MemorySlice::get_reference_to_single_value_by_index_or_break(slice, i); + if component.is_preinitialized() && component.has_unassigned_inputs(){ + return Result::Err((MemoryError::MissingInputs(name.clone()), component.meta.as_ref().unwrap().clone())); + } + } + } + Result::Ok(()) +} \ No newline at end of file diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index a19e8ab9d..cadb16ca8 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -3,6 +3,7 @@ use super::environment_utils::{ environment_shortcut_add_component, environment_shortcut_add_input, environment_shortcut_add_intermediate, environment_shortcut_add_output, environment_shortcut_add_variable, ExecutionEnvironment, ExecutionEnvironmentError, + environment_check_all_components_assigned }, slice_types::{ AExpressionSlice, ArithmeticExpression as ArithmeticExpressionGen, ComponentRepresentation, @@ -1060,6 +1061,7 @@ fn perform_assign( prenode_pointer, &runtime.exec_program, is_anonymous_component, + meta ); treat_result_with_memory_error_void( memory_result, @@ -1717,6 +1719,20 @@ fn execute_template_call( true )?; debug_assert!(ret.is_none()); + + let result_check_components = environment_check_all_components_assigned(&runtime.environment); + match result_check_components{ + Err((error, meta)) =>{ + treat_result_with_memory_error_void( + Err(error), + &meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + }, + Ok(_) => {}, + } + let new_node = node_wrap.unwrap(); let analysis = std::mem::replace(&mut runtime.analysis, analysis); let node_pointer = runtime.exec_program.add_node_to_scheme(new_node, analysis); @@ -2059,6 +2075,10 @@ fn treat_result_with_memory_error_void( "Tag value has not been previously initialized".to_string(), RuntimeError) , + MemoryError::MissingInputs(name) => Report::error( + format!("Component {} is created but not all its inputs are initialized", name), + RuntimeError, + ) }; add_report_to_runtime(report, meta, runtime_errors, call_trace); Result::Err(()) @@ -2114,8 +2134,12 @@ fn treat_result_with_memory_error( } MemoryError::MismatchedDimensionsWeak => { - Report::error("Entra aqui".to_string(), RuntimeError) + unreachable!() }, + MemoryError::MissingInputs(name) => Report::error( + format!("Component {} is created but not all its inputs are initialized", name), + RuntimeError, + ) }; add_report_to_runtime(report, meta, runtime_errors, call_trace); diff --git a/program_structure/src/utils/environment.rs b/program_structure/src/utils/environment.rs index 7acc2fbd9..0215dce61 100644 --- a/program_structure/src/utils/environment.rs +++ b/program_structure/src/utils/environment.rs @@ -234,6 +234,9 @@ where assert!(self.has_component(symbol), "Method call in file {} line {}", file, line); self.components.get_mut(symbol).unwrap() } + pub fn get_components_ref(&self)-> &HashMap{ + &self.components + } } impl RawEnvironment diff --git a/program_structure/src/utils/memory_slice.rs b/program_structure/src/utils/memory_slice.rs index e0a81660f..7451f3fa6 100644 --- a/program_structure/src/utils/memory_slice.rs +++ b/program_structure/src/utils/memory_slice.rs @@ -1,5 +1,6 @@ use num_bigint_dig::BigInt; use std::fmt::{Display, Formatter}; + pub enum MemoryError { OutOfBoundsError, AssignmentError, @@ -12,6 +13,7 @@ pub enum MemoryError { AssignmentTagTwice, AssignmentTagInput, TagValueNotInitializedAccess, + MissingInputs(String) } pub type SliceCapacity = usize; pub type SimpleSlice = MemorySlice; @@ -276,6 +278,15 @@ impl MemorySlice { } Result::Ok(&memory_slice.values[index]) } + pub fn get_reference_to_single_value_by_index_or_break<'a>( + memory_slice: &'a MemorySlice, + index: usize, + ) -> &'a C { + if index > MemorySlice::get_number_of_cells(memory_slice) { + unreachable!("The index is too big for the slice"); + } + &memory_slice.values[index] + } pub fn get_mut_reference_to_single_value<'a>( memory_slice: &'a mut MemorySlice, access: &[SliceCapacity], From 90b8fb5a22c11cfd47df98d2a690547dfa3bb586 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Mon, 16 Jan 2023 12:09:10 +0100 Subject: [PATCH 06/70] avoiding side effects in out of bounds log operations --- .../src/wasm_elements/wasm_code_generator.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/code_producers/src/wasm_elements/wasm_code_generator.rs b/code_producers/src/wasm_elements/wasm_code_generator.rs index 228f862d4..a3175c06e 100644 --- a/code_producers/src/wasm_elements/wasm_code_generator.rs +++ b/code_producers/src/wasm_elements/wasm_code_generator.rs @@ -1127,13 +1127,12 @@ pub fn copy_fr_in_shared_rw_memory_generator(producer: &WASMProducer) -> Vec Vec { instructions.push(set_constant(&producer.get_signal_memory_start().to_string())); instructions.push(add32()); // address of the signal in the signal Memory instructions.push(set_local("$c")); + let pos = producer.get_shared_rw_memory_start() - 8; + instructions.push(set_constant(&pos.to_string())); instructions.push(get_local("$c")); + instructions.push(call("$Fr_copy")); + instructions.push(set_constant(&pos.to_string())); instructions.push(call("$Fr_toLongNormal")); - instructions.push(get_local("$c")); - instructions.push(set_constant("8")); - instructions.push(add32()); - instructions.push(set_constant(&producer.get_shared_rw_memory_start().to_string())); - instructions.push(call("$Fr_int_copy")); instructions.push(")".to_string()); instructions } From 7015e3285e02c19344279f8a17826b1eb4162fc4 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Mon, 16 Jan 2023 12:13:10 +0100 Subject: [PATCH 07/70] improving error messages: invalid access and invalid assignment --- .../component_representation.rs | 22 ++++-- .../src/environment_utils/slice_types.rs | 2 +- constraint_generation/src/execute.rs | 77 +++++++++++++++---- program_structure/src/utils/memory_slice.rs | 15 +++- 4 files changed, 88 insertions(+), 28 deletions(-) diff --git a/constraint_generation/src/environment_utils/component_representation.rs b/constraint_generation/src/environment_utils/component_representation.rs index cb1d853ea..23c151b10 100644 --- a/constraint_generation/src/environment_utils/component_representation.rs +++ b/constraint_generation/src/environment_utils/component_representation.rs @@ -1,4 +1,4 @@ -use super::slice_types::{MemoryError, SignalSlice, SliceCapacity,TagInfo}; +use super::slice_types::{MemoryError, TypeInvalidAccess, TypeAssignmentError, SignalSlice, SliceCapacity,TagInfo}; use crate::execution_data::type_definitions::NodePointer; use crate::execution_data::ExecutedProgram; use std::collections::{BTreeMap,HashMap, HashSet}; @@ -63,7 +63,7 @@ impl ComponentRepresentation { meta: &Meta, ) -> Result<(), MemoryError>{ if !is_anonymous_component && component.is_preinitialized() { - return Result::Err(MemoryError::AssignmentError); + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::MultipleAssignments)); } let possible_node = ExecutedProgram::get_prenode(scheme, prenode_pointer); assert!(possible_node.is_some()); @@ -176,14 +176,14 @@ impl ComponentRepresentation { pub fn get_signal(&self, signal_name: &str) -> Result<(&TagInfo, &SignalSlice), MemoryError> { if self.node_pointer.is_none() { - return Result::Err(MemoryError::InvalidAccess); + return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedComponent)); } if self.outputs.contains_key(signal_name) && !self.unassigned_inputs.is_empty() { - return Result::Err(MemoryError::InvalidAccess); + return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::MissingInputs)); } if !self.is_initialized { - return Result::Err(MemoryError::InvalidAccess); + return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedComponent)); } let slice = if self.inputs.contains_key(signal_name) { @@ -229,8 +229,11 @@ impl ComponentRepresentation { // We copy tags in any case, complete or incomplete assignment // The values of the tags must be the same than the ones stored before - let tags_input = component.inputs_tags.get_mut(signal_name).unwrap(); + if !component.inputs_tags.contains_key(signal_name){ + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::AssignmentOutput)); + } + let tags_input = component.inputs_tags.get_mut(signal_name).unwrap(); for (t, value) in tags_input{ if !tags.contains_key(t){ return Result::Err(MemoryError::AssignmentMissingTags); @@ -257,7 +260,10 @@ impl ComponentRepresentation { access: &[SliceCapacity], slice_route: &[SliceCapacity], ) -> Result<(), MemoryError> { - + + if !component.inputs.contains_key(signal_name){ + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::AssignmentOutput)); + } let inputs_response = component.inputs.get_mut(signal_name).unwrap(); let signal_previous_value = SignalSlice::access_values( inputs_response, @@ -276,7 +282,7 @@ impl ComponentRepresentation { for i in 0..SignalSlice::get_number_of_cells(&signal_previous_value){ let signal_was_assigned = SignalSlice::access_value_by_index(&signal_previous_value, i)?; if signal_was_assigned { - return Result::Err(MemoryError::AssignmentError); + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::MultipleAssignments)); } } diff --git a/constraint_generation/src/environment_utils/slice_types.rs b/constraint_generation/src/environment_utils/slice_types.rs index 5d1052b5a..ead56b99d 100644 --- a/constraint_generation/src/environment_utils/slice_types.rs +++ b/constraint_generation/src/environment_utils/slice_types.rs @@ -1,6 +1,6 @@ pub use super::component_representation::ComponentRepresentation; pub use super::memory_slice::MemorySlice; -pub use super::memory_slice::{MemoryError, SliceCapacity}; +pub use super::memory_slice::{MemoryError, TypeInvalidAccess, TypeAssignmentError, SliceCapacity}; pub use circom_algebra::algebra::ArithmeticExpression; pub use num_bigint::BigInt; use std::collections::BTreeMap; diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index cadb16ca8..ae03fd224 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -7,7 +7,8 @@ use super::environment_utils::{ }, slice_types::{ AExpressionSlice, ArithmeticExpression as ArithmeticExpressionGen, ComponentRepresentation, - ComponentSlice, MemoryError, MemorySlice, SignalSlice, SliceCapacity, TagInfo + ComponentSlice, MemoryError, TypeInvalidAccess, TypeAssignmentError, MemorySlice, + SignalSlice, SliceCapacity, TagInfo }, }; @@ -997,7 +998,7 @@ fn perform_assign( &runtime.call_trace, )?; if signal_was_assigned { - let access_response = Result::Err(MemoryError::AssignmentError); + let access_response = Result::Err(MemoryError::AssignmentError(TypeAssignmentError::MultipleAssignments)); treat_result_with_memory_error( access_response, meta, @@ -1498,7 +1499,7 @@ fn signal_to_arith(symbol: String, slice: SignalSlice) -> Result { let report = match memory_error { - MemoryError::InvalidAccess => { - Report::error("Exception caused by invalid access".to_string(), RuntimeError) + MemoryError::InvalidAccess(type_invalid_access) => { + match type_invalid_access{ + TypeInvalidAccess::MissingInputs =>{ + Report::error("Exception caused by invalid access: trying to access to an output signal of a component with not all its inputs initialized".to_string(), + RuntimeError) + }, + TypeInvalidAccess::NoInitializedComponent =>{ + Report::error("Exception caused by invalid access: trying to access to a component that is not initialized" .to_string(), + RuntimeError) + }, + TypeInvalidAccess::BadDimensions =>{ + Report::error("Exception caused by invalid access: invalid dimensions" .to_string(), + RuntimeError) + } + } } - MemoryError::AssignmentError => Report::error( - "Exception caused by invalid assignment".to_string(), - RuntimeError, - ), + MemoryError::AssignmentError(type_asig_error) => { + match type_asig_error{ + TypeAssignmentError::MultipleAssignments =>{ + Report::error("Exception caused by invalid assignment: signal already assigned".to_string(), + RuntimeError) + }, + TypeAssignmentError::AssignmentOutput =>{ + Report::error("Exception caused by invalid assignment: trying to assign a value to an output signal of a component".to_string(), + RuntimeError) + }, + } + }, MemoryError::OutOfBoundsError => { Report::error("Out of bounds exception".to_string(), RuntimeError) }, @@ -2097,13 +2119,34 @@ fn treat_result_with_memory_error( Result::Ok(c) => Result::Ok(c), Result::Err(memory_error) => { let report = match memory_error { - MemoryError::InvalidAccess => { - Report::error("Exception caused by invalid access".to_string(), RuntimeError) - } - MemoryError::AssignmentError => Report::error( - "Exception caused by invalid assignment".to_string(), - RuntimeError, - ), + MemoryError::InvalidAccess(type_invalid_access) => { + match type_invalid_access{ + TypeInvalidAccess::MissingInputs =>{ + Report::error("Exception caused by invalid access: trying to access to an output signal of a component with not all its inputs initialized".to_string(), + RuntimeError) + }, + TypeInvalidAccess::NoInitializedComponent =>{ + Report::error("Exception caused by invalid access: trying to access to a component that is not initialized" .to_string(), + RuntimeError) + }, + TypeInvalidAccess::BadDimensions =>{ + Report::error("Exception caused by invalid access: invalid dimensions" .to_string(), + RuntimeError) + } + } + }, + MemoryError::AssignmentError(type_asig_error) => { + match type_asig_error{ + TypeAssignmentError::MultipleAssignments =>{ + Report::error("Exception caused by invalid assignment: signal already assigned".to_string(), + RuntimeError) + }, + TypeAssignmentError::AssignmentOutput =>{ + Report::error("Exception caused by invalid assignment: trying to assign a value to an output signal of a component".to_string(), + RuntimeError) + }, + } + }, MemoryError::AssignmentMissingTags => Report::error( "Invalid assignment: missing tags required by input signal".to_string(), RuntimeError, diff --git a/program_structure/src/utils/memory_slice.rs b/program_structure/src/utils/memory_slice.rs index 7451f3fa6..8ac99ea45 100644 --- a/program_structure/src/utils/memory_slice.rs +++ b/program_structure/src/utils/memory_slice.rs @@ -1,10 +1,21 @@ use num_bigint_dig::BigInt; use std::fmt::{Display, Formatter}; +pub enum TypeInvalidAccess{ + MissingInputs, + NoInitializedComponent, + BadDimensions +} + +pub enum TypeAssignmentError{ + MultipleAssignments, + AssignmentOutput +} + pub enum MemoryError { OutOfBoundsError, - AssignmentError, - InvalidAccess, + AssignmentError(TypeAssignmentError), + InvalidAccess(TypeInvalidAccess), UnknownSizeDimension, MismatchedDimensions, MismatchedDimensionsWeak, From 93fc176d1189f657c39c3679c361d0d95435508a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Isabel=20M=C3=A1rquez?= Date: Mon, 16 Jan 2023 14:23:53 +0100 Subject: [PATCH 08/70] Fixing a problem with the use of integer division and ===, <== operators. If an integer division is involved in the expression of one of these operators, then we consider the expression as no quadratic. --- circom_algebra/src/algebra.rs | 45 ----------------------------------- 1 file changed, 45 deletions(-) diff --git a/circom_algebra/src/algebra.rs b/circom_algebra/src/algebra.rs index 8feccc545..7ddeb6d6c 100644 --- a/circom_algebra/src/algebra.rs +++ b/circom_algebra/src/algebra.rs @@ -243,18 +243,6 @@ impl ArithmeticExpression { debug_assert!(ArithmeticExpression::valid_hashmap_for_expression(coefficients)); Result::Ok(()) } - fn idivide_coefficients_by_constant( - constant: &BigInt, - coefficients: &mut HashMap, - field: &BigInt, - ) -> Result<(), ArithmeticError> { - debug_assert!(ArithmeticExpression::valid_hashmap_for_expression(coefficients)); - for value in coefficients.values_mut() { - *value = modular_arithmetic::idiv(value, constant, field)?; - } - debug_assert!(ArithmeticExpression::valid_hashmap_for_expression(coefficients)); - Result::Ok(()) - } pub fn add( left: &ArithmeticExpression, @@ -519,39 +507,6 @@ impl ArithmeticExpression { let value = modular_arithmetic::idiv(value_0, value_1, field)?; Result::Ok(Number { value }) } - (Signal { symbol }, Number { value }) => { - let mut coefficients = HashMap::new(); - ArithmeticExpression::initialize_hashmap_for_expression(&mut coefficients); - ArithmeticExpression::add_symbol_to_coefficients( - symbol, - &BigInt::from(1), - &mut coefficients, - field, - ); - ArithmeticExpression::idivide_coefficients_by_constant( - value, - &mut coefficients, - field, - )?; - Result::Ok(Linear { coefficients }) - } - (Linear { coefficients }, Number { value }) => { - let mut coefficients = coefficients.clone(); - ArithmeticExpression::idivide_coefficients_by_constant( - value, - &mut coefficients, - field, - )?; - Result::Ok(Linear { coefficients }) - } - (Quadratic { a, b, c }, Number { value }) => { - let mut a = a.clone(); - let b = b.clone(); - let mut c = c.clone(); - ArithmeticExpression::idivide_coefficients_by_constant(value, &mut a, field)?; - ArithmeticExpression::idivide_coefficients_by_constant(value, &mut c, field)?; - Result::Ok(Quadratic { a, b, c }) - } _ => Result::Ok(NonQuadratic), } } From 68f327362ed13245afff9a9f96f449d63e354b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Isabel=20M=C3=A1rquez?= Date: Mon, 16 Jan 2023 14:35:52 +0100 Subject: [PATCH 09/70] Updating the releases file --- RELEASES.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 38647f237..d9e4c7e1b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,4 +1,17 @@ # Release notes + +## January 16, 2023 circom 2.1.3 + #### Extensions + - Improving error messages: invalid access and invalid assignment. + - Avoiding side effects in out of bounds log operations. + - Adding check to detect components that are created but do not receive all their inputs. + - Fixing field size of goldilocks when writing r1cs file. + + #### Fixed Bugs + - Fixing a problem with the use of integer division and ===, <== operators. If an integer division is involved in the expression of one of these operators, then we consider the expression as no quadratic. + - Fixing bug in code generation of constraint equalities with arrays + + ## November 7, 2022 circom 2.1.2 #### Fixed bugs From 1dadb4aea6551c774b5d1b65fdd4d42567d3064e Mon Sep 17 00:00:00 2001 From: clararod9 Date: Mon, 16 Jan 2023 14:45:15 +0100 Subject: [PATCH 10/70] updating version to 2.1.3 --- Cargo.lock | 16 ++++++++-------- circom/Cargo.toml | 2 +- circom_algebra/Cargo.toml | 2 +- code_producers/Cargo.toml | 2 +- compiler/Cargo.toml | 2 +- constraint_generation/Cargo.toml | 2 +- constraint_list/Cargo.toml | 2 +- dag/Cargo.toml | 2 +- program_structure/Cargo.toml | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a483cb744..d90f2b036 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,7 +155,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "circom" -version = "2.1.2" +version = "2.1.3" dependencies = [ "ansi_term", "clap", @@ -172,7 +172,7 @@ dependencies = [ [[package]] name = "circom_algebra" -version = "2.1.1" +version = "2.1.3" dependencies = [ "constant_tracking", "num-bigint-dig", @@ -205,7 +205,7 @@ dependencies = [ [[package]] name = "code_producers" -version = "2.1.1" +version = "2.1.3" dependencies = [ "handlebars", "lz_fnv", @@ -234,7 +234,7 @@ dependencies = [ [[package]] name = "compiler" -version = "2.1.2" +version = "2.1.3" dependencies = [ "code_producers", "constant_tracking", @@ -255,7 +255,7 @@ version = "2.0.0" [[package]] name = "constraint_generation" -version = "2.1.1" +version = "2.1.3" dependencies = [ "ansi_term", "circom_algebra", @@ -270,7 +270,7 @@ dependencies = [ [[package]] name = "constraint_list" -version = "2.0.9" +version = "2.1.3" dependencies = [ "circom_algebra", "constraint_writers", @@ -319,7 +319,7 @@ dependencies = [ [[package]] name = "dag" -version = "2.0.9" +version = "2.1.3" dependencies = [ "circom_algebra", "constraint_list", @@ -764,7 +764,7 @@ dependencies = [ [[package]] name = "program_structure" -version = "2.1.1" +version = "2.1.3" dependencies = [ "codespan", "codespan-reporting", diff --git a/circom/Cargo.toml b/circom/Cargo.toml index 54c5dee64..da4a5c73a 100644 --- a/circom/Cargo.toml +++ b/circom/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "circom" -version = "2.1.2" +version = "2.1.3" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/circom_algebra/Cargo.toml b/circom_algebra/Cargo.toml index 7b0fcdbe5..28c3f15be 100644 --- a/circom_algebra/Cargo.toml +++ b/circom_algebra/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "circom_algebra" -version = "2.1.1" +version = "2.1.3" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/code_producers/Cargo.toml b/code_producers/Cargo.toml index 41cd4fff3..8c8647536 100644 --- a/code_producers/Cargo.toml +++ b/code_producers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "code_producers" -version = "2.1.1" +version = "2.1.3" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index a47e146bc..4c753e81b 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "compiler" -version = "2.1.2" +version = "2.1.3" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/constraint_generation/Cargo.toml b/constraint_generation/Cargo.toml index df7d08e06..131eb673a 100644 --- a/constraint_generation/Cargo.toml +++ b/constraint_generation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "constraint_generation" -version = "2.1.1" +version = "2.1.3" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/constraint_list/Cargo.toml b/constraint_list/Cargo.toml index 8e95674cf..91cbce78d 100644 --- a/constraint_list/Cargo.toml +++ b/constraint_list/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "constraint_list" -version = "2.0.9" +version = "2.1.3" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/dag/Cargo.toml b/dag/Cargo.toml index b926041d1..4e0d4556f 100644 --- a/dag/Cargo.toml +++ b/dag/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dag" -version = "2.0.9" +version = "2.1.3" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/program_structure/Cargo.toml b/program_structure/Cargo.toml index 66528baab..926f954c9 100644 --- a/program_structure/Cargo.toml +++ b/program_structure/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "program_structure" -version = "2.1.1" +version = "2.1.3" authors = ["Costa Group UCM","iden3"] edition = "2018" From 18529d012b7aaa06818fd7163c315dd93bb1ae61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Antu=C3=B1a=20D=C3=ADez?= Date: Mon, 6 Feb 2023 13:24:40 +0100 Subject: [PATCH 11/70] fix generate_function_release_memory_component funtion --- .../src/c_elements/c_code_generator.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/code_producers/src/c_elements/c_code_generator.rs b/code_producers/src/c_elements/c_code_generator.rs index 48f888118..8d74f643c 100644 --- a/code_producers/src/c_elements/c_code_generator.rs +++ b/code_producers/src/c_elements/c_code_generator.rs @@ -738,12 +738,18 @@ pub fn generate_function_release_memory_component() -> Vec{ let mut instructions = vec![]; instructions.push("void release_memory_component(Circom_CalcWit* ctx, uint pos) {{\n".to_string()); instructions.push("if (pos != 0){{\n".to_string()); - instructions.push("delete ctx->componentMemory[pos].subcomponents;\n".to_string()); - instructions.push("delete ctx->componentMemory[pos].subcomponentsParallel;\n".to_string()); - instructions.push("delete ctx->componentMemory[pos].outputIsSet;\n".to_string()); - instructions.push("delete ctx->componentMemory[pos].mutexes;\n".to_string()); - instructions.push("delete ctx->componentMemory[pos].cvs;\n".to_string()); - instructions.push("delete ctx->componentMemory[pos].sbct;\n".to_string()); + instructions.push("if(ctx->componentMemory[pos].subcomponents)".to_string()); + instructions.push("delete []ctx->componentMemory[pos].subcomponents;\n".to_string()); + instructions.push("if(ctx->componentMemory[pos].subcomponentsParallel)".to_string()); + instructions.push("delete []ctx->componentMemory[pos].subcomponentsParallel;\n".to_string()); + instructions.push("if(ctx->componentMemory[pos].outputIsSet)".to_string()); + instructions.push("delete []ctx->componentMemory[pos].outputIsSet;\n".to_string()); + instructions.push("if(ctx->componentMemory[pos].mutexes)".to_string()); + instructions.push("delete []ctx->componentMemory[pos].mutexes;\n".to_string()); + instructions.push("if(ctx->componentMemory[pos].cvs)".to_string()); + instructions.push("delete []ctx->componentMemory[pos].cvs;\n".to_string()); + instructions.push("if(ctx->componentMemory[pos].sbct)".to_string()); + instructions.push("delete []ctx->componentMemory[pos].sbct;\n".to_string()); instructions.push("}}\n\n".to_string()); instructions.push("}}\n\n".to_string()); instructions From f44e4e9168833442a2dac8be7c931c83f97c0679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Antu=C3=B1a=20D=C3=ADez?= Date: Mon, 6 Feb 2023 13:30:10 +0100 Subject: [PATCH 12/70] initialise pointers to NULL --- code_producers/src/c_elements/common/circom.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code_producers/src/c_elements/common/circom.hpp b/code_producers/src/c_elements/common/circom.hpp index d9c8df1df..eabc686f6 100644 --- a/code_producers/src/c_elements/common/circom.hpp +++ b/code_producers/src/c_elements/common/circom.hpp @@ -47,12 +47,12 @@ struct Circom_Component { std::string templateName; std::string componentName; u64 idFather; - u32* subcomponents; - bool* subcomponentsParallel; - bool *outputIsSet; //one for each output - std::mutex *mutexes; //one for each output - std::condition_variable *cvs; - std::thread *sbct; //subcomponent threads + u32* subcomponents = NULL; + bool* subcomponentsParallel = NULL; + bool *outputIsSet = NULL; //one for each output + std::mutex *mutexes = NULL; //one for each output + std::condition_variable *cvs = NULL; + std::thread *sbct = NULL;//subcomponent threads }; /* From c7432b4f77a10b6927c3f313bd033a21a9654741 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Tue, 7 Feb 2023 11:35:03 +0100 Subject: [PATCH 13/70] Improving: syntax sugar remover, underscored substitution and tags propagation intermediate signals --- compiler/src/hir/component_preprocess.rs | 8 +- compiler/src/hir/merger.rs | 4 + compiler/src/hir/sugar_cleaner.rs | 2 +- compiler/src/hir/type_inference.rs | 2 +- .../component_representation.rs | 13 +- constraint_generation/src/execute.rs | 28 +- .../src/execution_data/filters.rs | 4 + .../src/constraint_simplification.rs | 6 +- parser/src/lib.rs | 4 +- parser/src/syntax_sugar_remover.rs | 914 +++++++++++------- .../src/abstract_syntax_tree/ast.rs | 5 + .../abstract_syntax_tree/statement_impl.rs | 21 + .../src/program_library/error_code.rs | 8 +- .../src/analyzers/custom_gate_analysis.rs | 18 + .../functions_free_of_template_elements.rs | 13 + .../src/analyzers/symbol_analysis.rs | 3 + type_analysis/src/analyzers/type_check.rs | 15 + .../src/analyzers/unknown_known_analysis.rs | 9 + .../src/decorators/constants_handler.rs | 10 +- .../src/decorators/type_reduction.rs | 5 +- 20 files changed, 737 insertions(+), 355 deletions(-) diff --git a/compiler/src/hir/component_preprocess.rs b/compiler/src/hir/component_preprocess.rs index e2512adca..60362f04f 100644 --- a/compiler/src/hir/component_preprocess.rs +++ b/compiler/src/hir/component_preprocess.rs @@ -18,9 +18,15 @@ fn rm_statement(stmt: &mut Statement) { rm_init(stmt); } else if stmt.is_substitution(){ rm_substitution(stmt); + } else if stmt.is_underscore_substitution(){ + rm_underscore_substitution(stmt); } - else{ +} +fn rm_underscore_substitution(stmt: &mut Statement){ + use Statement::{Block, UnderscoreSubstitution}; + if let UnderscoreSubstitution { meta, .. } = stmt{ + *stmt = Block{ meta: meta.clone(), stmts: Vec::new() }; } } diff --git a/compiler/src/hir/merger.rs b/compiler/src/hir/merger.rs index c655ed5de..4cb428b79 100644 --- a/compiler/src/hir/merger.rs +++ b/compiler/src/hir/merger.rs @@ -109,6 +109,8 @@ fn produce_vcf_stmt(stmt: &Statement, state: &mut State, environment: &mut E) { produce_vcf_while(stmt, state, environment); } else if stmt.is_if_then_else() { produce_vcf_conditional(stmt, state, environment); + } else if stmt.is_underscore_substitution() { + //No code should be produced for an instruction of the form _ <== exp; } else { unreachable!(); } @@ -359,6 +361,8 @@ fn link_stmt(stmt: &mut Statement, state: &State, env: &mut E) { link_declaration(stmt, state, env); } else if stmt.is_substitution() { link_substitution(stmt, state, env); + } else if stmt.is_underscore_substitution() { + //No code should be produced for an instruction of the form _ <== exp; } else { unreachable!(); } diff --git a/compiler/src/hir/sugar_cleaner.rs b/compiler/src/hir/sugar_cleaner.rs index 10bc5b606..3572ce7cd 100644 --- a/compiler/src/hir/sugar_cleaner.rs +++ b/compiler/src/hir/sugar_cleaner.rs @@ -62,7 +62,7 @@ fn extend_statement(stmt: &mut Statement, state: &mut State, context: &Context) extend_log_call(stmt, state, context) } else if stmt.is_assert() { extend_assert(stmt, state, context) - } else { + } else{ unreachable!() } } diff --git a/compiler/src/hir/type_inference.rs b/compiler/src/hir/type_inference.rs index 9b3c69200..02b82ee9d 100644 --- a/compiler/src/hir/type_inference.rs +++ b/compiler/src/hir/type_inference.rs @@ -40,7 +40,7 @@ fn infer_type_stmt(stmt: &Statement, state: &State, context: &mut SearchInfo) -> Option::None } else if stmt.is_assert() { Option::None - } else { + } else{ unreachable!() } } diff --git a/constraint_generation/src/environment_utils/component_representation.rs b/constraint_generation/src/environment_utils/component_representation.rs index 23c151b10..c1f85b796 100644 --- a/constraint_generation/src/environment_utils/component_representation.rs +++ b/constraint_generation/src/environment_utils/component_representation.rs @@ -129,9 +129,18 @@ impl ComponentRepresentation { for (symbol, route) in node.outputs() { component.outputs.insert(symbol.clone(), SignalSlice::new_with_route(route, &true)); + let tags_output = node.signal_to_tags.get(symbol); - if tags_output.is_some(){ - component.outputs_tags.insert(symbol.to_string(), tags_output.unwrap().clone()); + let component_tags_output = component.outputs_tags.get_mut(symbol); + if tags_output.is_some() && component_tags_output.is_some(){ + let result_tags_output = tags_output.unwrap(); + let result_component_tags_output = component_tags_output.unwrap(); + for (tag, value) in result_tags_output{ + // only update the output tag in case it contains the tag in the definition + if result_component_tags_output.contains_key(tag){ + result_component_tags_output.insert(tag.clone(), value.clone()); + } + } } } component.node_pointer = Option::Some(node_pointer); diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index ae03fd224..1fe21d291 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -459,6 +459,30 @@ fn execute_statement( &runtime.call_trace, )? } + UnderscoreSubstitution{ meta, rhe, op} =>{ + let f_result = execute_expression(rhe, program_archive, runtime, flag_verbose)?; + let arithmetic_slice = safe_unwrap_to_arithmetic_slice(f_result, line!()); + if *op == AssignOp::AssignConstraintSignal{ + for i in 0..AExpressionSlice::get_number_of_cells(&arithmetic_slice){ + let _value_cell = treat_result_with_memory_error( + AExpressionSlice::access_value_by_index(&arithmetic_slice, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + //let constraint_expression = AExpr::transform_expression_to_constraint_form( + // value_cell, + // runtime.constants.get_p(), + //).unwrap(); + //if let Option::Some(node) = actual_node { + //for signal in constraint_expression.take_signals(){ + // node.add_underscored_signal(signal); + //} + //} + } + } + Option::None + } }; Result::Ok(res) } @@ -960,8 +984,7 @@ fn perform_assign( } } } - /* else { // it is a tag that does not belong to the value - + else { // it is a tag that does not belong to the signal reference_to_tags.insert(tag.clone(), value.clone()); if let Option::Some(node) = actual_node{ node.add_tag_signal(symbol, &tag, value.clone()); @@ -969,7 +992,6 @@ fn perform_assign( unreachable!(); } } - */ } } } diff --git a/constraint_generation/src/execution_data/filters.rs b/constraint_generation/src/execution_data/filters.rs index 06c14d230..20b080ab4 100644 --- a/constraint_generation/src/execution_data/filters.rs +++ b/constraint_generation/src/execution_data/filters.rs @@ -100,6 +100,10 @@ pub fn apply_computed(stmt: &mut Statement, analysis: &Analysis) { *arg = computed_or_original(analysis, arg); apply_computed_expr(arg, analysis); } + UnderscoreSubstitution { rhe, .. } => { + *rhe = computed_or_original(analysis, rhe); + apply_computed_expr(rhe, analysis); + }, } } diff --git a/constraint_list/src/constraint_simplification.rs b/constraint_list/src/constraint_simplification.rs index 0fdb43d71..af57e1bde 100644 --- a/constraint_list/src/constraint_simplification.rs +++ b/constraint_list/src/constraint_simplification.rs @@ -457,7 +457,7 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { let mut deleted = HashSet::new(); let mut lconst = LinkedList::new(); let mut no_rounds = smp.no_rounds; - let remove_unused = apply_linear; + let remove_unused = true; let relevant_signals = { // println!("Creating first relevant set"); @@ -529,7 +529,7 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { relevant }; - let linear_substitutions = if remove_unused { + let linear_substitutions = if apply_linear { let now = SystemTime::now(); let (subs, mut cons) = linear_simplification( &mut substitution_log, @@ -585,7 +585,7 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { let mut round_id = 0; let _ = round_id; let mut linear = with_linear; - let mut apply_round = remove_unused && no_rounds > 0 && !linear.is_empty(); + let mut apply_round = no_rounds > 0 && !linear.is_empty(); let mut non_linear_map = if apply_round || remove_unused { // println!("Building non-linear map"); let now = SystemTime::now(); diff --git a/parser/src/lib.rs b/parser/src/lib.rs index b77e7bb20..ddda36f02 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -131,8 +131,8 @@ pub fn run_parser( let lib = program_archive.get_file_library().clone(); let program_archive_result = apply_syntactic_sugar( &mut program_archive); match program_archive_result { - Result::Err(v) => {Result::Err((lib,vec![v]))} - Result::Ok(()) => {Ok((program_archive, warnings))} + Result::Err(v) => Result::Err((lib,vec![v])), + Result::Ok(_) => Ok((program_archive, warnings)), } } } diff --git a/parser/src/syntax_sugar_remover.rs b/parser/src/syntax_sugar_remover.rs index 713c05e66..21085bff2 100644 --- a/parser/src/syntax_sugar_remover.rs +++ b/parser/src/syntax_sugar_remover.rs @@ -13,133 +13,340 @@ use num_bigint::BigInt; use crate::errors::{AnonymousCompError,TupleError}; +pub fn apply_syntactic_sugar(program_archive : &mut ProgramArchive) -> Result<(), Report> { + let mut new_templates : HashMap = HashMap::new(); + if program_archive.get_main_expression().is_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(program_archive.get_main_expression().get_meta().clone(),"The main component cannot contain an anonymous call ".to_string())); + + } + for temp in program_archive.templates.clone() { + let t = temp.1.clone(); + let body = t.get_body().clone(); + check_anonymous_components_statement(&body)?; + let (new_body, initializations) = remove_anonymous_from_statement(&mut program_archive.templates, &program_archive.file_library, body, &None)?; + if let Statement::Block { meta, mut stmts } = new_body { + let (component_decs, variable_decs, mut substitutions) = separate_declarations_in_comp_var_subs(initializations); + let mut init_block = vec![ + build_initialization_block(meta.clone(), VariableType::Var, variable_decs), + build_initialization_block(meta.clone(), VariableType::Component, component_decs)]; + init_block.append(&mut substitutions); + init_block.append(&mut stmts); + let new_body_with_inits = build_block(meta, init_block); + check_tuples_statement(&new_body_with_inits)?; + let new_body = remove_tuples_from_statement(new_body_with_inits)?; + let t2 = TemplateData::copy(t.get_name().to_string(), t.get_file_id(), new_body, t.get_num_of_params(), t.get_name_of_params().clone(), + t.get_param_location(), t.get_inputs().clone(), t.get_outputs().clone(), t.is_parallel(), t.is_custom_gate(), t.get_declaration_inputs().clone(), t.get_declaration_outputs().clone()); + new_templates.insert(temp.0.clone(), t2); + } else{ + unreachable!() + } + } + program_archive.templates = new_templates; + Result::Ok(()) +} -fn remove_anonymous_from_statement( - templates : &mut HashMap, - file_lib : &FileLibrary, - stm : Statement, - var_access: &Option -) -> Result<(Statement,Vec),Report>{ - match stm.clone() { - Statement::MultSubstitution { meta, lhe, op, rhe } => { +fn check_anonymous_components_statement( + stm : &Statement, +) -> Result<(), Report>{ + match stm { + Statement::MultSubstitution {meta, lhe, rhe, ..} => { if lhe.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(lhe.get_meta().clone(),"An anonymous component cannot be used in the left side of an assignment".to_string())); - } else{ - let (mut stmts, declarations, new_rhe) = remove_anonymous_from_expression(templates, file_lib, rhe, var_access)?; - let subs = Statement::MultSubstitution { meta: meta.clone(), lhe: lhe, op: op, rhe: new_rhe }; - let mut substs = Vec::new(); - if stmts.is_empty(){ - Result::Ok((subs, declarations)) - }else{ - substs.append(&mut stmts); - substs.push(subs); - Result::Ok((Statement::Block { meta : meta, stmts : substs}, declarations)) - } + Result::Err(AnonymousCompError::anonymous_general_error( + meta.clone(), + "An anonymous component cannot be used in the left side of an assignment".to_string()) + ) + } else{ + check_anonymous_components_expression(rhe) } }, - Statement::IfThenElse { meta, cond, if_case, else_case } + Statement::IfThenElse { meta, cond, if_case, else_case, .. } => { if cond.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_inside_condition_error(cond.get_meta().clone())) + Result::Err(AnonymousCompError::anonymous_inside_condition_error(meta.clone())) } else{ - let (if_ok,mut declarations) = remove_anonymous_from_statement(templates, file_lib, *if_case, var_access)?; - let b_if = Box::new(if_ok); - if else_case.is_none(){ - Result::Ok((Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::None},declarations)) - }else { - let else_c = *(else_case.unwrap()); - let (else_ok, mut declarations2) = remove_anonymous_from_statement(templates, file_lib, else_c, var_access)?; - let b_else = Box::new(else_ok); - declarations.append(&mut declarations2); - Result::Ok((Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::Some(b_else)},declarations)) + check_anonymous_components_statement(if_case)?; + if else_case.is_some(){ + check_anonymous_components_statement(else_case.as_ref().unwrap())?; } + Result::Ok(()) } } - Statement::While { meta, cond, stmt } => { + Statement::While { meta, cond, stmt, .. } => { if cond.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_inside_condition_error(cond.get_meta().clone())) + Result::Err(AnonymousCompError::anonymous_inside_condition_error(meta.clone())) } else{ - - let id_var_while = "anon_var_".to_string() + &file_lib.get_line(meta.start, meta.get_file_id()).unwrap().to_string() + "_" + &meta.start.to_string(); - let var_access = Expression::Variable{meta: meta.clone(), name: id_var_while.clone(), access: Vec::new()}; - let mut declarations = vec![]; - let (while_ok, mut declarations2) = remove_anonymous_from_statement(templates, file_lib, *stmt, &Some(var_access.clone()))?; - let b_while = if !declarations2.is_empty(){ - declarations.push( - build_declaration( - meta.clone(), - VariableType::Var, - id_var_while.clone(), - Vec::new(), - ) - ); - declarations.push( - build_substitution( - meta.clone(), - id_var_while.clone(), - vec![], - AssignOp::AssignVar, - Expression::Number(meta.clone(), BigInt::from(0)) - ) - ); - declarations.append(&mut declarations2); - let next_access = Expression::InfixOp{ - meta: meta.clone(), - infix_op: ExpressionInfixOpcode::Add, - lhe: Box::new(var_access), - rhe: Box::new(Expression::Number(meta.clone(), BigInt::from(1))), - }; - let subs_access = Statement::Substitution{ - meta: meta.clone(), - var: id_var_while, - access: Vec::new(), - op: AssignOp::AssignVar, - rhe: next_access, - }; - - let new_block = Statement::Block{ - meta: meta.clone(), - stmts: vec![while_ok, subs_access], - }; - Box::new(new_block) - } else{ - Box::new(while_ok) - }; - - Result::Ok((Statement::While { meta: meta, cond: cond, stmt: b_while}, declarations)) + check_anonymous_components_statement(stmt) } } Statement::LogCall {meta, args } => { - for arg in &args { + for arg in args { if let program_structure::ast::LogArgument::LogExp( exp ) = arg { if exp.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta,"An anonymous component cannot be used inside a log".to_string())) + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone() ,"An anonymous component cannot be used inside a log".to_string())) } } } - Result::Ok((build_log_call(meta, args),Vec::new())) + Result::Ok(()) } - Statement::Assert { meta, arg} => { Result::Ok((build_assert(meta, arg),Vec::new()))} + Statement::Assert { meta, arg} => { + if arg.contains_anonymous_comp() { + Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(), "An anonymous component cannot be used inside an assert".to_string())) + } else{ + Result::Ok(()) + } + } Statement::Return { meta, value: arg}=> { if arg.contains_anonymous_comp(){ - Result::Err(AnonymousCompError::anonymous_general_error(meta,"An anonymous component cannot be used inside a function ".to_string())) + Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(), "An anonymous component cannot be used inside a function ".to_string())) + } else{ + Result::Ok(()) } - else{ Result::Ok((build_return(meta, arg),Vec::new()))} } Statement::ConstraintEquality {meta, lhe, rhe } => { if lhe.contains_anonymous_comp() || rhe.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_general_error(meta,"An anonymous component cannot be used with operator === ".to_string())) + Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(), "An anonymous component cannot be used with operator === ".to_string())) + } + else{ + Result::Ok(()) } - else{ Result::Ok((build_constraint_equality(meta, lhe, rhe),Vec::new())) } } - Statement::Declaration { meta , xtype , name , - dimensions, .. } => { - for exp in dimensions.clone(){ + Statement::Declaration { meta, dimensions, .. } => { + for exp in dimensions{ if exp.contains_anonymous_comp(){ - return Result::Err(AnonymousCompError::anonymous_general_error(exp.get_meta().clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + } + } + Result::Ok(()) + } + Statement::InitializationBlock { initializations, .. } => + { + for stmt in initializations { + check_anonymous_components_statement(stmt)?; + } + Result::Ok(()) + } + Statement::Block {stmts, .. } => { + + for stmt in stmts { + check_anonymous_components_statement(stmt)?; + } + Result::Ok(()) + } + Statement::Substitution { meta, rhe, access, ..} => { + use program_structure::ast::Access::ComponentAccess; + use program_structure::ast::Access::ArrayAccess; + for acc in access{ + match acc{ + ArrayAccess(exp) =>{ + if exp.contains_anonymous_comp(){ + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + } + }, + ComponentAccess(_)=>{}, + } + } + check_anonymous_components_expression(rhe) + } + Statement::UnderscoreSubstitution { .. } => unreachable!(), + } +} + +pub fn check_anonymous_components_expression( + exp : &Expression, +) -> Result<(),Report>{ + use Expression::*; + match exp { + ArrayInLine { meta, values, .. } => { + for value in values{ + if value.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + } + } + Result::Ok(()) + }, + UniformArray { meta, value, dimension } => { + if value.contains_anonymous_comp() || dimension.contains_anonymous_comp() { + Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())) + } else{ + Result::Ok(()) + } + }, + Number(_, _) => { + Result::Ok(()) + }, + Variable { meta, access, .. } => { + use program_structure::ast::Access::ComponentAccess; + use program_structure::ast::Access::ArrayAccess; + for acc in access{ + match acc{ + ArrayAccess(exp) =>{ + if exp.contains_anonymous_comp(){ + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + } + }, + ComponentAccess(_)=>{}, } } + Result::Ok(()) + }, + InfixOp { meta, lhe, rhe, .. } => { + if lhe.contains_anonymous_comp() || rhe.contains_anonymous_comp() { + Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used in the middle of an operation ".to_string())) + } else{ + Result::Ok(()) + } + }, + PrefixOp { meta, rhe, .. } => { + if rhe.contains_anonymous_comp() { + Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used in the middle of an operation ".to_string())) + } else{ + Result::Ok(()) + } + }, + InlineSwitchOp { meta, cond, if_true, if_false } => { + if cond.contains_anonymous_comp() || if_true.contains_anonymous_comp() || if_false.contains_anonymous_comp() { + Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used inside an inline switch ".to_string())) + } else{ + Result::Ok(()) + } + }, + Call { meta, args, .. } => { + for value in args{ + if value.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); + } + } + Result::Ok(()) + }, + AnonymousComp {meta, params, signals, .. } => { + for value in params{ + if value.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); + } + } + for value in signals{ + check_anonymous_components_expression(value)?; + } + Result::Ok(()) + }, + Tuple {values, .. } => { + + for val in values{ + check_anonymous_components_expression(val)?; + } + Result::Ok(()) + }, + ParallelOp { meta, rhe } => { + if !rhe.is_call() && !rhe.is_anonymous_comp() && rhe.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"Bad use of parallel operator in combination with anonymous components ".to_string())); + } + else if rhe.is_call() && rhe.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); + } + Result::Ok(()) + }, + } +} + + +fn remove_anonymous_from_statement( + templates : &HashMap, + file_lib : &FileLibrary, + stm : Statement, + var_access: &Option +) -> Result<(Statement, Vec),Report>{ + match stm { + Statement::MultSubstitution { meta, lhe, op, rhe } => { + + let (mut stmts, declarations, new_rhe) = remove_anonymous_from_expression(templates, file_lib, rhe, var_access)?; + let subs = Statement::MultSubstitution { meta: meta.clone(), lhe: lhe, op: op, rhe: new_rhe }; + let mut substs = Vec::new(); + if stmts.is_empty(){ + Result::Ok((subs, declarations)) + }else{ + substs.append(&mut stmts); + substs.push(subs); + Result::Ok((Statement::Block { meta : meta, stmts : substs}, declarations)) + } + }, + Statement::IfThenElse { meta, cond, if_case, else_case } + => { + + let (if_ok,mut declarations) = remove_anonymous_from_statement(templates, file_lib, *if_case, var_access)?; + let b_if = Box::new(if_ok); + if else_case.is_none(){ + Result::Ok((Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::None},declarations)) + }else { + let else_c = *(else_case.unwrap()); + let (else_ok, mut declarations2) = remove_anonymous_from_statement(templates, file_lib, else_c, var_access)?; + let b_else = Box::new(else_ok); + declarations.append(&mut declarations2); + Result::Ok((Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::Some(b_else)},declarations)) + } + } + Statement::While { meta, cond, stmt } => { + let id_var_while = "anon_var_".to_string() + &file_lib.get_line(meta.start, meta.get_file_id()).unwrap().to_string() + "_" + &meta.start.to_string(); + let var_access = Expression::Variable{meta: meta.clone(), name: id_var_while.clone(), access: Vec::new()}; + let mut declarations = vec![]; + let (while_ok, mut declarations2) = remove_anonymous_from_statement(templates, file_lib, *stmt, &Some(var_access.clone()))?; + let b_while = if !declarations2.is_empty(){ + declarations.push( + build_declaration( + meta.clone(), + VariableType::Var, + id_var_while.clone(), + Vec::new(), + ) + ); + declarations.push( + build_substitution( + meta.clone(), + id_var_while.clone(), + vec![], + AssignOp::AssignVar, + Expression::Number(meta.clone(), BigInt::from(0)) + ) + ); + declarations.append(&mut declarations2); + let next_access = Expression::InfixOp{ + meta: meta.clone(), + infix_op: ExpressionInfixOpcode::Add, + lhe: Box::new(var_access), + rhe: Box::new(Expression::Number(meta.clone(), BigInt::from(1))), + }; + let subs_access = Statement::Substitution{ + meta: meta.clone(), + var: id_var_while, + access: Vec::new(), + op: AssignOp::AssignVar, + rhe: next_access, + }; + + let new_block = Statement::Block{ + meta: meta.clone(), + stmts: vec![while_ok, subs_access], + }; + Box::new(new_block) + } else{ + Box::new(while_ok) + }; + Result::Ok((Statement::While { meta: meta, cond: cond, stmt: b_while}, declarations)) + }, + Statement::LogCall {meta, args } => { + Result::Ok((build_log_call(meta, args),Vec::new())) + } + Statement::Assert { meta, arg} => { + Result::Ok((build_assert(meta, arg),Vec::new())) + } + Statement::Return { meta, value: arg}=> { + Result::Ok((build_return(meta, arg),Vec::new())) + } + Statement::ConstraintEquality {meta, lhe, rhe } => { + Result::Ok((build_constraint_equality(meta, lhe, rhe),Vec::new())) + } + Statement::Declaration { meta , xtype , name , + dimensions, .. } => { Result::Ok((build_declaration(meta, xtype, name, dimensions),Vec::new())) } Statement::InitializationBlock { meta, xtype, initializations } => @@ -175,87 +382,30 @@ fn remove_anonymous_from_statement( Result::Ok((Statement::Block { meta : meta, stmts : substs}, declarations)) } } + Statement::UnderscoreSubstitution { .. } => unreachable!(), } } // returns a block with the substitutions, the declarations and finally the output expression pub fn remove_anonymous_from_expression( - templates : &mut HashMap, - file_lib : & FileLibrary, + templates : &HashMap, + file_lib : &FileLibrary, exp : Expression, var_access: &Option, // in case the call is inside a loop, variable used to control the access ) -> Result<(Vec, Vec, Expression),Report>{ use Expression::*; - match exp.clone() { - ArrayInLine { values, .. } => { - for value in values{ - if value.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(value.get_meta().clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); - } - } - Result::Ok((Vec::new(),Vec::new(),exp)) - }, - UniformArray { meta, value, dimension } => { - if value.contains_anonymous_comp() || dimension.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); - } - Result::Ok((Vec::new(),Vec::new(),exp)) - }, - Number(_, _) => { Result::Ok((Vec::new(),Vec::new(),exp)) }, - Variable { meta, .. } => { - if exp.contains_anonymous_comp(){ - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to access an array".to_string())); - } - Result::Ok((Vec::new(),Vec::new(),exp)) - }, - InfixOp { meta, lhe, rhe, .. } => { - if lhe.contains_anonymous_comp() || rhe.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used in the middle of an operation ".to_string())); - } - Result::Ok((Vec::new(),Vec::new(),exp)) - }, - PrefixOp { meta, rhe, .. } => { - if rhe.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used in the middle of an operation ".to_string())); - } - Result::Ok((Vec::new(),Vec::new(),exp)) - }, - InlineSwitchOp { meta, cond, if_true, if_false } => { - if cond.contains_anonymous_comp() || if_true.contains_anonymous_comp() || if_false.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used inside an inline switch ".to_string())); - } - Result::Ok((Vec::new(),Vec::new(),exp)) - /* This code is useful if we want to allow anonymous components inside InLineSwitch. - let result_if = remove_anonymous_from_expression( templates, file_lib, *if_true); - let result_else = remove_anonymous_from_expression( templates, file_lib, *if_false); - if result_if.is_err() { Result::Err(result_if.err().unwrap())} - else if result_else.is_err() { Result::Err(result_else.err().unwrap())} - else { - let (result_if2,exp_if) = result_if.ok().unwrap(); - let (result_else2, exp_else) = result_else.ok().unwrap(); - let block_if = if result_if2.is_none() { build_block(meta.clone(), Vec::new())} else { result_if2.unwrap()}; - - Result::Ok((Option::Some(build_conditional_block(meta.clone(), *cond.clone(), block_if, result_else2)), - build_inline_switch_op(meta.clone(), *cond.clone(), exp_if, exp_else)))*/ - - }, - Call { meta, args, .. } => { - for value in args{ - if value.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); - } - } - Result::Ok((Vec::new(),Vec::new(),exp)) - }, + match exp { AnonymousComp { meta, id, params, signals, names, is_parallel } => { - let template = templates.get(&id); let mut declarations = Vec::new(); + let mut seq_substs = Vec::new(); + // get the template we are calling to + let template = templates.get(&id); if template.is_none(){ return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"The template does not exist ".to_string())); } - let mut i = 0; - let mut seq_substs = Vec::new(); let id_anon_temp = id.to_string() + "_" + &file_lib.get_line(meta.start, meta.get_file_id()).unwrap().to_string() + "_" + &meta.start.to_string(); + + // in case we are not inside a loop, we can automatically convert into a component if var_access.is_none(){ declarations.push(build_declaration( meta.clone(), @@ -263,7 +413,7 @@ pub fn remove_anonymous_from_expression( id_anon_temp.clone(), Vec::new(), )); - } else{ + } else{ // we generate an anonymous component, it depends on the var_access indicating the loop declarations.push(build_declaration( meta.clone(), VariableType::AnonymousComponent, @@ -271,71 +421,82 @@ pub fn remove_anonymous_from_expression( vec![var_access.as_ref().unwrap().clone()], )); } - let call = build_call(meta.clone(), id, params); - if call.contains_anonymous_comp(){ - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); - } + // build the call generating the component + let call = build_call(meta.clone(), id.clone(), params.clone()); let exp_with_call = if is_parallel { build_parallel_op(meta.clone(), call) - } else { + } else { call }; + // in case we are in a loop in only generates a position, needs the var_access reference let access = if var_access.is_none(){ Vec::new() } else{ vec![build_array_access(var_access.as_ref().unwrap().clone())] }; - let sub = build_substitution(meta.clone(), id_anon_temp.clone(), - access.clone(), AssignOp::AssignVar, exp_with_call); + // in loop: id_anon_temp[var_access] = (parallel) Template(params); + // out loop: id_anon_temp = (parallel) Template(params) + let sub = build_substitution( + meta.clone(), + id_anon_temp.clone(), + access.clone(), + AssignOp::AssignVar, + exp_with_call + ); seq_substs.push(sub); + + // assign the inputs + // reorder the signals in new_signals (case names) let inputs = template.unwrap().get_declaration_inputs(); let mut new_signals = Vec::new(); let mut new_operators = Vec::new(); if let Some(m) = names { let (operators, names) : (Vec, Vec) = m.iter().cloned().unzip(); - for inp in inputs{ - if !names.contains(&inp.0) { - let error = inp.0.clone() + " has not been found in the anonymous call"; + for (signal, _) in inputs{ + if !names.contains(signal) { + let error = signal.clone() + " has not been found in the anonymous call"; return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),error)); } else { - let pos = names.iter().position(|r| *r == inp.0).unwrap(); + let pos = names.iter().position(|r| r == signal).unwrap(); new_signals.push(signals.get(pos).unwrap().clone()); new_operators.push(*operators.get(pos).unwrap()); } } } else{ - new_signals = signals.clone(); - for _i in 0..signals.len() { + new_signals = signals; + for _i in 0..new_signals.len() { new_operators.push(AssignOp::AssignConstraintSignal); } } - if inputs.len() != new_signals.len() || inputs.len() != signals.len() { + if inputs.len() != new_signals.len() { return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"The number of template input signals must coincide with the number of input parameters ".to_string())); } - for inp in inputs{ + + // generate the substitutions for the inputs + let mut num_input = 0; + for (name_signal, _) in inputs{ let mut acc = if var_access.is_none(){ Vec::new() } else{ vec![build_array_access(var_access.as_ref().unwrap().clone())] }; - acc.push(Access::ComponentAccess(inp.0.clone())); + acc.push(Access::ComponentAccess(name_signal.clone())); let (mut stmts, mut declarations2, new_exp) = remove_anonymous_from_expression( - &mut templates.clone(), + templates, file_lib, - new_signals.get(i).unwrap().clone(), + new_signals.get(num_input).unwrap().clone(), var_access )?; - if new_exp.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(new_exp.get_meta().clone(),"Inputs of an anonymous component cannot contain anonymous calls".to_string())); - } + seq_substs.append(&mut stmts); declarations.append(&mut declarations2); - let subs = Statement::Substitution { meta: meta.clone(), var: id_anon_temp.clone(), access: acc, op: *new_operators.get(i).unwrap(), rhe: new_exp }; - i+=1; + let subs = Statement::Substitution { meta: meta.clone(), var: id_anon_temp.clone(), access: acc, op: *new_operators.get(num_input).unwrap(), rhe: new_exp }; + num_input += 1; seq_substs.push(subs); } + // generate the expression for the outputs -> return as expression (if single out) or tuple let outputs = template.unwrap().get_declaration_outputs(); if outputs.len() == 1 { let output = outputs.get(0).unwrap().0.clone(); @@ -347,7 +508,7 @@ pub fn remove_anonymous_from_expression( acc.push(Access::ComponentAccess(output.clone())); let out_exp = Expression::Variable { meta: meta.clone(), name: id_anon_temp, access: acc }; - Result::Ok((vec![Statement::Block { meta: meta, stmts: seq_substs }],declarations,out_exp)) + Result::Ok((vec![Statement::Block { meta: meta.clone(), stmts: seq_substs }], declarations, out_exp)) } else{ let mut new_values = Vec::new(); @@ -362,7 +523,7 @@ pub fn remove_anonymous_from_expression( new_values.push(out_exp); } let out_exp = Tuple {meta : meta.clone(), values : new_values}; - Result::Ok((vec![Statement::Block { meta: meta, stmts: seq_substs }], declarations, out_exp)) + Result::Ok((vec![Statement::Block { meta: meta.clone(), stmts: seq_substs }], declarations, out_exp)) } }, @@ -381,21 +542,19 @@ pub fn remove_anonymous_from_expression( Err(er) => {return Result::Err(er);}, } } - Result::Ok((new_stmts, declarations, build_tuple(meta, new_values))) + Result::Ok((new_stmts, declarations, build_tuple(meta.clone(), new_values))) }, ParallelOp { meta, rhe } => { - if !rhe.is_call() && !rhe.is_anonymous_comp() && rhe.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"Bad use of parallel operator in combination with anonymous components ".to_string())); - } - else if rhe.is_call() && rhe.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); - } - else if rhe.is_anonymous_comp(){ + if rhe.is_anonymous_comp(){ let rhe2 = rhe.make_anonymous_parallel(); - return remove_anonymous_from_expression(templates, file_lib, rhe2, var_access); + remove_anonymous_from_expression(templates, file_lib, rhe2, var_access) + } else{ + Result::Ok((Vec::new(),Vec::new(), ParallelOp { meta, rhe })) } - Result::Ok((Vec::new(),Vec::new(),exp)) }, + _ =>{ + Result::Ok((Vec::new(),Vec::new(),exp)) + } } } @@ -423,42 +582,200 @@ pub fn separate_declarations_in_comp_var_subs(declarations: Vec) -> ( } (components_dec, variables_dec, substitutions) } -pub fn apply_syntactic_sugar(program_archive : &mut ProgramArchive) -> Result<(),Report> { - let mut new_templates : HashMap = HashMap::new(); - if program_archive.get_main_expression().is_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(program_archive.get_main_expression().get_meta().clone(),"The main component cannot contain an anonymous call ".to_string())); - + +fn check_tuples_statement(stm: &Statement)-> Result<(), Report>{ + match stm{ + Statement::MultSubstitution { lhe, rhe, .. } => { + check_tuples_expression(lhe)?; + check_tuples_expression(rhe)?; + Result::Ok(()) + }, + Statement::IfThenElse { cond, if_case, else_case, meta, .. } + => { + if cond.contains_tuple() { + Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a condition ".to_string())) + } else{ + check_tuples_statement(if_case)?; + if else_case.is_some(){ + check_tuples_statement(else_case.as_ref().unwrap())?; + } + Result::Ok(()) + } + } + + Statement::While { meta, cond, stmt } => { + if cond.contains_tuple() { + Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a condition ".to_string())) + } else{ + check_tuples_statement(stmt) + } + } + Statement::LogCall {args, .. } => { + for arg in args { + match arg { + LogArgument::LogStr(_) => {}, + LogArgument::LogExp(exp) => { + check_tuples_expression(&exp)?; + }, + } + } + Result::Ok(()) + } + Statement::Assert { meta, arg} => { + if arg.contains_tuple(){ + Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in a return ".to_string())) + } + else{ + Result::Ok(()) + } + } + Statement::Return { meta, value: arg}=> { + if arg.contains_tuple(){ + Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a function ".to_string())) + } + else{ + Result::Ok(()) + } + } + Statement::ConstraintEquality {meta, lhe, rhe } => { + if lhe.contains_tuple() || rhe.contains_tuple() { + Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used with the operator === ".to_string())) + } + else{ + Result::Ok(()) + } + } + Statement::Declaration { meta, + dimensions, .. } => + { + for exp in dimensions.clone(){ + if exp.contains_tuple(){ + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); + } + } + Result::Ok(()) + } + Statement::InitializationBlock {initializations, ..} => + { + for stmt in initializations { + check_tuples_statement(stmt)?; + } + Result::Ok(()) + } + Statement::Block { stmts, ..} => { + for stmt in stmts { + check_tuples_statement(stmt)?; + } + Result::Ok(()) + } + Statement::Substitution { rhe, access, meta, ..} => { + use program_structure::ast::Access::ComponentAccess; + use program_structure::ast::Access::ArrayAccess; + for acc in access{ + match acc{ + ArrayAccess(exp) =>{ + if exp.contains_tuple(){ + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array".to_string())); + } + }, + ComponentAccess(_)=>{}, + } + } + check_tuples_expression(rhe) + } + Statement::UnderscoreSubstitution { .. } => unreachable!(), } - for temp in program_archive.templates.clone() { - let t = temp.1.clone(); - let body = t.get_body().clone(); - let (new_body, initializations) = remove_anonymous_from_statement(&mut program_archive.templates, &program_archive.file_library, body, &None)?; - if let Statement::Block { meta, mut stmts } = new_body { - let (component_decs, variable_decs, mut substitutions) = separate_declarations_in_comp_var_subs(initializations); - let mut init_block = vec![ - build_initialization_block(meta.clone(), VariableType::Var, variable_decs), - build_initialization_block(meta.clone(), VariableType::Component, component_decs)]; - init_block.append(&mut substitutions); - init_block.append(&mut stmts); - let new_body_with_inits = build_block(meta, init_block); - let new_body = remove_tuples_from_statement(&mut program_archive.templates, &program_archive.file_library, new_body_with_inits)?; - let t2 = TemplateData::copy(t.get_name().to_string(), t.get_file_id(), new_body, t.get_num_of_params(), t.get_name_of_params().clone(), - t.get_param_location(), t.get_inputs().clone(), t.get_outputs().clone(), t.is_parallel(), t.is_custom_gate(), t.get_declaration_inputs().clone(), t.get_declaration_outputs().clone()); - new_templates.insert(temp.0.clone(), t2); - } else{ - unreachable!() +} + + +pub fn check_tuples_expression(exp: &Expression) -> Result<(), Report>{ + use Expression::*; + match exp{ + ArrayInLine { meta, values } => { + for value in values{ + if value.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); + } + } + Result::Ok(()) + }, + UniformArray { meta, value, dimension } => { + if value.contains_tuple() || dimension.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); + } + Result::Ok(()) + }, + Number(_, _) => { + Result::Ok(()) + }, + Variable { access, meta, .. } => { + use program_structure::ast::Access::*; + for acc in access{ + match acc{ + ArrayAccess(exp) =>{ + if exp.contains_tuple(){ + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array".to_string())); + } + }, + ComponentAccess(_)=>{}, + } + } + Result::Ok(()) + }, + InfixOp { meta, lhe, rhe, .. } => { + if lhe.contains_tuple() || rhe.contains_tuple() { + Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in the middle of an operation".to_string())) + } else{ + Result::Ok(()) + } + }, + PrefixOp { meta, rhe, .. } => { + if rhe.contains_tuple() { + Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in the middle of an operation".to_string())) + } else{ + Result::Ok(()) + } + }, + InlineSwitchOp { meta, cond, if_true, if_false } => { + if cond.contains_tuple() || if_true.contains_tuple() || if_false.contains_tuple() { + Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside an inline switch".to_string())) + } else{ + Result::Ok(()) + } + }, + Call { meta, args, .. } => { + for value in args{ + if value.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used as a parameter of a function call".to_string())); + } + } + Result::Ok(()) + }, + AnonymousComp { .. } => { + unreachable!(); } + Tuple { values, .. } => { + for val in values { + check_tuples_expression(val)?; + } + Result::Ok(()) + }, + ParallelOp { meta, rhe} => { + if rhe.contains_tuple() { + Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in a parallel operator ".to_string())) + } else{ + Result::Ok(()) + } + }, } - program_archive.templates = new_templates; - Result::Ok(()) } -fn remove_tuples_from_statement(templates: &mut HashMap, file_lib: &FileLibrary, stm: Statement) -> Result { - match stm.clone() { +fn remove_tuples_from_statement(stm: Statement) -> Result { + match stm{ Statement::MultSubstitution { meta, lhe, op, rhe } => { - let ok = remove_tuple_from_expression(templates, file_lib, lhe)?; - let ok2 = remove_tuple_from_expression(templates, file_lib, rhe)?; - match (ok, ok2) { + let new_exp_lhe = remove_tuple_from_expression(lhe); + let new_exp_rhe = remove_tuple_from_expression(rhe); + match (new_exp_lhe, new_exp_rhe) { (Expression::Tuple { values: mut values1, .. }, Expression::Tuple { values: mut values2, .. }) => { if values1.len() == values2.len() { @@ -468,7 +785,9 @@ fn remove_tuples_from_statement(templates: &mut HashMap, f if let Expression::Variable { meta, name, access } = lhe { let rhe = values2.remove(0); if name != "_" { - substs.push(build_substitution(meta.clone(), name.clone(), access.to_vec(), op, rhe)); + substs.push(build_substitution(meta, name, access, op, rhe)); + } else{ + substs.push(Statement::UnderscoreSubstitution { meta: meta, op, rhe: rhe }); } } else{ return Result::Err(TupleError::tuple_general_error(meta.clone(),"The elements of the receiving tuple must be signals or variables.".to_string())); @@ -492,72 +811,43 @@ fn remove_tuples_from_statement(templates: &mut HashMap, f }, Statement::IfThenElse { meta, cond, if_case, else_case } => { - if cond.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a condition ".to_string())); - } else{ - let if_ok = remove_tuples_from_statement(templates, file_lib, *if_case)?; - let b_if = Box::new(if_ok); - if else_case.is_none(){ - Result::Ok(Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::None}) - }else { - let else_c = *(else_case.unwrap()); - let else_ok = remove_tuples_from_statement(templates, file_lib, else_c)?; - let b_else = Box::new(else_ok); - Result::Ok(Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::Some(b_else)}) - } + let if_ok = remove_tuples_from_statement(*if_case)?; + let b_if = Box::new(if_ok); + if else_case.is_none(){ + Result::Ok(Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::None}) + }else { + let else_c = *(else_case.unwrap()); + let else_ok = remove_tuples_from_statement(else_c)?; + let b_else = Box::new(else_ok); + Result::Ok(Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::Some(b_else)}) } } Statement::While { meta, cond, stmt } => { - if cond.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a condition ".to_string())); - } else{ - let while_ok = remove_tuples_from_statement(templates, file_lib, *stmt)?; - let b_while = Box::new(while_ok); - Result::Ok(Statement::While { meta : meta, cond : cond, stmt : b_while}) - } + let while_ok = remove_tuples_from_statement(*stmt)?; + let b_while = Box::new(while_ok); + Result::Ok(Statement::While { meta : meta, cond : cond, stmt : b_while}) } Statement::LogCall {meta, args } => { let mut newargs = Vec::new(); for arg in args { match arg { - LogArgument::LogStr(str) => {newargs.push(LogArgument::LogStr(str));}, + LogArgument::LogStr(str) => { + newargs.push(LogArgument::LogStr(str)); + }, LogArgument::LogExp(exp) => { - let mut args2 = separate_tuple_for_logcall(vec![exp]); - newargs.append(&mut args2); + let mut args2 = separate_tuple_for_logcall(vec![exp]); + newargs.append(&mut args2); }, } } Result::Ok(build_log_call(meta, newargs)) } - Statement::Assert { meta, arg} => { Result::Ok(build_assert(meta, arg))} - Statement::Return { meta, value: arg}=> { - if arg.contains_tuple(){ - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a function ".to_string())); - } - else{ Result::Ok(build_return(meta, arg))} - } - Statement::ConstraintEquality {meta, lhe, rhe } => { - if lhe.contains_tuple() || rhe.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used with the operator === ".to_string())); - } - else{ Result::Ok(build_constraint_equality(meta, lhe, rhe)) } - } - Statement::Declaration { meta , xtype , name , - dimensions, .. } => - { - for exp in dimensions.clone(){ - if exp.contains_tuple(){ - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); - } - } - Result::Ok(build_declaration(meta, xtype, name, dimensions)) - } Statement::InitializationBlock { meta, xtype, initializations } => { let mut new_inits = Vec::new(); for stmt in initializations { - let stmt_ok = remove_tuples_from_statement(templates, file_lib, stmt)?; + let stmt_ok = remove_tuples_from_statement(stmt)?; new_inits.push(stmt_ok); } Result::Ok(Statement::InitializationBlock { meta: meta, xtype: xtype, initializations: new_inits }) @@ -565,23 +855,25 @@ fn remove_tuples_from_statement(templates: &mut HashMap, f Statement::Block { meta, stmts } => { let mut new_stmts = Vec::new(); for stmt in stmts { - let stmt_ok = remove_tuples_from_statement(templates, file_lib, stmt)?; + let stmt_ok = remove_tuples_from_statement(stmt)?; new_stmts.push(stmt_ok); } Result::Ok(Statement::Block { meta : meta, stmts: new_stmts}) } Statement::Substitution { meta, var, op, rhe, access} => { - let new_rhe = remove_tuple_from_expression(templates, file_lib, rhe)?; - if new_rhe.is_tuple() { + let new_rhe = remove_tuple_from_expression(rhe); + if new_rhe.is_tuple() { return Result::Err(TupleError::tuple_general_error(meta.clone(),"Left-side of the statement is not a tuple".to_string())); } if var != "_" { Result::Ok(Statement::Substitution { meta: meta.clone(), var: var, access: access, op: op, rhe: new_rhe }) } - else {//If this - Result::Ok(build_block(meta, Vec::new())) + else { + Result::Ok(Statement::UnderscoreSubstitution { meta: meta, op, rhe: new_rhe }) } } + Statement::UnderscoreSubstitution { .. } => unreachable!(), + _ => Result::Ok(stm), // The rest of cases do not change the stmt (cannot contain tuples) } } @@ -602,77 +894,25 @@ fn separate_tuple_for_logcall(values: Vec) -> Vec { } -pub fn remove_tuple_from_expression(templates : &mut HashMap, file_lib : & FileLibrary, exp : Expression) -> Result{ +pub fn remove_tuple_from_expression(exp : Expression) -> Expression{ use Expression::*; - match exp.clone() { - ArrayInLine { meta, values } => { - for value in values{ - if value.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); - } - } - Result::Ok(exp) - }, - UniformArray { meta, value, dimension } => { - if value.contains_tuple() || dimension.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); - } - Result::Ok(exp) - }, - Number(_, _) => { Result::Ok(exp) }, - Variable { meta, .. } => { - if exp.contains_tuple(){ - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to access an array ".to_string())); - - } - Result::Ok(exp) - }, - InfixOp { meta, lhe, rhe, .. } => { - if lhe.contains_tuple() || rhe.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in the middle of an operation".to_string())); - } - Result::Ok(exp) - }, - PrefixOp { meta, rhe, .. } => { - if rhe.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in the middle of an operation".to_string())); - } - Result::Ok(exp) - }, - InlineSwitchOp { meta, cond, if_true, if_false } => { - if cond.contains_tuple() || if_true.contains_tuple() || if_false.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside an inline switch".to_string())); - } - Result::Ok(exp) - }, - Call { meta, args, .. } => { - for value in args{ - if value.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used as a parameter of a function call".to_string())); - } - } - Result::Ok(exp) - }, + match exp { AnonymousComp { .. } => { unreachable!(); } Tuple { meta, values } => { let mut unfolded_values = Vec::new(); for val in values { - let exp = remove_tuple_from_expression(templates, file_lib, val)?; + let exp = remove_tuple_from_expression(val); if let Tuple { values: mut values2, ..} = exp { unfolded_values.append(&mut values2); } else { unfolded_values.push(exp); } } - Result::Ok(build_tuple(meta, unfolded_values)) - }, - ParallelOp { meta, rhe} => { - if rhe.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in a parallel operator ".to_string())); - } - Result::Ok(exp) + build_tuple(meta, unfolded_values) }, + _ => exp, } } + diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index f8ca3b26f..8ebece7af 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -201,6 +201,11 @@ pub enum Statement { op: AssignOp, rhe: Expression, }, + UnderscoreSubstitution{ + meta: Meta, + op: AssignOp, + rhe: Expression, + }, ConstraintEquality { meta: Meta, lhe: Expression, diff --git a/program_structure/src/abstract_syntax_tree/statement_impl.rs b/program_structure/src/abstract_syntax_tree/statement_impl.rs index 496eac1dd..48f681012 100644 --- a/program_structure/src/abstract_syntax_tree/statement_impl.rs +++ b/program_structure/src/abstract_syntax_tree/statement_impl.rs @@ -15,6 +15,7 @@ impl Statement { | ConstraintEquality { meta, .. } | InitializationBlock { meta, .. } => meta, | MultSubstitution { meta, ..} => meta, + | UnderscoreSubstitution { meta, .. } => meta, } } pub fn get_mut_meta(&mut self) -> &mut Meta { @@ -31,6 +32,7 @@ impl Statement { | ConstraintEquality { meta, .. } | InitializationBlock { meta, .. } => meta, | MultSubstitution { meta, ..} => meta, + | UnderscoreSubstitution { meta, .. } => meta, } } @@ -82,6 +84,15 @@ impl Statement { false } } + + pub fn is_underscore_substitution(&self) -> bool { + use Statement::UnderscoreSubstitution; + if let UnderscoreSubstitution { .. } = self { + true + } else { + false + } + } pub fn is_constraint_equality(&self) -> bool { use Statement::ConstraintEquality; if let ConstraintEquality { .. } = self { @@ -146,11 +157,15 @@ impl FillMeta for Statement { LogCall { meta, args, .. } => fill_log_call(meta, args, file_id, element_id), Block { meta, stmts, .. } => fill_block(meta, stmts, file_id, element_id), Assert { meta, arg, .. } => fill_assert(meta, arg, file_id, element_id), + UnderscoreSubstitution { meta, rhe, .. } => { + fill_underscore_substitution(meta, rhe, file_id, element_id); + }, } } } + fn fill_conditional( meta: &mut Meta, cond: &mut Expression, @@ -268,3 +283,9 @@ fn fill_assert(meta: &mut Meta, arg: &mut Expression, file_id: usize, element_id meta.set_file_id(file_id); arg.fill(file_id, element_id); } + +fn fill_underscore_substitution(meta: &mut Meta, rhe: &mut Expression, file_id: usize, element_id: &mut usize) { + meta.set_file_id(file_id); + rhe.fill(file_id, element_id); + +} \ No newline at end of file diff --git a/program_structure/src/program_library/error_code.rs b/program_structure/src/program_library/error_code.rs index ccb2accc7..42a50f045 100644 --- a/program_structure/src/program_library/error_code.rs +++ b/program_structure/src/program_library/error_code.rs @@ -14,6 +14,7 @@ pub enum ReportCode { UnableToTypeFunction, UnreachableConstraints, UnreachableTags, + UnreachableSignals, UnknownIndex, UnknownDimension, SameFunctionDeclaredTwice, @@ -101,8 +102,6 @@ impl fmt::Display for ReportCode { UninitializedSymbolInExpression => "T2003", UnableToTypeFunction => "T2004", UnreachableConstraints => "T2005", - UnreachableTags => "T2015", - MainComponentWithTags => "T2016", SameFunctionDeclaredTwice => "T2006", SameTemplateDeclaredTwice => "T2007", SameSymbolDeclaredTwice => "T2008", @@ -152,6 +151,9 @@ impl fmt::Display for ReportCode { MustBeSameDimension => "T2046", MustBeArithmetic => "T2047", OutputTagCannotBeModifiedOutside => "T2048", + UnreachableTags => "T2049", + UnreachableSignals => "T2050", + MainComponentWithTags => "T2051", RuntimeError => "T3001", RuntimeWarning => "T3002", UnknownDimension => "T20460", @@ -171,7 +173,7 @@ impl fmt::Display for ReportCode { CustomGatesPragmaError => "CG04", CustomGatesVersionError => "CG05", AnonymousCompError => "TAC01", - TupleError => "TAC02" + TupleError => "TAC02", }; f.write_str(string_format) } diff --git a/type_analysis/src/analyzers/custom_gate_analysis.rs b/type_analysis/src/analyzers/custom_gate_analysis.rs index 6e4e879de..8269bbde2 100644 --- a/type_analysis/src/analyzers/custom_gate_analysis.rs +++ b/type_analysis/src/analyzers/custom_gate_analysis.rs @@ -101,6 +101,24 @@ pub fn custom_gate_analysis( custom_gate_analysis(custom_gate_name, stmt, errors, warnings); } } + UnderscoreSubstitution { meta, op, .. } => { + use AssignOp::*; + match op { + AssignConstraintSignal => { + let mut error = Report::error( + String::from("Added constraint inside custom template"), + ReportCode::CustomGateConstraintError + ); + error.add_primary( + meta.location.clone(), + meta.file_id.unwrap(), + String::from("Added constraint") + ); + errors.push(error); + } + _ => {} + } + } _ => {} }; } diff --git a/type_analysis/src/analyzers/functions_free_of_template_elements.rs b/type_analysis/src/analyzers/functions_free_of_template_elements.rs index b508f05c6..b5330864c 100644 --- a/type_analysis/src/analyzers/functions_free_of_template_elements.rs +++ b/type_analysis/src/analyzers/functions_free_of_template_elements.rs @@ -115,6 +115,19 @@ fn analyse_statement( Return { value, .. } => { analyse_expression(value, function_names, reports); } + UnderscoreSubstitution { meta, op, rhe } => { + if op.is_signal_operator() { + let mut report = Report::error( + "Function uses template operators".to_string(), + ReportCode::UndefinedFunction, + ); + let location = + file_definition::generate_file_location(meta.get_start(), meta.get_end()); + report.add_primary(location, file_id, "Template operator found".to_string()); + reports.push(report); + } + analyse_expression(rhe, function_names, reports); + }, } } diff --git a/type_analysis/src/analyzers/symbol_analysis.rs b/type_analysis/src/analyzers/symbol_analysis.rs index 0ecc67167..1d92ce25e 100644 --- a/type_analysis/src/analyzers/symbol_analysis.rs +++ b/type_analysis/src/analyzers/symbol_analysis.rs @@ -168,6 +168,9 @@ fn analyze_statement( Statement::Return { value, .. } => { analyze_expression(value, file_id, function_info, template_info, reports, environment) } + Statement::UnderscoreSubstitution { rhe, .. } => { + analyze_expression(rhe, file_id, function_info, template_info, reports, environment); + } Statement::Substitution { meta, var, access, rhe, .. } => { analyze_expression(rhe, file_id, function_info, template_info, reports, environment); treat_variable( diff --git a/type_analysis/src/analyzers/type_check.rs b/type_analysis/src/analyzers/type_check.rs index aa5f5cc95..82c153c88 100644 --- a/type_analysis/src/analyzers/type_check.rs +++ b/type_analysis/src/analyzers/type_check.rs @@ -379,6 +379,21 @@ fn type_statement( analysis_information.environment.remove_variable_block(); } MultSubstitution { .. } => unreachable!(), + UnderscoreSubstitution { rhe , ..} => { + let rhe_response = type_expression(rhe, program_archive, analysis_information); + let rhe_type = if let Result::Ok(r_type) = rhe_response { + r_type + } else { + return; + }; + if rhe_type.is_template() { + add_report( + ReportCode::MustBeArithmetic, + rhe.get_meta(), + &mut analysis_information.reports, + ); + } + }, } } fn type_expression( diff --git a/type_analysis/src/analyzers/unknown_known_analysis.rs b/type_analysis/src/analyzers/unknown_known_analysis.rs index 7806c5174..01c4cb582 100644 --- a/type_analysis/src/analyzers/unknown_known_analysis.rs +++ b/type_analysis/src/analyzers/unknown_known_analysis.rs @@ -152,6 +152,15 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma } } } + UnderscoreSubstitution { op, rhe, .. } => { + let _expression_tag = tag(rhe, &environment); + if *op == AssignOp::AssignConstraintSignal { + constraints_declared = true; + if is_non_quadratic(rhe, &environment) { + add_report(ReportCode::NonQuadratic, rhe.get_meta(), file_id, &mut reports); + } + } + }, ConstraintEquality { lhe, rhe, .. } => { constraints_declared = true; if is_non_quadratic(lhe, &environment) { diff --git a/type_analysis/src/decorators/constants_handler.rs b/type_analysis/src/decorators/constants_handler.rs index 11782ddc9..bbffe78d7 100644 --- a/type_analysis/src/decorators/constants_handler.rs +++ b/type_analysis/src/decorators/constants_handler.rs @@ -291,7 +291,8 @@ fn expand_statement(stmt: &mut Statement, environment: &mut ExpressionHolder) { LogCall { args, .. } => expand_log_call(args, environment), Assert { arg, .. } => expand_assert(arg, environment), Block { stmts, .. } => expand_block(stmts, environment), - MultSubstitution { .. } => unreachable!() + MultSubstitution { .. } => unreachable!(), + UnderscoreSubstitution { rhe, .. } => expand_underscore_substitution(rhe, environment), } } @@ -362,6 +363,13 @@ fn expand_substitution( } } +fn expand_underscore_substitution( + rhe: &mut Expression, + environment: &ExpressionHolder, +) { + *rhe = expand_expression(rhe.clone(), environment); +} + fn expand_constraint_equality( lhe: &mut Expression, rhe: &mut Expression, diff --git a/type_analysis/src/decorators/type_reduction.rs b/type_analysis/src/decorators/type_reduction.rs index bade4526a..3e42a1027 100644 --- a/type_analysis/src/decorators/type_reduction.rs +++ b/type_analysis/src/decorators/type_reduction.rs @@ -48,7 +48,10 @@ fn reduce_types_in_statement(stmt: &mut Statement, environment: &mut Environment ConstraintEquality { lhe, rhe, .. } => { reduce_types_in_constraint_equality(lhe, rhe, environment) } - MultSubstitution { .. } => unreachable!() + MultSubstitution { .. } => unreachable!(), + UnderscoreSubstitution { rhe, .. } => { + reduce_types_in_expression(rhe, environment); + }, } } From 60355380f738a7f330e489809f3f5d427d96feb9 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Tue, 7 Feb 2023 17:27:05 +0100 Subject: [PATCH 14/70] improving --O1 simplification: avoiding unnecessary constraint normalizations --- circom_algebra/src/algebra.rs | 12 +++++++++--- circom_algebra/src/simplification_utils.rs | 7 ++++++- .../src/constraint_simplification.rs | 18 ++++++++++++++---- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/circom_algebra/src/algebra.rs b/circom_algebra/src/algebra.rs index 7ddeb6d6c..bfddd49e2 100644 --- a/circom_algebra/src/algebra.rs +++ b/circom_algebra/src/algebra.rs @@ -762,14 +762,20 @@ impl ArithmeticExpression { ) { use ArithmeticExpression::*; match expr { - Linear { coefficients } => raw_substitution(coefficients, substitution, field), + Linear { coefficients } => { + raw_substitution(coefficients, substitution, field); + *coefficients = remove_zero_value_coefficients(std::mem::take(coefficients)); + } Signal { symbol } if *symbol == substitution.from => { *expr = Linear { coefficients: substitution.to.clone() }; } Quadratic { a, b, c } => { raw_substitution(a, substitution, field); + *a = remove_zero_value_coefficients(std::mem::take(a)); raw_substitution(b, substitution, field); + *b = remove_zero_value_coefficients(std::mem::take(b)); raw_substitution(c, substitution, field); + *c = remove_zero_value_coefficients(std::mem::take(c)); } _ => {} } @@ -1137,7 +1143,7 @@ impl Constraint { raw_substitution(&mut constraint.a, substitution, field); raw_substitution(&mut constraint.b, substitution, field); raw_substitution(&mut constraint.c, substitution, field); - Constraint::fix_constraint(constraint, field); + //Constraint::fix_constraint(constraint, field); } pub fn remove_zero_value_coefficients(constraint: &mut Constraint) { @@ -1284,7 +1290,7 @@ fn raw_substitution( ArithmeticExpression::multiply_coefficients_by_constant(&val, &mut coefficients, field); ArithmeticExpression::add_coefficients_to_coefficients(&coefficients, change, field); } - *change = remove_zero_value_coefficients(std::mem::take(change)); + //*change = remove_zero_value_coefficients(std::mem::take(change)); } fn remove_zero_value_coefficients(raw_expression: HashMap) -> HashMap diff --git a/circom_algebra/src/simplification_utils.rs b/circom_algebra/src/simplification_utils.rs index f808a867b..6219843fb 100644 --- a/circom_algebra/src/simplification_utils.rs +++ b/circom_algebra/src/simplification_utils.rs @@ -493,14 +493,17 @@ pub fn debug_substitution_check(substitutions: &HashMap) -> bool { result } -pub fn fast_encoded_constraint_substitution(c: &mut C, enc: &HashMap, field: &BigInt) { +pub fn fast_encoded_constraint_substitution(c: &mut C, enc: &HashMap, field: &BigInt)-> bool { let signals = C::take_cloned_signals(c); + let mut applied_substitution = false; for signal in signals { if let Some(expr) = HashMap::get(enc, &signal) { let sub = S::new(signal, expr.clone()).unwrap(); C::apply_substitution(c, &sub, field); + applied_substitution = true; } } + applied_substitution } pub fn fast_encoded_substitution_substitution(s: &mut S, enc: &HashMap, field: &BigInt) { @@ -658,6 +661,7 @@ pub fn check_substitutions( let mut cons = S::substitution_into_constraint(s.clone(), field); for s_2 in subs_2{ C::apply_substitution(&mut cons, s_2, field); + C::fix_constraint(&mut cons, field); } if !cons.is_empty(){ println!("ERROR: FOUND NOT EMPTY SUBS"); @@ -672,6 +676,7 @@ pub fn check_substitutions( let mut cons = S::substitution_into_constraint(s.clone(), field); for s_2 in subs_1{ C::apply_substitution(&mut cons, s_2, field); + C::fix_constraint(&mut cons, field); } if !cons.is_empty(){ println!("ERROR: FOUND NOT EMPTY SUBS"); diff --git a/constraint_list/src/constraint_simplification.rs b/constraint_list/src/constraint_simplification.rs index af57e1bde..6c9b33989 100644 --- a/constraint_list/src/constraint_simplification.rs +++ b/constraint_list/src/constraint_simplification.rs @@ -362,6 +362,7 @@ fn apply_substitution_to_map( let c_id = *c_id; let mut constraint = storage.read_constraint(c_id).unwrap(); C::apply_substitution(&mut constraint, substitution, field); + C::fix_constraint(&mut constraint, field); if C::is_linear(&constraint) { linear.push_back(c_id); } @@ -486,10 +487,14 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { LinkedList::append(&mut lconst, &mut cons); let mut substitutions = build_encoded_fast_substitutions(subs); for constraint in &mut linear { - fast_encoded_constraint_substitution(constraint, &substitutions, &field); + if fast_encoded_constraint_substitution(constraint, &substitutions, &field){ + C::fix_constraint(constraint, &field); + } } for constraint in &mut cons_equalities { - fast_encoded_constraint_substitution(constraint, &substitutions, &field); + if fast_encoded_constraint_substitution(constraint, &substitutions, &field){ + C::fix_constraint(constraint, &field); + } } for signal in substitutions.keys().cloned() { deleted.insert(signal); @@ -508,7 +513,9 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { LinkedList::append(&mut lconst, &mut cons); let substitutions = build_encoded_fast_substitutions(subs); for constraint in &mut linear { - fast_encoded_constraint_substitution(constraint, &substitutions, &field); + if fast_encoded_constraint_substitution(constraint, &substitutions, &field){ + C::fix_constraint(constraint, &field); + } } for signal in substitutions.keys().cloned() { deleted.insert(signal); @@ -555,7 +562,9 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { // println!("End of cluster simplification: {} ms", dur); LinkedList::append(&mut lconst, &mut cons); for constraint in &mut lconst { - fast_encoded_constraint_substitution(constraint, &substitutions, &field); + if fast_encoded_constraint_substitution(constraint, &substitutions, &field){ + C::fix_constraint(constraint, &field); + } } substitutions } else { @@ -616,6 +625,7 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { for substitution in &substitutions { C::apply_substitution(constraint, substitution, &field); } + C::fix_constraint(constraint, &field); } linear = apply_substitution_to_map( &mut constraint_storage, From f21965ce7f7c1a84c36c3c79f4759585f045ad78 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Tue, 7 Feb 2023 23:44:06 +0100 Subject: [PATCH 15/70] fixing minors --- constraint_list/src/constraint_simplification.rs | 6 ++---- constraint_list/src/non_linear_utils.rs | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/constraint_list/src/constraint_simplification.rs b/constraint_list/src/constraint_simplification.rs index 6c9b33989..99476cff5 100644 --- a/constraint_list/src/constraint_simplification.rs +++ b/constraint_list/src/constraint_simplification.rs @@ -585,16 +585,14 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { crate::state_utils::empty_encoding_constraints(&mut smp.dag_encoding); let _dur = now.elapsed().unwrap().as_millis(); // println!("Storages built in {} ms", dur); - if remove_unused { - no_rounds -= 1; - } + no_rounds -= 1; (with_linear, storage) }; let mut round_id = 0; let _ = round_id; let mut linear = with_linear; - let mut apply_round = no_rounds > 0 && !linear.is_empty(); + let mut apply_round = apply_linear && no_rounds > 0 && !linear.is_empty(); let mut non_linear_map = if apply_round || remove_unused { // println!("Building non-linear map"); let now = SystemTime::now(); diff --git a/constraint_list/src/non_linear_utils.rs b/constraint_list/src/non_linear_utils.rs index d3107c190..91fd5078e 100644 --- a/constraint_list/src/non_linear_utils.rs +++ b/constraint_list/src/non_linear_utils.rs @@ -15,6 +15,7 @@ pub fn obtain_and_simplify_non_linear( for frame in frames { fast_encoded_constraint_substitution(&mut constraint, frame, &field); } + C::fix_constraint(&mut constraint, &field); if C::is_linear(&constraint) { linear.push_back(constraint); } else { From e9b75cb247eca09314c896ebc0ae0b7d31e90574 Mon Sep 17 00:00:00 2001 From: miguelis Date: Fri, 10 Feb 2023 11:46:20 +0100 Subject: [PATCH 16/70] Updating documentation about tags --- mkdocs/docs/circom-language/tags.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/mkdocs/docs/circom-language/tags.md b/mkdocs/docs/circom-language/tags.md index 8d49211d3..d08b703d9 100644 --- a/mkdocs/docs/circom-language/tags.md +++ b/mkdocs/docs/circom-language/tags.md @@ -35,6 +35,22 @@ Then, whenever the previous template is instantiated, the compiler checks if the It is important to highlight that the compiler does never make any check about the validity of the tags. It is the programmer's responsability to include the constraints and executable code to guarantee that the inteded meaning of each signal is always true. +When doing a substitution from a tagged signal to another signal, the tags are always inherited by it (even if it is not declared with it). For instance, + +``` +template A() { + signal input {binary} in; + signal intermediate; + signal {binary} out; + intermediate <== in; + out <== intermediate; +} +``` + +In this example, the intermediate signal inherits the binary tag from in even though it is not declared as binary. Finally, out also receives the tag as expected, since the input is binary. + + + Let us consider another well-known template that the programmer can use to guarantee that the output signal is always binary. ``` @@ -85,4 +101,4 @@ template Bits2Num(n) { ``` ###Tags in signal arrays -Every signal in an array has exactly the same tag value. Then, the tag is accessed directly from the array name instead of accessing from a particular signal in the array. Similarly to the previous erroneous example: if a particular position of the array is modified, then the tag value of the whole array cannot be modified at all. \ No newline at end of file +Every signal in an array has exactly the same tag value. Then, the tag is accessed directly from the array name instead of accessing from a particular signal in the array. Similarly to the previous erroneous example: if a particular position of the array is modified, then the tag value of the whole array cannot be modified at all. From cbda75d3b019ad9cfd4907efb3db6b7beb7bbc98 Mon Sep 17 00:00:00 2001 From: miguelis Date: Fri, 10 Feb 2023 11:48:31 +0100 Subject: [PATCH 17/70] Update RELEASES.md --- RELEASES.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index d9e4c7e1b..45fdf5d89 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,17 @@ # Release notes +## February 10, 2023 circom 2.1.4 + + #### Extensions + - Improving the efficiency of the parser regarding the anonnymous components and tuples. + - Improving the substitution process: better compilation times for --O1 and --O2. + - Improving the handling of the underscore substitution. + - Extending the substitution to allow the inheritance of signal tags. + - Removing unused signal when applying --O1. (If a signal does not appear in any constraint, it is removed). + + #### Fixed Bugs + - Solving bug in the release of the memory of the components. + ## January 16, 2023 circom 2.1.3 #### Extensions - Improving error messages: invalid access and invalid assignment. From ca3345681549c859af1f3f42128e53e3e43fe5e2 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Fri, 10 Feb 2023 11:55:25 +0100 Subject: [PATCH 18/70] updating version to 2.1.4 --- Cargo.lock | 18 +++++++++--------- circom/Cargo.toml | 2 +- circom_algebra/Cargo.toml | 2 +- code_producers/Cargo.toml | 2 +- compiler/Cargo.toml | 2 +- constraint_generation/Cargo.toml | 2 +- constraint_list/Cargo.toml | 2 +- parser/Cargo.toml | 2 +- program_structure/Cargo.toml | 2 +- type_analysis/Cargo.toml | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d90f2b036..cbfbbc8cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,7 +155,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "circom" -version = "2.1.3" +version = "2.1.4" dependencies = [ "ansi_term", "clap", @@ -172,7 +172,7 @@ dependencies = [ [[package]] name = "circom_algebra" -version = "2.1.3" +version = "2.1.4" dependencies = [ "constant_tracking", "num-bigint-dig", @@ -205,7 +205,7 @@ dependencies = [ [[package]] name = "code_producers" -version = "2.1.3" +version = "2.1.4" dependencies = [ "handlebars", "lz_fnv", @@ -234,7 +234,7 @@ dependencies = [ [[package]] name = "compiler" -version = "2.1.3" +version = "2.1.4" dependencies = [ "code_producers", "constant_tracking", @@ -255,7 +255,7 @@ version = "2.0.0" [[package]] name = "constraint_generation" -version = "2.1.3" +version = "2.1.4" dependencies = [ "ansi_term", "circom_algebra", @@ -270,7 +270,7 @@ dependencies = [ [[package]] name = "constraint_list" -version = "2.1.3" +version = "2.1.4" dependencies = [ "circom_algebra", "constraint_writers", @@ -655,7 +655,7 @@ checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" [[package]] name = "parser" -version = "2.1.1" +version = "2.1.4" dependencies = [ "lalrpop", "lalrpop-util", @@ -764,7 +764,7 @@ dependencies = [ [[package]] name = "program_structure" -version = "2.1.3" +version = "2.1.4" dependencies = [ "codespan", "codespan-reporting", @@ -1188,7 +1188,7 @@ dependencies = [ [[package]] name = "type_analysis" -version = "2.1.0" +version = "2.1.4" dependencies = [ "num-bigint-dig", "num-traits", diff --git a/circom/Cargo.toml b/circom/Cargo.toml index da4a5c73a..b54a3559d 100644 --- a/circom/Cargo.toml +++ b/circom/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "circom" -version = "2.1.3" +version = "2.1.4" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/circom_algebra/Cargo.toml b/circom_algebra/Cargo.toml index 28c3f15be..30e713dbf 100644 --- a/circom_algebra/Cargo.toml +++ b/circom_algebra/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "circom_algebra" -version = "2.1.3" +version = "2.1.4" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/code_producers/Cargo.toml b/code_producers/Cargo.toml index 8c8647536..9a19c504a 100644 --- a/code_producers/Cargo.toml +++ b/code_producers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "code_producers" -version = "2.1.3" +version = "2.1.4" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 4c753e81b..aa9438958 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "compiler" -version = "2.1.3" +version = "2.1.4" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/constraint_generation/Cargo.toml b/constraint_generation/Cargo.toml index 131eb673a..3621fd258 100644 --- a/constraint_generation/Cargo.toml +++ b/constraint_generation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "constraint_generation" -version = "2.1.3" +version = "2.1.4" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/constraint_list/Cargo.toml b/constraint_list/Cargo.toml index 91cbce78d..41880292f 100644 --- a/constraint_list/Cargo.toml +++ b/constraint_list/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "constraint_list" -version = "2.1.3" +version = "2.1.4" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 03f7837a2..6be5afa58 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "2.1.1" +version = "2.1.4" authors = ["Costa Group UCM","iden3"] edition = "2018" build = "build.rs" diff --git a/program_structure/Cargo.toml b/program_structure/Cargo.toml index 926f954c9..5ee40ff9c 100644 --- a/program_structure/Cargo.toml +++ b/program_structure/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "program_structure" -version = "2.1.3" +version = "2.1.4" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/type_analysis/Cargo.toml b/type_analysis/Cargo.toml index 722ae8d35..432b5c1ea 100644 --- a/type_analysis/Cargo.toml +++ b/type_analysis/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "type_analysis" -version = "2.1.0" +version = "2.1.4" authors = ["Costa Group UCM","iden3"] edition = "2018" From 7b50264a08bb9c202d0e6483f79b3123b6068ca9 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Mon, 13 Feb 2023 16:05:02 +0100 Subject: [PATCH 19/70] improving parallel: array assignments of outputs --- .../load_bucket.rs | 23 ++++++++++++------- .../intermediate_representation/translate.rs | 2 ++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/compiler/src/intermediate_representation/load_bucket.rs b/compiler/src/intermediate_representation/load_bucket.rs index 05b2000c7..f6ec63c29 100644 --- a/compiler/src/intermediate_representation/load_bucket.rs +++ b/compiler/src/intermediate_representation/load_bucket.rs @@ -9,6 +9,7 @@ pub struct LoadBucket { pub message_id: usize, pub address_type: AddressType, pub src: LocationRule, + pub context: InstrContext, } impl IntoInstruction for LoadBucket { @@ -230,15 +231,18 @@ impl WriteC for LoadBucket { prologue.push(format!("{{")); prologue.push(format!("int aux1 = {};",cmp_index_ref.clone())); prologue.push(format!("int aux2 = {};",src_index.clone())); + // check each one of the outputs of the assignment, we add i to check them one by one + prologue.push(format!("for (int i = 0; i < {}; i++) {{",self.context.size)); prologue.push(format!( - "std::unique_lock lk({}->componentMemory[{}[aux1]].mutexes[aux2]);", + "std::unique_lock lk({}->componentMemory[{}[aux1]].mutexes[aux2 + i]);", CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) ); prologue.push(format!( - "{}->componentMemory[{}[aux1]].cvs[aux2].wait(lk, [{},{},aux1,aux2]() {{return {}->componentMemory[{}[aux1]].outputIsSet[aux2];}});", + "{}->componentMemory[{}[aux1]].cvs[aux2 + i].wait(lk, [{},{},aux1,aux2, i]() {{return {}->componentMemory[{}[aux1]].outputIsSet[aux2 + i];}});", CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) ); + prologue.push(format!("}}")); prologue.push(format!("}}")); } } @@ -254,15 +258,18 @@ impl WriteC for LoadBucket { prologue.push(format!("{{")); prologue.push(format!("int aux1 = {};",cmp_index_ref.clone())); prologue.push(format!("int aux2 = {};",src_index.clone())); - prologue.push(format!( - "std::unique_lock lk({}->componentMemory[{}[aux1]].mutexes[aux2]);", + // check each one of the outputs of the assignment, we add i to check them one by one + prologue.push(format!("for (int i = 0; i < {}; i++) {{",self.context.size)); + prologue.push(format!( + "std::unique_lock lk({}->componentMemory[{}[aux1]].mutexes[aux2 + i]);", CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) ); - prologue.push(format!( - "{}->componentMemory[{}[aux1]].cvs[aux2].wait(lk, [{},{},aux1,aux2]() {{return {}->componentMemory[{}[aux1]].outputIsSet[aux2];}});", - CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, - MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) + prologue.push(format!( + "{}->componentMemory[{}[aux1]].cvs[aux2 + i].wait(lk, [{},{},aux1,aux2, i]() {{return {}->componentMemory[{}[aux1]].outputIsSet[aux2 + i];}});", + CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, + MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) ); + prologue.push(format!("}}")); prologue.push(format!("}}")); // end of case parallel, in case no parallel we do nothing diff --git a/compiler/src/intermediate_representation/translate.rs b/compiler/src/intermediate_representation/translate.rs index c44c17a0f..1a018276a 100644 --- a/compiler/src/intermediate_representation/translate.rs +++ b/compiler/src/intermediate_representation/translate.rs @@ -1087,6 +1087,7 @@ impl ProcessedSymbol { line: self.line, message_id: self.message_id, address_type: dest_type, + context: InstrContext { size: self.length }, } .allocate() } else { @@ -1100,6 +1101,7 @@ impl ProcessedSymbol { address_type: xtype, message_id: self.message_id, src: LocationRule::Indexed { location: address, template_header: None }, + context: InstrContext { size: self.length }, } .allocate() } From c263ea9aa9256dbef81cda9aade18a2eab804079 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Mon, 20 Feb 2023 21:18:26 +0100 Subject: [PATCH 20/70] improving parallel: improving efficiency by updating numThread while waiting --- compiler/src/circuit_design/template.rs | 14 ++++++-- .../call_bucket.rs | 9 +++--- .../load_bucket.rs | 32 +++++++++++++------ .../store_bucket.rs | 16 +++++----- 4 files changed, 46 insertions(+), 25 deletions(-) diff --git a/compiler/src/circuit_design/template.rs b/compiler/src/circuit_design/template.rs index c2a3bf2e1..bb15c99a5 100644 --- a/compiler/src/circuit_design/template.rs +++ b/compiler/src/circuit_design/template.rs @@ -305,10 +305,18 @@ impl TemplateCodeInfo { } if parallel { // parallelism - run_body.push(format!("ctx->numThreadMutex.lock();")); + // set to true all outputs + run_body.push(format!("for (uint i = 0; i < {}; i++) {{", &self.number_of_outputs.to_string())); + run_body.push(format!("{}->componentMemory[{}].mutexes[i].lock();",CIRCOM_CALC_WIT,CTX_INDEX)); + run_body.push(format!("{}->componentMemory[{}].outputIsSet[i]=true;",CIRCOM_CALC_WIT,CTX_INDEX)); + run_body.push(format!("{}->componentMemory[{}].mutexes[i].unlock();",CIRCOM_CALC_WIT,CTX_INDEX)); + run_body.push(format!("{}->componentMemory[{}].cvs[i].notify_all();",CIRCOM_CALC_WIT,CTX_INDEX)); + run_body.push(format!("}}")); + //parallelism + run_body.push(format!("ctx->numThreadMutex.lock();")); run_body.push(format!("ctx->numThread--;")); - run_body.push(format!("ctx->numThreadMutex.unlock();")); - run_body.push(format!("ctx->ntcvs.notify_one();")); + run_body.push(format!("ctx->numThreadMutex.unlock();")); + run_body.push(format!("ctx->ntcvs.notify_one();")); } // to release the memory of its subcomponents diff --git a/compiler/src/intermediate_representation/call_bucket.rs b/compiler/src/intermediate_representation/call_bucket.rs index b0171e109..190196c1a 100644 --- a/compiler/src/intermediate_representation/call_bucket.rs +++ b/compiler/src/intermediate_representation/call_bucket.rs @@ -523,10 +523,10 @@ impl WriteC for CallBucket { let mut thread_call_instr = vec![]; // parallelism + thread_call_instr.push(format!("{}->componentMemory[{}].sbct[{}] = std::thread({},{});",CIRCOM_CALC_WIT,CTX_INDEX,cmp_index_ref, sub_cmp_call_name, argument_list(sub_cmp_call_arguments))); thread_call_instr.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); thread_call_instr.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); - thread_call_instr.push(format!("{}->numThread++;",CIRCOM_CALC_WIT)); - thread_call_instr.push(format!("{}->componentMemory[{}].sbct[{}] = std::thread({},{});",CIRCOM_CALC_WIT,CTX_INDEX,cmp_index_ref, sub_cmp_call_name, argument_list(sub_cmp_call_arguments))); + thread_call_instr.push(format!("ctx->numThread++;")); thread_call_instr } @@ -570,11 +570,10 @@ impl WriteC for CallBucket { }; let mut call_instructions = vec![]; // parallelism + call_instructions.push(format!("{}->componentMemory[{}].sbct[{}] = std::thread({},{});",CIRCOM_CALC_WIT,CTX_INDEX,cmp_index_ref, sub_cmp_call_name, argument_list(sub_cmp_call_arguments.clone()))); call_instructions.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); call_instructions.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); - call_instructions.push(format!("{}->numThread++;",CIRCOM_CALC_WIT)); - call_instructions.push(format!("{}->componentMemory[{}].sbct[{}] = std::thread({},{});",CIRCOM_CALC_WIT,CTX_INDEX,cmp_index_ref, sub_cmp_call_name, argument_list(sub_cmp_call_arguments.clone()))); - + call_instructions.push(format!("ctx->numThread++;")); if let StatusInput::Unknown = status { let sub_cmp_counter_decrease_andcheck = format!("!({})",sub_cmp_counter_decrease); let if_condition = vec![sub_cmp_counter_decrease_andcheck]; diff --git a/compiler/src/intermediate_representation/load_bucket.rs b/compiler/src/intermediate_representation/load_bucket.rs index f6ec63c29..c731b5afe 100644 --- a/compiler/src/intermediate_representation/load_bucket.rs +++ b/compiler/src/intermediate_representation/load_bucket.rs @@ -233,6 +233,10 @@ impl WriteC for LoadBucket { prologue.push(format!("int aux2 = {};",src_index.clone())); // check each one of the outputs of the assignment, we add i to check them one by one prologue.push(format!("for (int i = 0; i < {}; i++) {{",self.context.size)); + prologue.push(format!("ctx->numThreadMutex.lock();")); + prologue.push(format!("ctx->numThread--;")); + prologue.push(format!("ctx->numThreadMutex.unlock();")); + prologue.push(format!("ctx->ntcvs.notify_one();")); prologue.push(format!( "std::unique_lock lk({}->componentMemory[{}[aux1]].mutexes[aux2 + i]);", CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) @@ -242,6 +246,9 @@ impl WriteC for LoadBucket { CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) ); + prologue.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); + prologue.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); + prologue.push(format!("ctx->numThread++;")); prologue.push(format!("}}")); prologue.push(format!("}}")); } @@ -260,15 +267,22 @@ impl WriteC for LoadBucket { prologue.push(format!("int aux2 = {};",src_index.clone())); // check each one of the outputs of the assignment, we add i to check them one by one prologue.push(format!("for (int i = 0; i < {}; i++) {{",self.context.size)); - prologue.push(format!( - "std::unique_lock lk({}->componentMemory[{}[aux1]].mutexes[aux2 + i]);", - CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) - ); - prologue.push(format!( - "{}->componentMemory[{}[aux1]].cvs[aux2 + i].wait(lk, [{},{},aux1,aux2, i]() {{return {}->componentMemory[{}[aux1]].outputIsSet[aux2 + i];}});", - CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, - MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) - ); + prologue.push(format!("ctx->numThreadMutex.lock();")); + prologue.push(format!("ctx->numThread--;")); + prologue.push(format!("ctx->numThreadMutex.unlock();")); + prologue.push(format!("ctx->ntcvs.notify_one();")); + prologue.push(format!( + "std::unique_lock lk({}->componentMemory[{}[aux1]].mutexes[aux2 + i]);", + CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) + ); + prologue.push(format!( + "{}->componentMemory[{}[aux1]].cvs[aux2 + i].wait(lk, [{},{},aux1,aux2, i]() {{return {}->componentMemory[{}[aux1]].outputIsSet[aux2 + i];}});", + CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, + MY_SUBCOMPONENTS, CIRCOM_CALC_WIT, MY_SUBCOMPONENTS) + ); + prologue.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); + prologue.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); + prologue.push(format!("ctx->numThread++;")); prologue.push(format!("}}")); prologue.push(format!("}}")); diff --git a/compiler/src/intermediate_representation/store_bucket.rs b/compiler/src/intermediate_representation/store_bucket.rs index 537a740ac..dd1c1148e 100644 --- a/compiler/src/intermediate_representation/store_bucket.rs +++ b/compiler/src/intermediate_representation/store_bucket.rs @@ -431,10 +431,10 @@ impl WriteC for StoreBucket { let mut thread_call_instr = vec![]; // parallelism - thread_call_instr.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); - thread_call_instr.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); - thread_call_instr.push(format!("{}->numThread++;",CIRCOM_CALC_WIT)); - thread_call_instr.push(format!("{}->componentMemory[{}].sbct[{}] = std::thread({},{});",CIRCOM_CALC_WIT,CTX_INDEX,cmp_index_ref, sub_cmp_call_name, argument_list(sub_cmp_call_arguments))); + thread_call_instr.push(format!("{}->componentMemory[{}].sbct[{}] = std::thread({},{});",CIRCOM_CALC_WIT,CTX_INDEX,cmp_index_ref, sub_cmp_call_name, argument_list(sub_cmp_call_arguments))); + thread_call_instr.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); + thread_call_instr.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); + thread_call_instr.push(format!("ctx->numThread++;")); thread_call_instr } @@ -478,10 +478,10 @@ impl WriteC for StoreBucket { }; let mut call_instructions = vec![]; // parallelism - call_instructions.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); - call_instructions.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); - call_instructions.push(format!("{}->numThread++;",CIRCOM_CALC_WIT)); - call_instructions.push(format!("{}->componentMemory[{}].sbct[{}] = std::thread({},{});",CIRCOM_CALC_WIT,CTX_INDEX,cmp_index_ref, sub_cmp_call_name, argument_list(sub_cmp_call_arguments.clone()))); + call_instructions.push(format!("{}->componentMemory[{}].sbct[{}] = std::thread({},{});",CIRCOM_CALC_WIT,CTX_INDEX,cmp_index_ref, sub_cmp_call_name, argument_list(sub_cmp_call_arguments.clone()))); + call_instructions.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); + call_instructions.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); + call_instructions.push(format!("ctx->numThread++;")); if let StatusInput::Unknown = status { let sub_cmp_counter_decrease_andcheck = format!("!({})",sub_cmp_counter_decrease); From 5cd2828a11b37836eb692208b14d54dd5d8f7376 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Wed, 22 Feb 2023 13:39:04 +0100 Subject: [PATCH 21/70] parallel: numThread as integer --- code_producers/src/c_elements/common/calcwit.hpp | 4 ++-- compiler/src/circuit_design/template.rs | 2 ++ compiler/src/intermediate_representation/call_bucket.rs | 4 +++- compiler/src/intermediate_representation/load_bucket.rs | 4 ++++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/code_producers/src/c_elements/common/calcwit.hpp b/code_producers/src/c_elements/common/calcwit.hpp index 5f5ae24e5..0b7c05575 100644 --- a/code_producers/src/c_elements/common/calcwit.hpp +++ b/code_producers/src/c_elements/common/calcwit.hpp @@ -32,9 +32,9 @@ class Circom_CalcWit { // parallelism std::mutex numThreadMutex; std::condition_variable ntcvs; - uint numThread; + int numThread; - uint maxThread; + int maxThread; // Functions called by the circuit Circom_CalcWit(Circom_Circuit *aCircuit, uint numTh = NMUTEXES); diff --git a/compiler/src/circuit_design/template.rs b/compiler/src/circuit_design/template.rs index bb15c99a5..399ca43e8 100644 --- a/compiler/src/circuit_design/template.rs +++ b/compiler/src/circuit_design/template.rs @@ -289,6 +289,7 @@ impl TemplateCodeInfo { run_body.push(format!("{};", declare_lvar(self.var_stack_depth))); run_body.push(format!("{};", declare_sub_component_aux())); run_body.push(format!("{};", declare_index_multiple_eq())); + for t in &self.body { let (mut instructions_body, _) = t.produce_c(producer, Some(parallel)); run_body.append(&mut instructions_body); @@ -315,6 +316,7 @@ impl TemplateCodeInfo { //parallelism run_body.push(format!("ctx->numThreadMutex.lock();")); run_body.push(format!("ctx->numThread--;")); + //run_body.push(format!("printf(\"%i \\n\", ctx->numThread);")); run_body.push(format!("ctx->numThreadMutex.unlock();")); run_body.push(format!("ctx->ntcvs.notify_one();")); } diff --git a/compiler/src/intermediate_representation/call_bucket.rs b/compiler/src/intermediate_representation/call_bucket.rs index 190196c1a..8a74c0e3b 100644 --- a/compiler/src/intermediate_representation/call_bucket.rs +++ b/compiler/src/intermediate_representation/call_bucket.rs @@ -525,8 +525,9 @@ impl WriteC for CallBucket { // parallelism thread_call_instr.push(format!("{}->componentMemory[{}].sbct[{}] = std::thread({},{});",CIRCOM_CALC_WIT,CTX_INDEX,cmp_index_ref, sub_cmp_call_name, argument_list(sub_cmp_call_arguments))); thread_call_instr.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); - thread_call_instr.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); + thread_call_instr.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); thread_call_instr.push(format!("ctx->numThread++;")); + //thread_call_instr.push(format!("printf(\"%i \\n\", ctx->numThread);")); thread_call_instr } @@ -574,6 +575,7 @@ impl WriteC for CallBucket { call_instructions.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); call_instructions.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); call_instructions.push(format!("ctx->numThread++;")); + //call_instructions.push(format!("printf(\"%i \\n\", ctx->numThread);")); if let StatusInput::Unknown = status { let sub_cmp_counter_decrease_andcheck = format!("!({})",sub_cmp_counter_decrease); let if_condition = vec![sub_cmp_counter_decrease_andcheck]; diff --git a/compiler/src/intermediate_representation/load_bucket.rs b/compiler/src/intermediate_representation/load_bucket.rs index c731b5afe..18001e331 100644 --- a/compiler/src/intermediate_representation/load_bucket.rs +++ b/compiler/src/intermediate_representation/load_bucket.rs @@ -235,6 +235,7 @@ impl WriteC for LoadBucket { prologue.push(format!("for (int i = 0; i < {}; i++) {{",self.context.size)); prologue.push(format!("ctx->numThreadMutex.lock();")); prologue.push(format!("ctx->numThread--;")); + //prologue.push(format!("printf(\"%i \\n\", ctx->numThread);")); prologue.push(format!("ctx->numThreadMutex.unlock();")); prologue.push(format!("ctx->ntcvs.notify_one();")); prologue.push(format!( @@ -249,6 +250,7 @@ impl WriteC for LoadBucket { prologue.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); prologue.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); prologue.push(format!("ctx->numThread++;")); + //prologue.push(format!("printf(\"%i \\n\", ctx->numThread);")); prologue.push(format!("}}")); prologue.push(format!("}}")); } @@ -269,6 +271,7 @@ impl WriteC for LoadBucket { prologue.push(format!("for (int i = 0; i < {}; i++) {{",self.context.size)); prologue.push(format!("ctx->numThreadMutex.lock();")); prologue.push(format!("ctx->numThread--;")); + //prologue.push(format!("printf(\"%i \\n\", ctx->numThread);")); prologue.push(format!("ctx->numThreadMutex.unlock();")); prologue.push(format!("ctx->ntcvs.notify_one();")); prologue.push(format!( @@ -283,6 +286,7 @@ impl WriteC for LoadBucket { prologue.push(format!("std::unique_lock lkt({}->numThreadMutex);",CIRCOM_CALC_WIT)); prologue.push(format!("{}->ntcvs.wait(lkt, [{}]() {{return {}->numThread < {}->maxThread; }});",CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT,CIRCOM_CALC_WIT)); prologue.push(format!("ctx->numThread++;")); + //prologue.push(format!("printf(\"%i \\n\", ctx->numThread);")); prologue.push(format!("}}")); prologue.push(format!("}}")); From 774332ce5dcd78617b147a961f20790272cdff15 Mon Sep 17 00:00:00 2001 From: miguelis Date: Thu, 23 Feb 2023 11:56:09 +0100 Subject: [PATCH 22/70] Update lang.lalrpop --- parser/src/lang.lalrpop | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 4502b433b..cae354448 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -401,7 +401,7 @@ ParseStatement2 : Statement = { "assert" "(" ")" Semicolon => build_assert(Meta::new(s,e),arg), - ";" + Semicolon => build_anonymous_component_statement(Meta::new(s,e), lhe), ParseBlock @@ -741,4 +741,4 @@ Version : Version = { "." "." => { (version, subversion, subsubversion) } -}; \ No newline at end of file +}; From 50dcbdf68e9b5caebeccf978aaf96b90df0f1797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Isabel=20M=C3=A1rquez?= Date: Thu, 23 Feb 2023 12:31:43 +0100 Subject: [PATCH 23/70] Adding more info to semicolon error --- parser/src/errors.rs | 15 ++++++++++++--- parser/src/lang.lalrpop | 6 +++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/parser/src/errors.rs b/parser/src/errors.rs index c0d74739e..bbfbb09cd 100644 --- a/parser/src/errors.rs +++ b/parser/src/errors.rs @@ -24,7 +24,10 @@ pub enum Error { required_version: Version, version: Version, }, - MissingSemicolon, + MissingSemicolon{ + location: FileLocation, + file_id: FileID, + }, UnrecognizedInclude { location: FileLocation, file_id: FileID, @@ -85,8 +88,14 @@ impl Error { ReportCode::CompilerVersionError, ) } - Error::MissingSemicolon => { - Report::error("missing semicolon".to_owned(), ReportCode::ParseFail) + Error::MissingSemicolon { + location, + file_id, + } => { + let mut report = Report::error(format!("Missing semicolon"), + ReportCode::ParseFail); + report.add_primary(location, file_id, "A semicolon is needed here".to_string()); + report } Error::UnrecognizedInclude{location, file_id} => { let mut report = diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 4502b433b..1dd84939c 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -27,7 +27,7 @@ IdentifierListDef : Vec = { Semicolon:() = { ";" => (), - ! => errors.push(Error::MissingSemicolon) + ! => errors.push(Error::MissingSemicolon{ location: s..e, file_id }) } // ==================================================================== @@ -98,7 +98,7 @@ ParsePublicList : Vec = { }; pub ParseMainComponent : MainComponent = { - "component" "main" "=" Semicolon + "component" "main" "=" Semicolon => match public_list { None => build_main_component(Vec::new(),init), Some(list) => build_main_component(list,init) @@ -401,7 +401,7 @@ ParseStatement2 : Statement = { "assert" "(" ")" Semicolon => build_assert(Meta::new(s,e),arg), - ";" + Semicolon => build_anonymous_component_statement(Meta::new(s,e), lhe), ParseBlock From fb3060614ecb082dfc0d48f5107b564508416f1c Mon Sep 17 00:00:00 2001 From: clararod9 Date: Thu, 23 Feb 2023 18:14:29 +0100 Subject: [PATCH 24/70] setting maxThreads to 32 --- code_producers/src/c_elements/common/calcwit.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_producers/src/c_elements/common/calcwit.hpp b/code_producers/src/c_elements/common/calcwit.hpp index 0b7c05575..363de21d1 100644 --- a/code_producers/src/c_elements/common/calcwit.hpp +++ b/code_producers/src/c_elements/common/calcwit.hpp @@ -10,7 +10,7 @@ #include "circom.hpp" #include "fr.hpp" -#define NMUTEXES 12 //512 +#define NMUTEXES 32 //512 u64 fnv1a(std::string s); From ffab26c7666c464d1bf2878275e5650f0ac552f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Isabel=20M=C3=A1rquez?= Date: Fri, 24 Feb 2023 02:12:29 +0100 Subject: [PATCH 25/70] Parser errors --- parser/src/errors.rs | 123 ----------------- parser/src/include_logic.rs | 8 +- parser/src/lang.lalrpop | 24 ++-- parser/src/lib.rs | 22 ++- parser/src/parser_logic.rs | 45 +++--- .../src/abstract_syntax_tree/ast.rs | 128 ++++++++++++++++-- .../src/program_library/error_code.rs | 28 ++++ 7 files changed, 189 insertions(+), 189 deletions(-) diff --git a/parser/src/errors.rs b/parser/src/errors.rs index bbfbb09cd..fe3ed693f 100644 --- a/parser/src/errors.rs +++ b/parser/src/errors.rs @@ -4,129 +4,6 @@ use program_structure::error_definition::Report; use program_structure::file_definition::{FileID, FileLocation}; use program_structure::abstract_syntax_tree::ast::Version; -pub enum Error { - UnclosedComment { - location: FileLocation, - file_id: FileID, - }, - GenericParsing { - location: FileLocation, - file_id: FileID, - msg: String, - }, - FileOs { - path: String, - }, - NoMain, - MultipleMain, - CompilerVersion { - path: String, - required_version: Version, - version: Version, - }, - MissingSemicolon{ - location: FileLocation, - file_id: FileID, - }, - UnrecognizedInclude { - location: FileLocation, - file_id: FileID, - }, - UnrecognizedVersion { - location: FileLocation, - file_id: FileID, - }, - UnrecognizedPragma { - location: FileLocation, - file_id: FileID, - }, - IllegalExpression { - location: FileLocation, - file_id: FileID, - }, -} - -impl Error { - pub fn produce_report(self) -> Report { - match self { - Error::UnclosedComment { location, file_id } => { - let mut report = - Report::error("unterminated /* */".to_string(), ReportCode::ParseFail); - report.add_primary(location, file_id, "Comment starts here".to_string()); - report - } - Error::GenericParsing { - location, - file_id, - msg, - } => { - let mut report = Report::error(msg, ReportCode::ParseFail); - report.add_primary(location, file_id, "Invalid syntax".to_string()); - report - } - Error::FileOs { path } => Report::error( - format!("Could not open file {}", path), - ReportCode::ParseFail, - ), - Error::NoMain => Report::error( - "No main specified in the project structure".to_string(), - ReportCode::NoMainFoundInProject, - ), - Error::MultipleMain =>{ - Report::error( - "Multiple main components in the project structure".to_string(), - ReportCode::MultipleMainInComponent, - ) - } - Error::CompilerVersion { - path, - required_version, - version, - } => { - Report::error( - format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", path, required_version, version ), - ReportCode::CompilerVersionError, - ) - } - Error::MissingSemicolon { - location, - file_id, - } => { - let mut report = Report::error(format!("Missing semicolon"), - ReportCode::ParseFail); - report.add_primary(location, file_id, "A semicolon is needed here".to_string()); - report - } - Error::UnrecognizedInclude{location, file_id} => { - let mut report = - Report::error("unrecognized argument in include directive".to_string(), ReportCode::ParseFail); - report.add_primary(location, file_id, "this argument".to_string()); - report - - } - Error::UnrecognizedPragma{location, file_id} => { - let mut report = - Report::error("unrecognized argument in pragma directive".to_string(), ReportCode::ParseFail); - report.add_primary(location, file_id, "this argument".to_string()); - report - - } - Error::UnrecognizedVersion{location, file_id} => { - let mut report = - Report::error("unrecognized version argument in pragma directive".to_string(), ReportCode::ParseFail); - report.add_primary(location, file_id, "this argument".to_string()); - report - } - Error::IllegalExpression{location, file_id} => { - let mut report = - Report::error("illegal expression".to_string(), ReportCode::ParseFail); - report.add_primary(location, file_id, "here".to_string()); - report - } - } - } -} - pub struct NoCompilerVersionWarning { pub path: String, pub version: Version, diff --git a/parser/src/include_logic.rs b/parser/src/include_logic.rs index f4e5a8ea1..88d95a034 100644 --- a/parser/src/include_logic.rs +++ b/parser/src/include_logic.rs @@ -1,8 +1,8 @@ +use program_structure::ast::produce_report_with_message; use program_structure::error_code::ReportCode; use program_structure::error_definition::Report; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; -use crate::errors::Error; pub struct FileStack { current_location: PathBuf, @@ -42,8 +42,7 @@ impl FileStack { } } } - let error = "Include not found: ".to_string() + &name; - Result::Err(Report::error(error, ReportCode::ParseFail)) + Result::Err( produce_report_with_message(ReportCode::IncludeNotFound, name)) } pub fn take_next(f_stack: &mut FileStack) -> Option { @@ -92,8 +91,7 @@ impl IncludesGraph { let mut crr = PathBuf::new(); crr.push(old_path.clone()); let path = std::fs::canonicalize(crr) - .map_err(|_| Error::FileOs { path: old_path }) - .map_err(|e| Error::produce_report(e))?; + .map_err(|_e| produce_report_with_message(ReportCode::FileOs,old_path))?; let edges = self.adjacency.entry(path).or_insert(vec![]); edges.push(self.nodes.len() - 1); Ok(()) diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 49d21c0f3..29fa9992f 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -4,10 +4,11 @@ use program_structure::expression_builders::*; use program_structure::ast::*; use program_structure::ast_shortcuts::{self,Symbol,TupleInit}; use std::str::FromStr; -use crate::errors::Error; use lalrpop_util::ParseError; +use program_structure::error_definition::Report; +use program_structure::error_code::ReportCode; -grammar<'err>(file_id: usize, errors:&'err mut Vec); +grammar<'err>(file_id: usize, errors:&'err mut Vec); CommaSepList:Vec = { ",")*> => { @@ -27,7 +28,7 @@ IdentifierListDef : Vec = { Semicolon:() = { ";" => (), - ! => errors.push(Error::MissingSemicolon{ location: s..e, file_id }) + ! => errors.push(produce_report(ReportCode::MissingSemicolon, s..e, file_id)) } // ==================================================================== @@ -42,20 +43,20 @@ Semicolon:() = { ParsePragma:Pragma = { // version - "pragma" "circom" Semicolon => Pragma::Version(<>), + "pragma" "circom" Semicolon => Pragma::Version(Meta::new(s,e), file_id, v), "pragma" "circom" Semicolon => match <>.error { ParseError::UnrecognizedToken { ref token, .. } => { - errors.push(Error::UnrecognizedVersion{location: token.0..token.2, file_id}); + errors.push(produce_report(ReportCode::UnrecognizedVersion,token.0..token.2, file_id)); Pragma::Unrecognized } _ => unreachable!(), }, // custom templates - "pragma" "custom_templates" Semicolon => Pragma::CustomGates, + "pragma" "custom_templates" Semicolon => Pragma::CustomGates(Meta::new(s,e), file_id), // unrecognized "pragma" Semicolon => match <>.error { ParseError::UnrecognizedToken { ref token, .. } => { - errors.push(Error::UnrecognizedPragma{location: token.0..token.2, file_id}); + errors.push(produce_report(ReportCode::UnrecognizedPragma,token.0..token.2, file_id)); Pragma::Unrecognized } _ => unreachable!(), @@ -69,7 +70,7 @@ ParseInclude:String = { "include" Semicolon => { match <>.error { ParseError::UnrecognizedToken { ref token, .. } => { - errors.push(Error::UnrecognizedInclude{location: token.0..token.2, file_id}); + errors.push(produce_report(ReportCode::UnrecognizedInclude, token.0..token.2, file_id)); } _ => unreachable!(), } @@ -84,7 +85,10 @@ ParseInclude:String = { // Parsing the declaration of the main component pub ParseAst:AST = { - => AST::new(Meta::new(s,e), pragmas, includes, definitions, main), + => { let (ast, mut ers) = AST::new(Meta::new(s,e), pragmas, includes, definitions, main); + errors.append(&mut ers); + ast + }, }; // ==================================================================== @@ -638,7 +642,7 @@ Expression0: Expression = { => match <>.error { ParseError::UnrecognizedToken { ref token, .. } => { - errors.push(Error::IllegalExpression{location: token.0..token.2, file_id}); + errors.push(produce_report(ReportCode::IllegalExpression, token.0..token.2, file_id)); // doesn't matter build_number(Meta::new(0,0),BigInt::from(0)) } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index d000866c3..34a31e451 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -13,15 +13,18 @@ mod parser_logic; mod syntax_sugar_remover; use include_logic::{FileStack, IncludesGraph}; +use program_structure::ast::produce_compiler_version_report; +use program_structure::ast::produce_report; +use program_structure::ast::produce_report_with_message; use program_structure::error_code::ReportCode; -use program_structure::error_definition::{Report, ReportCollection}; +use program_structure::error_definition::ReportCollection; +use program_structure::error_definition::Report; use program_structure::file_definition::{FileLibrary}; use program_structure::program_archive::ProgramArchive; use std::path::{PathBuf, Path}; use syntax_sugar_remover::{apply_syntactic_sugar}; use std::str::FromStr; -use errors::Error; pub type Version = (usize, usize, usize); @@ -110,10 +113,10 @@ pub fn run_parser( } if main_components.len() == 0 { - let report = Error::NoMain.produce_report(); + let report = produce_report(ReportCode::NoMain,0..0, 0); Err((file_library, vec![report])) } else if main_components.len() > 1 { - let report = Error::MultipleMain.produce_report(); + let report = produce_report(ReportCode::MultipleMain,0..0, 0); Err((file_library, vec![report])) } else { let errors: ReportCollection = includes_graph.get_problematic_paths().iter().map(|path| @@ -159,8 +162,7 @@ fn open_file(path: PathBuf) -> Result<(String, String), Report> /* path, src */ let path_str = format!("{:?}", path); read_to_string(path) .map(|contents| (path_str.clone(), contents)) - .map_err(|_| Error::FileOs { path: path_str.clone() }) - .map_err(|e| e.produce_report()) + .map_err(|_| produce_report_with_message(ReportCode::FileOs, path_str.clone())) } fn parse_number_version(version: &str) -> Version { @@ -185,13 +187,7 @@ fn check_number_version( { Ok(vec![]) } else { - let report = Error::CompilerVersion { - path: file_path, - required_version, - version: version_compiler, - } - .produce_report(); - Err(report) + Err(produce_compiler_version_report(file_path, required_version, version_compiler)) } } else { let report = diff --git a/parser/src/parser_logic.rs b/parser/src/parser_logic.rs index 435b91961..80f64e6cd 100644 --- a/parser/src/parser_logic.rs +++ b/parser/src/parser_logic.rs @@ -1,6 +1,6 @@ -use super::errors::Error; use super::lang; -use program_structure::ast::AST; +use program_structure::ast::{AST, produce_report, produce_generic_report}; +use program_structure::error_code::ReportCode; use program_structure::error_definition::{ReportCollection}; use program_structure::file_definition::FileID; @@ -75,7 +75,7 @@ pub fn preprocess(expr: &str, file_id: FileID) -> Result Result { .parse(file_id, &mut errors, &preprocess) // TODO: is this always fatal? .map_err(|parse_error| match parse_error { - InvalidToken { location } => Error::GenericParsing { - file_id, - msg: format!("{:?}", parse_error), - location: location..location, - }, - UnrecognizedToken { ref token, .. } => Error::GenericParsing { - file_id, - msg: format!("{:?}", parse_error), - location: token.0..token.2, - }, - ExtraToken { ref token } => Error::GenericParsing { - file_id, - msg: format!("{:?}", parse_error), - location: token.0..token.2, - }, - _ => { - Error::GenericParsing { file_id, msg: format!("{:?}", parse_error), location: 0..0 } - } + InvalidToken { location } => + produce_generic_report( + format!("{:?}", parse_error), + location..location, file_id + ), + UnrecognizedToken { ref token, .. } => + produce_generic_report( + format!("{:?}", parse_error), + token.0..token.2, file_id + ), + ExtraToken { ref token } => produce_generic_report( + format!("{:?}", parse_error), + token.0..token.2, file_id + ), + _ => produce_generic_report( + format!("{:?}", parse_error), + 0..0, file_id + ) }) - .map_err(Error::produce_report) .map_err(|e| vec![e])?; if !errors.is_empty() { - return Err(errors.into_iter().map(|e| e.produce_report()).collect()); + return Err(errors.into_iter().collect()); } Ok(ast) diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index 8718812da..769970422 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -1,11 +1,11 @@ -use crate::file_definition::FileLocation; +use crate::{file_definition::{FileLocation, FileID}, error_definition::Report, error_code::ReportCode}; use num_bigint::BigInt; use serde_derive::{Deserialize, Serialize}; -#[derive(Clone, Debug)] +#[derive(Clone)] pub enum Pragma { - Version(Version), - CustomGates, + Version(Meta, FileID, Version), + CustomGates(Meta ,FileID), Unrecognized, } @@ -99,24 +99,25 @@ impl AST { includes: Vec, definitions: Vec, main_component: Option, - ) -> AST { + ) -> (AST,Vec) { let mut custom_gates = None; let mut compiler_version = None; - + let mut reports = Vec::new(); for p in pragmas { match p { // TODO: don't panic - Pragma::Version(ver) => match compiler_version { - Some(_) => panic!("multiple circom pragmas"), + Pragma::Version(location, file_id, ver) => match compiler_version { + Some(_) => reports.push(produce_report( + ReportCode::MultiplePragma,location.start..location.end, file_id)), None => compiler_version = Some(ver), }, - Pragma::CustomGates => match custom_gates { - Some(_) => panic!("multiple custom gates pragmas"), + Pragma::CustomGates(location, file_id ) => match custom_gates { + Some(_) => reports.push(produce_report( + ReportCode::MultiplePragma, location.start..location.end, file_id)), None => custom_gates = Some(true), }, - // unrecognized - // TODO: maybe warn? - _ => (), + Pragma::Unrecognized => {}, //This error is previously handled, and the + //parsing continues to catch more parsing errors. } } @@ -124,7 +125,7 @@ impl AST { matches!(definition, Definition::Template { is_custom_gate: true, .. }) }); - AST { + (AST { meta, compiler_version, custom_gates: custom_gates.unwrap_or(false), @@ -132,7 +133,7 @@ impl AST { includes, definitions, main_component, - } + }, reports) } } @@ -460,3 +461,100 @@ impl MemoryKnowledge { } } } + + + +pub fn produce_report_with_message(error_code : ReportCode, msg : String) -> Report { + match error_code { + ReportCode::FileOs => { + Report::error( + format!("Could not open file {}", msg), + ReportCode::FileOs, + ) + } + ReportCode::IncludeNotFound => { + Report::error( + format!(" The file {} to be included has not been found", msg), + ReportCode::FileOs, + ) + }, + _ => unreachable!() + } +} + +pub fn produce_generic_report( msg : String, location : FileLocation, file_id : FileID) -> Report { + let mut report = Report::error(msg, ReportCode::GenericParsing); + report.add_primary(location, file_id, "Error here".to_string()); + report +} +pub fn produce_compiler_version_report(path : String, required_version : Version, version : Version) -> Report { + let report = Report::error( + format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", path, required_version, version ), + ReportCode::CompilerVersionError, + ); + report +} + + + pub fn produce_report(error_code: ReportCode, location : FileLocation, file_id : FileID) -> Report { + use ReportCode::*; + let report = match error_code { + UnclosedComment => { + let mut report = + Report::error("unterminated /* */".to_string(), ReportCode::UnclosedComment); + report.add_primary(location, file_id, "Comment starts here".to_string()); + report + } + NoMain => Report::error( + "No main specified in the project structure".to_string(), + ReportCode::NoMainFoundInProject, + ), + MultipleMain =>{ + Report::error( + "Multiple main components in the project structure".to_string(), + ReportCode::MultipleMainInComponent, + ) + } + MissingSemicolon => { + let mut report = Report::error(format!("Missing semicolon"), + ReportCode::MissingSemicolon); + report.add_primary(location, file_id, "A semicolon is needed here".to_string()); + report + } + UnrecognizedInclude => { + let mut report = + Report::error("unrecognized argument in include directive".to_string(), ReportCode::UnrecognizedInclude); + report.add_primary(location, file_id, "this argument".to_string()); + report + + } + UnrecognizedPragma => { + let mut report = + Report::error("unrecognized argument in pragma directive".to_string(), ReportCode::UnrecognizedPragma); + report.add_primary(location, file_id, "this argument".to_string()); + report + + } + UnrecognizedVersion => { + let mut report = + Report::error("unrecognized version argument in pragma directive".to_string(), ReportCode::UnrecognizedVersion); + report.add_primary(location, file_id, "this argument".to_string()); + report + } + IllegalExpression => { + let mut report = + Report::error("illegal expression".to_string(), ReportCode::IllegalExpression); + report.add_primary(location, file_id, "here".to_string()); + report + } + MultiplePragma => { + let mut report = + Report::error("Multiple pragma directives".to_string(), ReportCode::MultiplePragma); + report.add_primary(location, file_id, "here".to_string()); + report + }, + _ => unreachable!(), + }; + report +} + diff --git a/program_structure/src/program_library/error_code.rs b/program_structure/src/program_library/error_code.rs index 42a50f045..61cd7f5e0 100644 --- a/program_structure/src/program_library/error_code.rs +++ b/program_structure/src/program_library/error_code.rs @@ -3,6 +3,21 @@ use std::fmt::Formatter; #[derive(Copy, Clone)] pub enum ReportCode { + //Parse Errors + UnclosedComment, + GenericParsing, + FileOs, + NoMain, + MultipleMain, + CompilerVersion, + MissingSemicolon, + UnrecognizedInclude, + UnrecognizedVersion, + UnrecognizedPragma, + IncludeNotFound, + IllegalExpression, + MultiplePragma, + // AssertWrongType, ParseFail, CompilerVersionError, @@ -174,6 +189,19 @@ impl fmt::Display for ReportCode { CustomGatesVersionError => "CG05", AnonymousCompError => "TAC01", TupleError => "TAC02", + UnclosedComment => "P01", + GenericParsing => "P02", + FileOs => "P03", + NoMain => "P04", + MultipleMain => "P05", + CompilerVersion => "P06", + MissingSemicolon => "P07", + UnrecognizedInclude => "P08", + UnrecognizedVersion => "P09", + UnrecognizedPragma => "P10", + IllegalExpression => "P11", + MultiplePragma => "P12", + IncludeNotFound => "P13", }; f.write_str(string_format) } From af0a9e5de6637273a9ab07b236e594a6f5b291fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Isabel=20M=C3=A1rquez?= Date: Fri, 24 Feb 2023 02:21:47 +0100 Subject: [PATCH 26/70] Parser errors --- parser/src/include_logic.rs | 2 +- parser/src/lang.lalrpop | 1 + parser/src/lib.rs | 6 +- parser/src/parser_logic.rs | 3 +- .../src/abstract_syntax_tree/ast.rs | 101 +----------------- .../src/program_library/error_code.rs | 100 +++++++++++++++++ 6 files changed, 109 insertions(+), 104 deletions(-) diff --git a/parser/src/include_logic.rs b/parser/src/include_logic.rs index 88d95a034..38473c71d 100644 --- a/parser/src/include_logic.rs +++ b/parser/src/include_logic.rs @@ -1,4 +1,4 @@ -use program_structure::ast::produce_report_with_message; +use program_structure::error_code::produce_report_with_message; use program_structure::error_code::ReportCode; use program_structure::error_definition::Report; use std::collections::{HashMap, HashSet}; diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 29fa9992f..91e238c14 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -5,6 +5,7 @@ use program_structure::ast::*; use program_structure::ast_shortcuts::{self,Symbol,TupleInit}; use std::str::FromStr; use lalrpop_util::ParseError; +use program_structure::error_code::produce_report; use program_structure::error_definition::Report; use program_structure::error_code::ReportCode; diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 34a31e451..68dc3dd66 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -13,9 +13,9 @@ mod parser_logic; mod syntax_sugar_remover; use include_logic::{FileStack, IncludesGraph}; -use program_structure::ast::produce_compiler_version_report; -use program_structure::ast::produce_report; -use program_structure::ast::produce_report_with_message; +use program_structure::error_code::produce_compiler_version_report; +use program_structure::error_code::produce_report; +use program_structure::error_code::produce_report_with_message; use program_structure::error_code::ReportCode; use program_structure::error_definition::ReportCollection; use program_structure::error_definition::Report; diff --git a/parser/src/parser_logic.rs b/parser/src/parser_logic.rs index 80f64e6cd..6fa6ce3a2 100644 --- a/parser/src/parser_logic.rs +++ b/parser/src/parser_logic.rs @@ -1,5 +1,6 @@ use super::lang; -use program_structure::ast::{AST, produce_report, produce_generic_report}; +use program_structure::ast::{AST}; +use program_structure::error_code::{produce_report, produce_generic_report}; use program_structure::error_code::ReportCode; use program_structure::error_definition::{ReportCollection}; use program_structure::file_definition::FileID; diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index 769970422..fd7c884ec 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -1,4 +1,4 @@ -use crate::{file_definition::{FileLocation, FileID}, error_definition::Report, error_code::ReportCode}; +use crate::{file_definition::{FileLocation, FileID}, error_definition::Report, error_code::{ReportCode, produce_report}}; use num_bigint::BigInt; use serde_derive::{Deserialize, Serialize}; @@ -460,101 +460,4 @@ impl MemoryKnowledge { panic!("abstract memory address was look at without being initialized"); } } -} - - - -pub fn produce_report_with_message(error_code : ReportCode, msg : String) -> Report { - match error_code { - ReportCode::FileOs => { - Report::error( - format!("Could not open file {}", msg), - ReportCode::FileOs, - ) - } - ReportCode::IncludeNotFound => { - Report::error( - format!(" The file {} to be included has not been found", msg), - ReportCode::FileOs, - ) - }, - _ => unreachable!() - } -} - -pub fn produce_generic_report( msg : String, location : FileLocation, file_id : FileID) -> Report { - let mut report = Report::error(msg, ReportCode::GenericParsing); - report.add_primary(location, file_id, "Error here".to_string()); - report -} -pub fn produce_compiler_version_report(path : String, required_version : Version, version : Version) -> Report { - let report = Report::error( - format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", path, required_version, version ), - ReportCode::CompilerVersionError, - ); - report -} - - - pub fn produce_report(error_code: ReportCode, location : FileLocation, file_id : FileID) -> Report { - use ReportCode::*; - let report = match error_code { - UnclosedComment => { - let mut report = - Report::error("unterminated /* */".to_string(), ReportCode::UnclosedComment); - report.add_primary(location, file_id, "Comment starts here".to_string()); - report - } - NoMain => Report::error( - "No main specified in the project structure".to_string(), - ReportCode::NoMainFoundInProject, - ), - MultipleMain =>{ - Report::error( - "Multiple main components in the project structure".to_string(), - ReportCode::MultipleMainInComponent, - ) - } - MissingSemicolon => { - let mut report = Report::error(format!("Missing semicolon"), - ReportCode::MissingSemicolon); - report.add_primary(location, file_id, "A semicolon is needed here".to_string()); - report - } - UnrecognizedInclude => { - let mut report = - Report::error("unrecognized argument in include directive".to_string(), ReportCode::UnrecognizedInclude); - report.add_primary(location, file_id, "this argument".to_string()); - report - - } - UnrecognizedPragma => { - let mut report = - Report::error("unrecognized argument in pragma directive".to_string(), ReportCode::UnrecognizedPragma); - report.add_primary(location, file_id, "this argument".to_string()); - report - - } - UnrecognizedVersion => { - let mut report = - Report::error("unrecognized version argument in pragma directive".to_string(), ReportCode::UnrecognizedVersion); - report.add_primary(location, file_id, "this argument".to_string()); - report - } - IllegalExpression => { - let mut report = - Report::error("illegal expression".to_string(), ReportCode::IllegalExpression); - report.add_primary(location, file_id, "here".to_string()); - report - } - MultiplePragma => { - let mut report = - Report::error("Multiple pragma directives".to_string(), ReportCode::MultiplePragma); - report.add_primary(location, file_id, "here".to_string()); - report - }, - _ => unreachable!(), - }; - report -} - +} \ No newline at end of file diff --git a/program_structure/src/program_library/error_code.rs b/program_structure/src/program_library/error_code.rs index 61cd7f5e0..1f7e13491 100644 --- a/program_structure/src/program_library/error_code.rs +++ b/program_structure/src/program_library/error_code.rs @@ -1,6 +1,8 @@ use core::fmt; use std::fmt::Formatter; +use crate::{error_definition::Report, file_definition::{FileLocation, FileID}, ast::Version}; + #[derive(Copy, Clone)] pub enum ReportCode { //Parse Errors @@ -206,3 +208,101 @@ impl fmt::Display for ReportCode { f.write_str(string_format) } } + + + + +pub fn produce_report_with_message(error_code : ReportCode, msg : String) -> Report { + match error_code { + ReportCode::FileOs => { + Report::error( + format!("Could not open file {}", msg), + ReportCode::FileOs, + ) + } + ReportCode::IncludeNotFound => { + Report::error( + format!(" The file {} to be included has not been found", msg), + ReportCode::FileOs, + ) + }, + _ => unreachable!() + } +} + +pub fn produce_generic_report( msg : String, location : FileLocation, file_id : FileID) -> Report { + let mut report = Report::error(msg, ReportCode::GenericParsing); + report.add_primary(location, file_id, "Error here".to_string()); + report +} +pub fn produce_compiler_version_report(path : String, required_version : Version, version : Version) -> Report { + let report = Report::error( + format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", path, required_version, version ), + ReportCode::CompilerVersionError, + ); + report +} + + + pub fn produce_report(error_code: ReportCode, location : FileLocation, file_id : FileID) -> Report { + use ReportCode::*; + let report = match error_code { + UnclosedComment => { + let mut report = + Report::error("unterminated /* */".to_string(), ReportCode::UnclosedComment); + report.add_primary(location, file_id, "Comment starts here".to_string()); + report + } + NoMain => Report::error( + "No main specified in the project structure".to_string(), + ReportCode::NoMainFoundInProject, + ), + MultipleMain =>{ + Report::error( + "Multiple main components in the project structure".to_string(), + ReportCode::MultipleMainInComponent, + ) + } + MissingSemicolon => { + let mut report = Report::error(format!("Missing semicolon"), + ReportCode::MissingSemicolon); + report.add_primary(location, file_id, "A semicolon is needed here".to_string()); + report + } + UnrecognizedInclude => { + let mut report = + Report::error("unrecognized argument in include directive".to_string(), ReportCode::UnrecognizedInclude); + report.add_primary(location, file_id, "this argument".to_string()); + report + + } + UnrecognizedPragma => { + let mut report = + Report::error("unrecognized argument in pragma directive".to_string(), ReportCode::UnrecognizedPragma); + report.add_primary(location, file_id, "this argument".to_string()); + report + + } + UnrecognizedVersion => { + let mut report = + Report::error("unrecognized version argument in pragma directive".to_string(), ReportCode::UnrecognizedVersion); + report.add_primary(location, file_id, "this argument".to_string()); + report + } + IllegalExpression => { + let mut report = + Report::error("illegal expression".to_string(), ReportCode::IllegalExpression); + report.add_primary(location, file_id, "here".to_string()); + report + } + MultiplePragma => { + let mut report = + Report::error("Multiple pragma directives".to_string(), ReportCode::MultiplePragma); + report.add_primary(location, file_id, "here".to_string()); + report + }, + _ => unreachable!(), + }; + report +} + From fdc18196b799033ed5b082bb7f010c180d1f0ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Isabel=20M=C3=A1rquez?= Date: Fri, 24 Feb 2023 11:53:06 +0100 Subject: [PATCH 27/70] Handling parser errors --- parser/src/errors.rs | 86 ---------- parser/src/include_logic.rs | 2 +- parser/src/lang.lalrpop | 2 +- parser/src/lib.rs | 10 +- parser/src/parser_logic.rs | 2 +- parser/src/syntax_sugar_remover.rs | 90 ++++++----- .../src/abstract_syntax_tree/ast.rs | 149 +++++++++++++++++- .../src/program_library/error_code.rs | 99 +----------- 8 files changed, 198 insertions(+), 242 deletions(-) delete mode 100644 parser/src/errors.rs diff --git a/parser/src/errors.rs b/parser/src/errors.rs deleted file mode 100644 index fe3ed693f..000000000 --- a/parser/src/errors.rs +++ /dev/null @@ -1,86 +0,0 @@ -use program_structure::ast::Meta; -use program_structure::error_code::ReportCode; -use program_structure::error_definition::Report; -use program_structure::file_definition::{FileID, FileLocation}; -use program_structure::abstract_syntax_tree::ast::Version; - -pub struct NoCompilerVersionWarning { - pub path: String, - pub version: Version, -} - -impl NoCompilerVersionWarning { - pub fn produce_report(error: Self) -> Report { - Report::warning( - format!( - "File {} does not include pragma version. Assuming pragma version {:?}", - error.path, error.version - ), - ReportCode::NoCompilerVersionWarning, - ) - } -} - -pub struct AnonymousCompError{ - pub location: FileLocation, - pub msg : String -} - -impl AnonymousCompError { - pub fn produce_report( error : Self) -> Report { - Report::error( - format!("{}", error.msg), - ReportCode::AnonymousCompError, - ) - } - - pub fn anonymous_inside_condition_error(meta : Meta) -> Report { - let error = AnonymousCompError {msg: "An anonymous component cannot be used inside a condition ".to_string(), location : meta.location.clone()}; - let mut report = AnonymousCompError::produce_report(error); - let file_id = meta.get_file_id().clone(); - report.add_primary( - meta.location, - file_id, - "This is an anonymous component used inside a condition".to_string(), - ); - report - } - - pub fn anonymous_general_error(meta : Meta, msg : String) -> Report { - let error = AnonymousCompError {msg, location : meta.location.clone()}; - let mut report = AnonymousCompError::produce_report(error); - let file_id = meta.get_file_id().clone(); - report.add_primary( - meta.location, - file_id, - "This is the anonymous component whose use is not allowed".to_string(), - ); - report - } -} - -pub struct TupleError{ - pub location: FileLocation, - pub msg : String -} - -impl TupleError { - pub fn produce_report( error : Self) -> Report { - Report::error( - format!("{}", error.msg), - ReportCode::TupleError, - ) - } - - pub fn tuple_general_error(meta : Meta, msg : String) -> Report { - let error = TupleError {msg, location : meta.location.clone()}; - let mut report = TupleError::produce_report(error); - let file_id = meta.get_file_id().clone(); - report.add_primary( - meta.location, - file_id, - "This is the tuple whose use is not allowed".to_string(), - ); - report - } -} \ No newline at end of file diff --git a/parser/src/include_logic.rs b/parser/src/include_logic.rs index 38473c71d..88d95a034 100644 --- a/parser/src/include_logic.rs +++ b/parser/src/include_logic.rs @@ -1,4 +1,4 @@ -use program_structure::error_code::produce_report_with_message; +use program_structure::ast::produce_report_with_message; use program_structure::error_code::ReportCode; use program_structure::error_definition::Report; use std::collections::{HashMap, HashSet}; diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 91e238c14..7aabb379d 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -5,7 +5,7 @@ use program_structure::ast::*; use program_structure::ast_shortcuts::{self,Symbol,TupleInit}; use std::str::FromStr; use lalrpop_util::ParseError; -use program_structure::error_code::produce_report; +use program_structure::ast::produce_report; use program_structure::error_definition::Report; use program_structure::error_code::ReportCode; diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 68dc3dd66..73bc18484 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -7,15 +7,12 @@ extern crate lalrpop_util; lalrpop_mod!(pub lang); -mod errors; mod include_logic; mod parser_logic; mod syntax_sugar_remover; use include_logic::{FileStack, IncludesGraph}; -use program_structure::error_code::produce_compiler_version_report; -use program_structure::error_code::produce_report; -use program_structure::error_code::produce_report_with_message; +use program_structure::ast::{produce_compiler_version_report, produce_report, produce_report_with_message, produce_version_warning_report}; use program_structure::error_code::ReportCode; use program_structure::error_definition::ReportCollection; use program_structure::error_definition::Report; @@ -191,10 +188,7 @@ fn check_number_version( } } else { let report = - errors::NoCompilerVersionWarning::produce_report(errors::NoCompilerVersionWarning { - path: file_path, - version: version_compiler, - }); + produce_version_warning_report(file_path, version_compiler); Ok(vec![report]) } } diff --git a/parser/src/parser_logic.rs b/parser/src/parser_logic.rs index 6fa6ce3a2..862c5a9ac 100644 --- a/parser/src/parser_logic.rs +++ b/parser/src/parser_logic.rs @@ -1,6 +1,6 @@ use super::lang; use program_structure::ast::{AST}; -use program_structure::error_code::{produce_report, produce_generic_report}; +use program_structure::ast::{produce_report, produce_generic_report}; use program_structure::error_code::ReportCode; use program_structure::error_definition::{ReportCollection}; use program_structure::file_definition::FileID; diff --git a/parser/src/syntax_sugar_remover.rs b/parser/src/syntax_sugar_remover.rs index 21085bff2..079ad4a75 100644 --- a/parser/src/syntax_sugar_remover.rs +++ b/parser/src/syntax_sugar_remover.rs @@ -11,12 +11,10 @@ use num_bigint::BigInt; -use crate::errors::{AnonymousCompError,TupleError}; - pub fn apply_syntactic_sugar(program_archive : &mut ProgramArchive) -> Result<(), Report> { let mut new_templates : HashMap = HashMap::new(); if program_archive.get_main_expression().is_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(program_archive.get_main_expression().get_meta().clone(),"The main component cannot contain an anonymous call ".to_string())); + return Result::Err(anonymous_general_error(program_archive.get_main_expression().get_meta().clone(),"The main component cannot contain an anonymous call ".to_string())); } for temp in program_archive.templates.clone() { @@ -52,7 +50,7 @@ fn check_anonymous_components_statement( match stm { Statement::MultSubstitution {meta, lhe, rhe, ..} => { if lhe.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_general_error( + Result::Err(anonymous_general_error( meta.clone(), "An anonymous component cannot be used in the left side of an assignment".to_string()) ) @@ -63,7 +61,7 @@ fn check_anonymous_components_statement( Statement::IfThenElse { meta, cond, if_case, else_case, .. } => { if cond.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_inside_condition_error(meta.clone())) + Result::Err(anonymous_inside_condition_error(meta.clone())) } else{ check_anonymous_components_statement(if_case)?; if else_case.is_some(){ @@ -74,7 +72,7 @@ fn check_anonymous_components_statement( } Statement::While { meta, cond, stmt, .. } => { if cond.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_inside_condition_error(meta.clone())) + Result::Err(anonymous_inside_condition_error(meta.clone())) } else{ check_anonymous_components_statement(stmt) } @@ -83,7 +81,7 @@ fn check_anonymous_components_statement( for arg in args { if let program_structure::ast::LogArgument::LogExp( exp ) = arg { if exp.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone() ,"An anonymous component cannot be used inside a log".to_string())) + return Result::Err(anonymous_general_error(meta.clone() ,"An anonymous component cannot be used inside a log".to_string())) } } } @@ -91,21 +89,21 @@ fn check_anonymous_components_statement( } Statement::Assert { meta, arg} => { if arg.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(), "An anonymous component cannot be used inside an assert".to_string())) + Result::Err(anonymous_general_error(meta.clone(), "An anonymous component cannot be used inside an assert".to_string())) } else{ Result::Ok(()) } } Statement::Return { meta, value: arg}=> { if arg.contains_anonymous_comp(){ - Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(), "An anonymous component cannot be used inside a function ".to_string())) + Result::Err(anonymous_general_error(meta.clone(), "An anonymous component cannot be used inside a function ".to_string())) } else{ Result::Ok(()) } } Statement::ConstraintEquality {meta, lhe, rhe } => { if lhe.contains_anonymous_comp() || rhe.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(), "An anonymous component cannot be used with operator === ".to_string())) + Result::Err(anonymous_general_error(meta.clone(), "An anonymous component cannot be used with operator === ".to_string())) } else{ Result::Ok(()) @@ -114,7 +112,7 @@ fn check_anonymous_components_statement( Statement::Declaration { meta, dimensions, .. } => { for exp in dimensions{ if exp.contains_anonymous_comp(){ - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + return Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); } } Result::Ok(()) @@ -140,7 +138,7 @@ fn check_anonymous_components_statement( match acc{ ArrayAccess(exp) =>{ if exp.contains_anonymous_comp(){ - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + return Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); } }, ComponentAccess(_)=>{}, @@ -160,14 +158,14 @@ pub fn check_anonymous_components_expression( ArrayInLine { meta, values, .. } => { for value in values{ if value.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + return Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); } } Result::Ok(()) }, UniformArray { meta, value, dimension } => { if value.contains_anonymous_comp() || dimension.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())) + Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())) } else{ Result::Ok(()) } @@ -182,7 +180,7 @@ pub fn check_anonymous_components_expression( match acc{ ArrayAccess(exp) =>{ if exp.contains_anonymous_comp(){ - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + return Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); } }, ComponentAccess(_)=>{}, @@ -192,21 +190,21 @@ pub fn check_anonymous_components_expression( }, InfixOp { meta, lhe, rhe, .. } => { if lhe.contains_anonymous_comp() || rhe.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used in the middle of an operation ".to_string())) + Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used in the middle of an operation ".to_string())) } else{ Result::Ok(()) } }, PrefixOp { meta, rhe, .. } => { if rhe.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used in the middle of an operation ".to_string())) + Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used in the middle of an operation ".to_string())) } else{ Result::Ok(()) } }, InlineSwitchOp { meta, cond, if_true, if_false } => { if cond.contains_anonymous_comp() || if_true.contains_anonymous_comp() || if_false.contains_anonymous_comp() { - Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used inside an inline switch ".to_string())) + Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used inside an inline switch ".to_string())) } else{ Result::Ok(()) } @@ -214,7 +212,7 @@ pub fn check_anonymous_components_expression( Call { meta, args, .. } => { for value in args{ if value.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); + return Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); } } Result::Ok(()) @@ -222,7 +220,7 @@ pub fn check_anonymous_components_expression( AnonymousComp {meta, params, signals, .. } => { for value in params{ if value.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); + return Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); } } for value in signals{ @@ -239,10 +237,10 @@ pub fn check_anonymous_components_expression( }, ParallelOp { meta, rhe } => { if !rhe.is_call() && !rhe.is_anonymous_comp() && rhe.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"Bad use of parallel operator in combination with anonymous components ".to_string())); + return Result::Err(anonymous_general_error(meta.clone(),"Bad use of parallel operator in combination with anonymous components ".to_string())); } else if rhe.is_call() && rhe.contains_anonymous_comp() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); + return Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); } Result::Ok(()) }, @@ -401,7 +399,7 @@ pub fn remove_anonymous_from_expression( // get the template we are calling to let template = templates.get(&id); if template.is_none(){ - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"The template does not exist ".to_string())); + return Result::Err(anonymous_general_error(meta.clone(),"The template does not exist ".to_string())); } let id_anon_temp = id.to_string() + "_" + &file_lib.get_line(meta.start, meta.get_file_id()).unwrap().to_string() + "_" + &meta.start.to_string(); @@ -456,7 +454,7 @@ pub fn remove_anonymous_from_expression( for (signal, _) in inputs{ if !names.contains(signal) { let error = signal.clone() + " has not been found in the anonymous call"; - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),error)); + return Result::Err(anonymous_general_error(meta.clone(),error)); } else { let pos = names.iter().position(|r| r == signal).unwrap(); new_signals.push(signals.get(pos).unwrap().clone()); @@ -471,7 +469,7 @@ pub fn remove_anonymous_from_expression( } } if inputs.len() != new_signals.len() { - return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"The number of template input signals must coincide with the number of input parameters ".to_string())); + return Result::Err(anonymous_general_error(meta.clone(),"The number of template input signals must coincide with the number of input parameters ".to_string())); } // generate the substitutions for the inputs @@ -593,7 +591,7 @@ fn check_tuples_statement(stm: &Statement)-> Result<(), Report>{ Statement::IfThenElse { cond, if_case, else_case, meta, .. } => { if cond.contains_tuple() { - Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a condition ".to_string())) + Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used inside a condition ".to_string())) } else{ check_tuples_statement(if_case)?; if else_case.is_some(){ @@ -605,7 +603,7 @@ fn check_tuples_statement(stm: &Statement)-> Result<(), Report>{ Statement::While { meta, cond, stmt } => { if cond.contains_tuple() { - Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a condition ".to_string())) + Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used inside a condition ".to_string())) } else{ check_tuples_statement(stmt) } @@ -623,7 +621,7 @@ fn check_tuples_statement(stm: &Statement)-> Result<(), Report>{ } Statement::Assert { meta, arg} => { if arg.contains_tuple(){ - Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in a return ".to_string())) + Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used in a return ".to_string())) } else{ Result::Ok(()) @@ -631,7 +629,7 @@ fn check_tuples_statement(stm: &Statement)-> Result<(), Report>{ } Statement::Return { meta, value: arg}=> { if arg.contains_tuple(){ - Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a function ".to_string())) + Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used inside a function ".to_string())) } else{ Result::Ok(()) @@ -639,7 +637,7 @@ fn check_tuples_statement(stm: &Statement)-> Result<(), Report>{ } Statement::ConstraintEquality {meta, lhe, rhe } => { if lhe.contains_tuple() || rhe.contains_tuple() { - Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used with the operator === ".to_string())) + Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used with the operator === ".to_string())) } else{ Result::Ok(()) @@ -650,7 +648,7 @@ fn check_tuples_statement(stm: &Statement)-> Result<(), Report>{ { for exp in dimensions.clone(){ if exp.contains_tuple(){ - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); + return Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); } } Result::Ok(()) @@ -675,7 +673,7 @@ fn check_tuples_statement(stm: &Statement)-> Result<(), Report>{ match acc{ ArrayAccess(exp) =>{ if exp.contains_tuple(){ - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array".to_string())); + return Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array".to_string())); } }, ComponentAccess(_)=>{}, @@ -694,14 +692,14 @@ pub fn check_tuples_expression(exp: &Expression) -> Result<(), Report>{ ArrayInLine { meta, values } => { for value in values{ if value.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); + return Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); } } Result::Ok(()) }, UniformArray { meta, value, dimension } => { if value.contains_tuple() || dimension.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); + return Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); } Result::Ok(()) }, @@ -714,7 +712,7 @@ pub fn check_tuples_expression(exp: &Expression) -> Result<(), Report>{ match acc{ ArrayAccess(exp) =>{ if exp.contains_tuple(){ - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array".to_string())); + return Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array".to_string())); } }, ComponentAccess(_)=>{}, @@ -724,21 +722,21 @@ pub fn check_tuples_expression(exp: &Expression) -> Result<(), Report>{ }, InfixOp { meta, lhe, rhe, .. } => { if lhe.contains_tuple() || rhe.contains_tuple() { - Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in the middle of an operation".to_string())) + Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used in the middle of an operation".to_string())) } else{ Result::Ok(()) } }, PrefixOp { meta, rhe, .. } => { if rhe.contains_tuple() { - Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in the middle of an operation".to_string())) + Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used in the middle of an operation".to_string())) } else{ Result::Ok(()) } }, InlineSwitchOp { meta, cond, if_true, if_false } => { if cond.contains_tuple() || if_true.contains_tuple() || if_false.contains_tuple() { - Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside an inline switch".to_string())) + Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used inside an inline switch".to_string())) } else{ Result::Ok(()) } @@ -746,7 +744,7 @@ pub fn check_tuples_expression(exp: &Expression) -> Result<(), Report>{ Call { meta, args, .. } => { for value in args{ if value.contains_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used as a parameter of a function call".to_string())); + return Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used as a parameter of a function call".to_string())); } } Result::Ok(()) @@ -762,7 +760,7 @@ pub fn check_tuples_expression(exp: &Expression) -> Result<(), Report>{ }, ParallelOp { meta, rhe} => { if rhe.contains_tuple() { - Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in a parallel operator ".to_string())) + Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used in a parallel operator ".to_string())) } else{ Result::Ok(()) } @@ -790,21 +788,21 @@ fn remove_tuples_from_statement(stm: Statement) -> Result { substs.push(Statement::UnderscoreSubstitution { meta: meta, op, rhe: rhe }); } } else{ - return Result::Err(TupleError::tuple_general_error(meta.clone(),"The elements of the receiving tuple must be signals or variables.".to_string())); + return Result::Err(tuple_general_error(meta.clone(),"The elements of the receiving tuple must be signals or variables.".to_string())); } } return Result::Ok(build_block(meta.clone(),substs)); } else if values1.len() > 0 { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"The number of elements in both tuples does not coincide".to_string())); + return Result::Err(tuple_general_error(meta.clone(),"The number of elements in both tuples does not coincide".to_string())); } else { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"This expression must be in the right side of an assignment".to_string())); + return Result::Err(tuple_general_error(meta.clone(),"This expression must be in the right side of an assignment".to_string())); } }, (lhe, rhe) => { if lhe.is_tuple() || lhe.is_variable(){ - return Result::Err(TupleError::tuple_general_error(rhe.get_meta().clone(),"This expression must be a tuple or an anonymous component".to_string())); + return Result::Err(tuple_general_error(rhe.get_meta().clone(),"This expression must be a tuple or an anonymous component".to_string())); } else { - return Result::Err(TupleError::tuple_general_error(lhe.get_meta().clone(),"This expression must be a tuple, a component, a signal or a variable ".to_string())); + return Result::Err(tuple_general_error(lhe.get_meta().clone(),"This expression must be a tuple, a component, a signal or a variable ".to_string())); } } } @@ -863,7 +861,7 @@ fn remove_tuples_from_statement(stm: Statement) -> Result { Statement::Substitution { meta, var, op, rhe, access} => { let new_rhe = remove_tuple_from_expression(rhe); if new_rhe.is_tuple() { - return Result::Err(TupleError::tuple_general_error(meta.clone(),"Left-side of the statement is not a tuple".to_string())); + return Result::Err(tuple_general_error(meta.clone(),"Left-side of the statement is not a tuple".to_string())); } if var != "_" { Result::Ok(Statement::Substitution { meta: meta.clone(), var: var, access: access, op: op, rhe: new_rhe }) diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index fd7c884ec..430345d66 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -1,4 +1,4 @@ -use crate::{file_definition::{FileLocation, FileID}, error_definition::Report, error_code::{ReportCode, produce_report}}; +use crate::{file_definition::{FileLocation, FileID}, error_definition::Report, error_code::{ReportCode}}; use num_bigint::BigInt; use serde_derive::{Deserialize, Serialize}; @@ -460,4 +460,151 @@ impl MemoryKnowledge { panic!("abstract memory address was look at without being initialized"); } } +} + + pub fn produce_report(error_code: ReportCode, location : FileLocation, file_id : FileID) -> Report { + use ReportCode::*; + let report = match error_code { + UnclosedComment => { + let mut report = + Report::error("unterminated /* */".to_string(), ReportCode::UnclosedComment); + report.add_primary(location, file_id, "Comment starts here".to_string()); + report + } + NoMain => Report::error( + "No main specified in the project structure".to_string(), + ReportCode::NoMainFoundInProject, + ), + MultipleMain =>{ + Report::error( + "Multiple main components in the project structure".to_string(), + ReportCode::MultipleMainInComponent, + ) + } + MissingSemicolon => { + let mut report = Report::error(format!("Missing semicolon"), + ReportCode::MissingSemicolon); + report.add_primary(location, file_id, "A semicolon is needed here".to_string()); + report + } + UnrecognizedInclude => { + let mut report = + Report::error("unrecognized argument in include directive".to_string(), ReportCode::UnrecognizedInclude); + report.add_primary(location, file_id, "this argument".to_string()); + report + + } + UnrecognizedPragma => { + let mut report = + Report::error("unrecognized argument in pragma directive".to_string(), ReportCode::UnrecognizedPragma); + report.add_primary(location, file_id, "this argument".to_string()); + report + + } + UnrecognizedVersion => { + let mut report = + Report::error("unrecognized version argument in pragma directive".to_string(), ReportCode::UnrecognizedVersion); + report.add_primary(location, file_id, "this argument".to_string()); + report + } + IllegalExpression => { + let mut report = + Report::error("illegal expression".to_string(), ReportCode::IllegalExpression); + report.add_primary(location, file_id, "here".to_string()); + report + } + MultiplePragma => { + let mut report = + Report::error("Multiple pragma directives".to_string(), ReportCode::MultiplePragma); + report.add_primary(location, file_id, "here".to_string()); + report + }, + _ => unreachable!(), + }; + report +} + +pub fn produce_version_warning_report(path : String, version : Version) -> Report { + Report::warning( + format!( + "File {} does not include pragma version. Assuming pragma version {:?}", + path, version + ), + ReportCode::NoCompilerVersionWarning, + ) +} + + +pub fn produce_report_with_message(error_code : ReportCode, msg : String) -> Report { + match error_code { + ReportCode::FileOs => { + Report::error( + format!("Could not open file {}", msg), + ReportCode::FileOs, + ) + } + ReportCode::IncludeNotFound => { + Report::error( + format!(" The file {} to be included has not been found", msg), + ReportCode::FileOs, + ) + }, + _ => unreachable!() + } +} + +pub fn produce_generic_report( msg : String, location : FileLocation, file_id : FileID) -> Report { + let mut report = Report::error(msg, ReportCode::GenericParsing); + report.add_primary(location, file_id, "Error here".to_string()); + report +} +pub fn produce_compiler_version_report(path : String, required_version : Version, version : Version) -> Report { + let report = Report::error( + format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", path, required_version, version ), + ReportCode::CompilerVersionError, + ); + report +} + +pub fn anonymous_inside_condition_error(meta : Meta) -> Report { + let msg = "An anonymous component cannot be used inside a condition ".to_string(); + let mut report = Report::error( + format!("{}", msg), + ReportCode::AnonymousCompError, + ); + let file_id = meta.get_file_id().clone(); + report.add_primary( + meta.location, + file_id, + "This is an anonymous component used inside a condition".to_string(), + ); + report +} + +pub fn anonymous_general_error(meta : Meta, msg : String) -> Report { + let mut report = Report::error( + format!("{}", msg), + ReportCode::AnonymousCompError, + ); + let file_id = meta.get_file_id().clone(); + report.add_primary( + meta.location, + file_id, + "This is the anonymous component whose use is not allowed".to_string(), + ); + report +} + +pub fn tuple_general_error(meta : Meta, msg : String) -> Report { + let mut report = Report::error( + format!("{}", msg), + ReportCode::TupleError, + ); + let file_id = meta.get_file_id().clone(); + report.add_primary( + meta.location, + file_id, + "This is the tuple whose use is not allowed".to_string(), + ); + report } \ No newline at end of file diff --git a/program_structure/src/program_library/error_code.rs b/program_structure/src/program_library/error_code.rs index 1f7e13491..83b9289d0 100644 --- a/program_structure/src/program_library/error_code.rs +++ b/program_structure/src/program_library/error_code.rs @@ -1,8 +1,6 @@ use core::fmt; use std::fmt::Formatter; -use crate::{error_definition::Report, file_definition::{FileLocation, FileID}, ast::Version}; - #[derive(Copy, Clone)] pub enum ReportCode { //Parse Errors @@ -19,6 +17,7 @@ pub enum ReportCode { IncludeNotFound, IllegalExpression, MultiplePragma, + NoCompilerVersionWarning, // AssertWrongType, ParseFail, @@ -51,7 +50,6 @@ pub enum ReportCode { NonEqualTypesInExpression, NonExistentSymbol, NoMainFoundInProject, - NoCompilerVersionWarning, MultipleMainInComponent, MainComponentWithTags, TemplateCallAsArgument, @@ -211,98 +209,3 @@ impl fmt::Display for ReportCode { - -pub fn produce_report_with_message(error_code : ReportCode, msg : String) -> Report { - match error_code { - ReportCode::FileOs => { - Report::error( - format!("Could not open file {}", msg), - ReportCode::FileOs, - ) - } - ReportCode::IncludeNotFound => { - Report::error( - format!(" The file {} to be included has not been found", msg), - ReportCode::FileOs, - ) - }, - _ => unreachable!() - } -} - -pub fn produce_generic_report( msg : String, location : FileLocation, file_id : FileID) -> Report { - let mut report = Report::error(msg, ReportCode::GenericParsing); - report.add_primary(location, file_id, "Error here".to_string()); - report -} -pub fn produce_compiler_version_report(path : String, required_version : Version, version : Version) -> Report { - let report = Report::error( - format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", path, required_version, version ), - ReportCode::CompilerVersionError, - ); - report -} - - - pub fn produce_report(error_code: ReportCode, location : FileLocation, file_id : FileID) -> Report { - use ReportCode::*; - let report = match error_code { - UnclosedComment => { - let mut report = - Report::error("unterminated /* */".to_string(), ReportCode::UnclosedComment); - report.add_primary(location, file_id, "Comment starts here".to_string()); - report - } - NoMain => Report::error( - "No main specified in the project structure".to_string(), - ReportCode::NoMainFoundInProject, - ), - MultipleMain =>{ - Report::error( - "Multiple main components in the project structure".to_string(), - ReportCode::MultipleMainInComponent, - ) - } - MissingSemicolon => { - let mut report = Report::error(format!("Missing semicolon"), - ReportCode::MissingSemicolon); - report.add_primary(location, file_id, "A semicolon is needed here".to_string()); - report - } - UnrecognizedInclude => { - let mut report = - Report::error("unrecognized argument in include directive".to_string(), ReportCode::UnrecognizedInclude); - report.add_primary(location, file_id, "this argument".to_string()); - report - - } - UnrecognizedPragma => { - let mut report = - Report::error("unrecognized argument in pragma directive".to_string(), ReportCode::UnrecognizedPragma); - report.add_primary(location, file_id, "this argument".to_string()); - report - - } - UnrecognizedVersion => { - let mut report = - Report::error("unrecognized version argument in pragma directive".to_string(), ReportCode::UnrecognizedVersion); - report.add_primary(location, file_id, "this argument".to_string()); - report - } - IllegalExpression => { - let mut report = - Report::error("illegal expression".to_string(), ReportCode::IllegalExpression); - report.add_primary(location, file_id, "here".to_string()); - report - } - MultiplePragma => { - let mut report = - Report::error("Multiple pragma directives".to_string(), ReportCode::MultiplePragma); - report.add_primary(location, file_id, "here".to_string()); - report - }, - _ => unreachable!(), - }; - report -} - From 1752787ec370fecdf84c2aa2ab9bccc6e3c1f875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Isabel=20M=C3=A1rquez?= Date: Sun, 26 Feb 2023 19:16:06 +0100 Subject: [PATCH 28/70] Improving error messages --- parser/src/lib.rs | 21 +++++++-- parser/src/parser_logic.rs | 11 ++++- .../src/abstract_syntax_tree/ast.rs | 23 +++++----- .../src/program_library/error_code.rs | 43 ++++++++----------- 4 files changed, 55 insertions(+), 43 deletions(-) diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 73bc18484..25c34d518 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -12,7 +12,7 @@ mod parser_logic; mod syntax_sugar_remover; use include_logic::{FileStack, IncludesGraph}; -use program_structure::ast::{produce_compiler_version_report, produce_report, produce_report_with_message, produce_version_warning_report}; +use program_structure::ast::{produce_compiler_version_report, produce_report, produce_report_with_message, produce_version_warning_report, Expression}; use program_structure::error_code::ReportCode; use program_structure::error_definition::ReportCollection; use program_structure::error_definition::Report; @@ -110,10 +110,10 @@ pub fn run_parser( } if main_components.len() == 0 { - let report = produce_report(ReportCode::NoMain,0..0, 0); + let report = produce_report(ReportCode::NoMainFoundInProject,0..0, 0); Err((file_library, vec![report])) } else if main_components.len() > 1 { - let report = produce_report(ReportCode::MultipleMain,0..0, 0); + let report = produce_report_with_main_components(main_components); Err((file_library, vec![report])) } else { let errors: ReportCollection = includes_graph.get_problematic_paths().iter().map(|path| @@ -154,6 +154,21 @@ pub fn run_parser( } } +fn produce_report_with_main_components(main_components: Vec<(usize, (Vec, Expression), bool)>) -> Report { + let mut j = 0; + let mut r = produce_report(ReportCode::MultipleMain, 0..0, 0); + for (i,exp,_) in main_components{ + if j > 0 { + r.add_secondary(exp.1.get_meta().location.clone(), i, Option::Some("Here it is another main component".to_string())); + } + else { + r.add_primary(exp.1.get_meta().location.clone(), i, "This is a main component".to_string()); + } + j+=1; + } + r +} + fn open_file(path: PathBuf) -> Result<(String, String), Report> /* path, src */ { use std::fs::read_to_string; let path_str = format!("{:?}", path); diff --git a/parser/src/parser_logic.rs b/parser/src/parser_logic.rs index 862c5a9ac..a572c53e7 100644 --- a/parser/src/parser_logic.rs +++ b/parser/src/parser_logic.rs @@ -1,8 +1,8 @@ use super::lang; use program_structure::ast::{AST}; -use program_structure::ast::{produce_report, produce_generic_report}; +use program_structure::ast::produce_report; use program_structure::error_code::ReportCode; -use program_structure::error_definition::{ReportCollection}; +use program_structure::error_definition::{ReportCollection, Report}; use program_structure::file_definition::FileID; pub fn preprocess(expr: &str, file_id: FileID) -> Result { @@ -120,3 +120,10 @@ pub fn parse_file(src: &str, file_id: FileID) -> Result { Ok(ast) } + +fn produce_generic_report(format: String, token: std::ops::Range, file_id: usize) -> Report { + let mut report = Report::error(format, ReportCode::IllegalExpression); + report.add_primary(token, file_id, "here".to_string()); + report +} + diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index 430345d66..2de0c4344 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -471,14 +471,14 @@ impl MemoryKnowledge { report.add_primary(location, file_id, "Comment starts here".to_string()); report } - NoMain => Report::error( + NoMainFoundInProject => Report::error( "No main specified in the project structure".to_string(), ReportCode::NoMainFoundInProject, ), MultipleMain =>{ Report::error( "Multiple main components in the project structure".to_string(), - ReportCode::MultipleMainInComponent, + ReportCode::MultipleMain, ) } MissingSemicolon => { @@ -525,13 +525,15 @@ impl MemoryKnowledge { } pub fn produce_version_warning_report(path : String, version : Version) -> Report { - Report::warning( + let mut r = Report::warning( format!( "File {} does not include pragma version. Assuming pragma version {:?}", path, version ), ReportCode::NoCompilerVersionWarning, - ) + ); + r.add_note(format!("At the beginning of file {}, you should add the directive \"pragma circom \", to indicate which compiler version you are using.",path)); + r } @@ -544,20 +546,17 @@ pub fn produce_report_with_message(error_code : ReportCode, msg : String) -> Rep ) } ReportCode::IncludeNotFound => { - Report::error( + let mut r = Report::error( format!(" The file {} to be included has not been found", msg), - ReportCode::FileOs, - ) + ReportCode::IncludeNotFound, + ); + r.add_note("Consider using compilation option -l to indicate include paths".to_string()); + r }, _ => unreachable!() } } -pub fn produce_generic_report( msg : String, location : FileLocation, file_id : FileID) -> Report { - let mut report = Report::error(msg, ReportCode::GenericParsing); - report.add_primary(location, file_id, "Error here".to_string()); - report -} pub fn produce_compiler_version_report(path : String, required_version : Version, version : Version) -> Report { let report = Report::error( format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", path, required_version, version ), diff --git a/program_structure/src/program_library/error_code.rs b/program_structure/src/program_library/error_code.rs index 83b9289d0..e29f03582 100644 --- a/program_structure/src/program_library/error_code.rs +++ b/program_structure/src/program_library/error_code.rs @@ -1,15 +1,13 @@ use core::fmt; use std::fmt::Formatter; -#[derive(Copy, Clone)] +#[derive(Copy,Clone)] pub enum ReportCode { //Parse Errors UnclosedComment, - GenericParsing, FileOs, - NoMain, - MultipleMain, - CompilerVersion, + NoMainFoundInProject, + MultipleMain, MissingSemicolon, UnrecognizedInclude, UnrecognizedVersion, @@ -17,11 +15,10 @@ pub enum ReportCode { IncludeNotFound, IllegalExpression, MultiplePragma, - NoCompilerVersionWarning, - // - AssertWrongType, - ParseFail, CompilerVersionError, + NoCompilerVersionWarning, + // + AssertWrongType, //we can remove it, it is not used. WrongTypesInAssignOperation, WrongNumberOfArguments(usize, usize), UndefinedFunction, @@ -49,8 +46,6 @@ pub enum ReportCode { NonCompatibleBranchTypes, NonEqualTypesInExpression, NonExistentSymbol, - NoMainFoundInProject, - MultipleMainInComponent, MainComponentWithTags, TemplateCallAsArgument, TemplateWrongNumberOfArguments, @@ -102,15 +97,24 @@ pub enum ReportCode { TupleError, InvalidSignalTagAccess, } + impl fmt::Display for ReportCode { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { use self::ReportCode::*; let string_format = match self { - ParseFail => "P1000", NoMainFoundInProject => "P1001", - MultipleMainInComponent => "P1002", + MultipleMain => "P1002", CompilerVersionError => "P1003", NoCompilerVersionWarning => "P1004", + UnclosedComment => "P1005", + FileOs => "P1006", + MissingSemicolon => "P1008", + UnrecognizedInclude => "P1009", + UnrecognizedVersion => "P1010", + UnrecognizedPragma => "P1011", + IllegalExpression => "P1012", + MultiplePragma => "P1013", + IncludeNotFound => "P1014", WrongTypesInAssignOperation => "T2000", UndefinedFunction => "T2001", UndefinedTemplate => "T2002", @@ -189,19 +193,6 @@ impl fmt::Display for ReportCode { CustomGatesVersionError => "CG05", AnonymousCompError => "TAC01", TupleError => "TAC02", - UnclosedComment => "P01", - GenericParsing => "P02", - FileOs => "P03", - NoMain => "P04", - MultipleMain => "P05", - CompilerVersion => "P06", - MissingSemicolon => "P07", - UnrecognizedInclude => "P08", - UnrecognizedVersion => "P09", - UnrecognizedPragma => "P10", - IllegalExpression => "P11", - MultiplePragma => "P12", - IncludeNotFound => "P13", }; f.write_str(string_format) } From 6ec5f3b3d36cd9f8dfe3651bea005468c616350c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Isabel=20M=C3=A1rquez?= Date: Tue, 28 Feb 2023 11:21:58 +0100 Subject: [PATCH 29/70] Showing warnings when errors are found. --- parser/src/lib.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 25c34d518..825e385fd 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -111,12 +111,14 @@ pub fn run_parser( if main_components.len() == 0 { let report = produce_report(ReportCode::NoMainFoundInProject,0..0, 0); - Err((file_library, vec![report])) + warnings.push(report); + Err((file_library, warnings)) } else if main_components.len() > 1 { let report = produce_report_with_main_components(main_components); - Err((file_library, vec![report])) + warnings.push(report); + Err((file_library, warnings)) } else { - let errors: ReportCollection = includes_graph.get_problematic_paths().iter().map(|path| + let mut errors: ReportCollection = includes_graph.get_problematic_paths().iter().map(|path| Report::error( format!( "Missing custom templates pragma in file {} because of the following chain of includes {}", @@ -127,7 +129,8 @@ pub fn run_parser( ) ).collect(); if errors.len() > 0 { - Err((file_library, errors)) + warnings.append(& mut errors); + Err((file_library, warnings)) } else { let (main_id, main_component, custom_gates) = main_components.pop().unwrap(); let result_program_archive = ProgramArchive::new( @@ -138,14 +141,17 @@ pub fn run_parser( custom_gates, ); match result_program_archive { - Err((lib, rep)) => { - Err((lib, rep)) + Err((lib, mut rep)) => { + warnings.append(&mut rep); + Err((lib, warnings)) } Ok(mut program_archive) => { let lib = program_archive.get_file_library().clone(); let program_archive_result = apply_syntactic_sugar( &mut program_archive); match program_archive_result { - Result::Err(v) => Result::Err((lib,vec![v])), + Result::Err(v) => { + warnings.push(v); + Result::Err((lib,warnings))}, Result::Ok(_) => Ok((program_archive, warnings)), } } From b02c5f3ee96a97a6d730570f6f26dd76f49bba3c Mon Sep 17 00:00:00 2001 From: miguelis Date: Mon, 6 Mar 2023 23:22:52 +0100 Subject: [PATCH 30/70] Update code-assertion.md --- .../code-quality/code-assertion.md | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/mkdocs/docs/circom-language/code-quality/code-assertion.md b/mkdocs/docs/circom-language/code-quality/code-assertion.md index fb69e8063..d985bc615 100644 --- a/mkdocs/docs/circom-language/code-quality/code-assertion.md +++ b/mkdocs/docs/circom-language/code-quality/code-assertion.md @@ -2,15 +2,33 @@ **assert(bool_expression);** -This statement introduces conditions to be checked at execution time. If the condition fails, the witness generation is interrupted and the error is reported. +This statement introduces conditions to be checked. Here, we distinguish two cases depending if **bool_expression** is unknown at compilation time: + +- If the condition only depends on the value of template parameters or field constants, the assert is evaluated in compilation time. If the result of the evaluation is false, then the compilation fails. Consider the next piece of code: + +``` +template A(n) { + signal input in; + assert(n>0); + in * in === n; +} + +component main = A(0); +``` + +Here, the assert can be evaluated during the compilation and the result of the evaluation is false. Thus, the compilation ends throwing error *error[T3001]: False assert reached*. If the main component was defined as `component main = A(2);`, then the compilation correctly finishes. + +Recall that, when a constraint like `in * in === n;` is introduced with `===`, then an assert is automatically added in the witness generation code. In this case, `assert(in * in == n)`. + +- If the condition involves the value of a signal, the assert cannot be evaluated in compilation time. The compiler produces an assert which must be satisfied during the witness generation. If the input `in` passed as parameter to produce the witness does not satisfy the assert, then the witness will not be generated. ```text template Translate(n) { -assert(n<=254); +signal input in; +assert(in<=254); ….. } ``` -Recall that, when a constraint is introduced with ===, then an assert is automatically added in the witness generation code. From 3f93f4bc9e49f40b30fe2c678f0aa253a24f1bf7 Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 7 Mar 2023 11:38:40 +0100 Subject: [PATCH 31/70] Update code-assertion.md --- mkdocs/docs/circom-language/code-quality/code-assertion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs/docs/circom-language/code-quality/code-assertion.md b/mkdocs/docs/circom-language/code-quality/code-assertion.md index d985bc615..98fb05c53 100644 --- a/mkdocs/docs/circom-language/code-quality/code-assertion.md +++ b/mkdocs/docs/circom-language/code-quality/code-assertion.md @@ -20,7 +20,7 @@ Here, the assert can be evaluated during the compilation and the result of the e Recall that, when a constraint like `in * in === n;` is introduced with `===`, then an assert is automatically added in the witness generation code. In this case, `assert(in * in == n)`. -- If the condition involves the value of a signal, the assert cannot be evaluated in compilation time. The compiler produces an assert which must be satisfied during the witness generation. If the input `in` passed as parameter to produce the witness does not satisfy the assert, then the witness will not be generated. +- If the condition or the evaluation of the assert involves the value of a signal, the assert cannot be evaluated in compilation time. The compiler produces an assert which must be satisfied during the witness generation. If the input `in` passed as parameter to produce the witness does not satisfy the assert, then the witness will not be generated. ```text template Translate(n) { From 368503ecc277f0e0ce3313eee7cde99d0b6f4703 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Tue, 7 Mar 2023 17:24:45 +0100 Subject: [PATCH 32/70] working on version v2.1.5: improving --inspect, allowing signal and component declarations in ifs, improving error handling and messages, solving bug x**x, solving bug log in functions, improving efficiency writing --- circom/src/compilation_user.rs | 117 ++--- circom/src/input_user.rs | 3 +- .../src/c_elements/c_code_generator.rs | 10 +- compiler/src/circuit_design/build.rs | 2 +- compiler/src/circuit_design/circuit.rs | 196 ++++++-- compiler/src/hir/sugar_cleaner.rs | 50 ++- .../src/compute_constants.rs | 2 +- .../component_representation.rs | 10 +- constraint_generation/src/execute.rs | 171 ++++--- .../src/execution_data/executed_program.rs | 9 +- .../src/execution_data/executed_template.rs | 13 + constraint_generation/src/lib.rs | 6 +- constraint_list/src/r1cs_porting.rs | 9 +- constraint_list/src/sym_porting.rs | 3 +- constraint_writers/src/r1cs_writer.rs | 49 +- constraint_writers/src/sym_writer.rs | 10 +- dag/src/constraint_correctness_analysis.rs | 157 ++++--- dag/src/lib.rs | 31 +- dag/src/r1cs_porting.rs | 9 +- dag/src/sym_porting.rs | 3 +- parser/src/lang.lalrpop | 7 +- .../src/abstract_syntax_tree/ast.rs | 7 + .../src/program_library/error_code.rs | 63 ++- program_structure/src/utils/memory_slice.rs | 25 +- .../analyzers/signal_declaration_analysis.rs | 10 +- type_analysis/src/analyzers/type_check.rs | 417 +++++++++++------- .../src/analyzers/unknown_known_analysis.rs | 43 +- 27 files changed, 971 insertions(+), 461 deletions(-) diff --git a/circom/src/compilation_user.rs b/circom/src/compilation_user.rs index 53aae2a89..c2b03bfa7 100644 --- a/circom/src/compilation_user.rs +++ b/circom/src/compilation_user.rs @@ -25,70 +25,75 @@ pub struct CompilerConfig { } pub fn compile(config: CompilerConfig) -> Result<(), ()> { - let circuit = compiler_interface::run_compiler( - config.vcp, - Config { debug_output: config.debug_output, produce_input_log: config.produce_input_log, wat_flag: config.wat_flag }, - VERSION - )?; - if config.c_flag { - compiler_interface::write_c(&circuit, &config.c_folder, &config.c_run_name, &config.c_file, &config.dat_file)?; - println!( - "{} {} and {}", - Colour::Green.paint("Written successfully:"), - config.c_file, - config.dat_file - ); - println!( - "{} {}/{}, {}, {}, {}, {}, {}, {} and {}", - Colour::Green.paint("Written successfully:"), - &config.c_folder, - "main.cpp".to_string(), - "circom.hpp".to_string(), - "calcwit.hpp".to_string(), - "calcwit.cpp".to_string(), - "fr.hpp".to_string(), - "fr.cpp".to_string(), - "fr.asm".to_string(), - "Makefile".to_string() - ); - } - match (config.wat_flag, config.wasm_flag) { - (true, true) => { - compiler_interface::write_wasm(&circuit, &config.js_folder, &config.wasm_name, &config.wat_file)?; - println!("{} {}", Colour::Green.paint("Written successfully:"), config.wat_file); - let result = wat_to_wasm(&config.wat_file, &config.wasm_file); - match result { - Result::Err(report) => { - Report::print_reports(&[report], &FileLibrary::new()); - return Err(()); - } - Result::Ok(()) => { - println!("{} {}", Colour::Green.paint("Written successfully:"), config.wasm_file); - } - } + if config.c_flag || config.wat_flag || config.wasm_flag{ + let circuit = compiler_interface::run_compiler( + config.vcp, + Config { debug_output: config.debug_output, produce_input_log: config.produce_input_log, wat_flag: config.wat_flag }, + VERSION + )?; + + if config.c_flag { + compiler_interface::write_c(&circuit, &config.c_folder, &config.c_run_name, &config.c_file, &config.dat_file)?; + println!( + "{} {} and {}", + Colour::Green.paint("Written successfully:"), + config.c_file, + config.dat_file + ); + println!( + "{} {}/{}, {}, {}, {}, {}, {}, {} and {}", + Colour::Green.paint("Written successfully:"), + &config.c_folder, + "main.cpp".to_string(), + "circom.hpp".to_string(), + "calcwit.hpp".to_string(), + "calcwit.cpp".to_string(), + "fr.hpp".to_string(), + "fr.cpp".to_string(), + "fr.asm".to_string(), + "Makefile".to_string() + ); } - (false, true) => { - compiler_interface::write_wasm(&circuit, &config.js_folder, &config.wasm_name, &config.wat_file)?; - let result = wat_to_wasm(&config.wat_file, &config.wasm_file); - std::fs::remove_file(&config.wat_file).unwrap(); - match result { - Result::Err(report) => { - Report::print_reports(&[report], &FileLibrary::new()); - return Err(()); + + match (config.wat_flag, config.wasm_flag) { + (true, true) => { + compiler_interface::write_wasm(&circuit, &config.js_folder, &config.wasm_name, &config.wat_file)?; + println!("{} {}", Colour::Green.paint("Written successfully:"), config.wat_file); + let result = wat_to_wasm(&config.wat_file, &config.wasm_file); + match result { + Result::Err(report) => { + Report::print_reports(&[report], &FileLibrary::new()); + return Err(()); + } + Result::Ok(()) => { + println!("{} {}", Colour::Green.paint("Written successfully:"), config.wasm_file); + } } - Result::Ok(()) => { - println!("{} {}", Colour::Green.paint("Written successfully:"), config.wasm_file); + } + (false, true) => { + compiler_interface::write_wasm(&circuit, &config.js_folder, &config.wasm_name, &config.wat_file)?; + let result = wat_to_wasm(&config.wat_file, &config.wasm_file); + std::fs::remove_file(&config.wat_file).unwrap(); + match result { + Result::Err(report) => { + Report::print_reports(&[report], &FileLibrary::new()); + return Err(()); + } + Result::Ok(()) => { + println!("{} {}", Colour::Green.paint("Written successfully:"), config.wasm_file); + } } } + (true, false) => { + compiler_interface::write_wasm(&circuit, &config.js_folder, &config.wasm_name, &config.wat_file)?; + println!("{} {}", Colour::Green.paint("Written successfully:"), config.wat_file); + } + (false, false) => {} } - (true, false) => { - compiler_interface::write_wasm(&circuit, &config.js_folder, &config.wasm_name, &config.wat_file)?; - println!("{} {}", Colour::Green.paint("Written successfully:"), config.wat_file); - } - (false, false) => {} } + Ok(()) } diff --git a/circom/src/input_user.rs b/circom/src/input_user.rs index 20e2faa7f..99164d1b9 100644 --- a/circom/src/input_user.rs +++ b/circom/src/input_user.rs @@ -213,7 +213,8 @@ mod input_processing { if route.is_file() { Result::Ok(route) } else { - Result::Err(eprintln!("{}", Colour::Red.paint("invalid input file"))) + let route = if route.to_str().is_some() { ": ".to_owned() + route.to_str().unwrap()} else { "".to_owned() }; + Result::Err(eprintln!("{}", Colour::Red.paint("Input file does not exist".to_owned() + &route))) } } diff --git a/code_producers/src/c_elements/c_code_generator.rs b/code_producers/src/c_elements/c_code_generator.rs index 8d74f643c..7bf3d32dc 100644 --- a/code_producers/src/c_elements/c_code_generator.rs +++ b/code_producers/src/c_elements/c_code_generator.rs @@ -658,20 +658,19 @@ pub fn generate_dat_file(dat_file: &mut dyn Write, producer: &CProducer) -> std: //let hml = 256 as u32; //dfile.write_all(&hml.to_be_bytes())?; dat_file.write_all(&hashmap)?; - dat_file.flush()?; + //dat_file.flush()?; let s = generate_dat_witness_to_signal_list(producer.get_witness_to_signal_list()); // list of bytes u64 //let sl = s.len() as u64; //8 bytes //dfile.write_all(&sl.to_be_bytes())?; dat_file.write_all(&s)?; - dat_file.flush()?; + //dat_file.flush()?; let s = generate_dat_constant_list(producer, producer.get_field_constant_list()); // list of bytes Fr dat_file.write_all(&s)?; - dat_file.flush()?; + //dat_file.flush()?; //let ioml = producer.get_io_map().len() as u64; //dfile.write_all(&ioml.to_be_bytes())?; let iomap = generate_dat_io_signals_info(&producer, producer.get_io_map()); dat_file.write_all(&iomap)?; - dat_file.flush()?; /* let ml = producer.get_message_list(); let mll = ml.len() as u64; @@ -684,6 +683,7 @@ pub fn generate_dat_file(dat_file: &mut dyn Write, producer: &CProducer) -> std: dfile.flush()?; } */ + dat_file.flush()?; Ok(()) } pub fn generate_function_list(_producer: &CProducer, list: &TemplateListParallel) -> (String, String) { @@ -972,8 +972,8 @@ pub fn generate_c_file(name: String, producer: &CProducer) -> std::io::Result<() // code.append(&mut ml_def); for l in code { cfile.write_all(l.as_bytes())?; - cfile.flush()?; } + cfile.flush()?; Ok(()) } diff --git a/compiler/src/circuit_design/build.rs b/compiler/src/circuit_design/build.rs index 26ad26850..73ef7bd21 100644 --- a/compiler/src/circuit_design/build.rs +++ b/compiler/src/circuit_design/build.rs @@ -334,8 +334,8 @@ fn write_main_inputs_log(vcp: &VCP) { let length = signal.size(); let msg = format!("{} {}\n", name, length); writer.write_all(msg.as_bytes()).unwrap(); - writer.flush().unwrap(); } + writer.flush().unwrap(); } } diff --git a/compiler/src/circuit_design/circuit.rs b/compiler/src/circuit_design/circuit.rs index fecfdba28..e6dd24cd7 100644 --- a/compiler/src/circuit_design/circuit.rs +++ b/compiler/src/circuit_design/circuit.rs @@ -130,163 +130,164 @@ impl WriteWasm for Circuit { code.push(")".to_string()); code } + fn write_wasm(&self, writer: &mut T, producer: &WASMProducer) -> Result<(), ()> { use code_producers::wasm_elements::wasm_code_generator::*; writer.write_all("(module".as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; let mut code_aux = generate_imports_list(); let mut code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = generate_memory_def_list(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = fr_types(&producer.prime_str); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = generate_types_list(); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = generate_exports_list(); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = fr_code(&producer.prime_str); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = desp_io_subcomponent_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = get_version_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = get_shared_rw_memory_start_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = read_shared_rw_memory_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = write_shared_rw_memory_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = reserve_stack_fr_function_generator(); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = init_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = set_input_signal_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = get_input_signal_size_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = get_raw_prime_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = get_field_num_len32_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = get_input_size_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = get_witness_size_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = get_witness_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = copy_32_in_shared_rw_memory_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = copy_fr_in_shared_rw_memory_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = get_message_char_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = build_buffer_message_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = build_log_message_generator(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; // Actual code from the program for f in &self.functions { f.write_wasm(writer, producer)?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; } for t in &self.templates { t.write_wasm(writer, producer)?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; } code_aux = generate_table_of_template_runs(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = fr_data(&producer.prime_str); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; code_aux = generate_data_list(&producer); code = merge_code(code_aux); writer.write_all(code.as_bytes()).map_err(|_| {})?; - writer.flush().map_err(|_| {})?; + //writer.flush().map_err(|_| {})?; writer.write_all(")".as_bytes()).map_err(|_| {})?; writer.flush().map_err(|_| {}) @@ -409,6 +410,139 @@ impl WriteC for Circuit { (code, "".to_string()) } + fn write_c(&self, writer: &mut T, producer: &CProducer) -> Result<(), ()> { + use code_producers::wasm_elements::wasm_code_generator::merge_code; + use c_code_generator::*; + let mut code = vec![]; + let mut code_write; + // Prologue + code.push("#include ".to_string()); + code.push("#include ".to_string()); + code.push("#include ".to_string()); + code.push("#include \"circom.hpp\"".to_string()); + code.push("#include \"calcwit.hpp\"".to_string()); + + + let mut template_headers = collect_template_headers(producer.get_template_instance_list()); + let function_headers: Vec<_> = self.functions + .iter() + .map(|f| f.header.clone()) + .collect(); + let mut function_headers = collect_function_headers(function_headers); + code.append(&mut template_headers); + code.append(&mut function_headers); + std::mem::drop(template_headers); + std::mem::drop(function_headers); + + let (func_list_no_parallel, func_list_parallel) = generate_function_list( + producer, + producer.get_template_instance_list() + ); + + code.push(format!("Circom_TemplateFunction {}[{}] = {{ {} }};", + function_table(), producer.get_number_of_template_instances(), func_list_no_parallel, + )); + + code.push(format!("Circom_TemplateFunction {}[{}] = {{ {} }};", + function_table_parallel(), producer.get_number_of_template_instances(), func_list_parallel, + )); + + code.push(format!( + "uint get_main_input_signal_start() {{return {};}}\n", + producer.get_number_of_main_outputs() + )); + + code.push(format!( + "uint get_main_input_signal_no() {{return {};}}\n", + producer.get_number_of_main_inputs() + )); + code.push(format!( + "uint get_total_signal_no() {{return {};}}\n", + producer.get_total_number_of_signals() + )); + code.push(format!( + "uint get_number_of_components() {{return {};}}\n", + producer.get_number_of_components() + )); + code.push(format!("uint get_size_of_input_hashmap() {{return {};}}\n", SIZE_INPUT_HASHMAP)); + code.push(format!( + "uint get_size_of_witness() {{return {};}}\n", + producer.get_witness_to_signal_list().len() + )); + code.push(format!( + "uint get_size_of_constants() {{return {};}}\n", + producer.get_field_constant_list().len() + )); + code.push(format!( + "uint get_size_of_io_map() {{return {};}}\n", + producer.get_io_map().len() + )); + //code.append(&mut generate_message_list_def(producer, producer.get_message_list())); + + // Functions to release the memory + let mut release_component_code = generate_function_release_memory_component(); + code.append(&mut release_component_code); + + // Actual code of the circuit + + code_write = merge_code(code); + writer.write_all(code_write.as_bytes()).map_err(|_| {})?; + + code_write = "// function declarations\n".to_string(); + writer.write_all(code_write.as_bytes()).map_err(|_| {})?; + + for f in &self.functions { + let (f_code, _) = f.produce_c(producer, None); + //code.append(&mut f_code); + code_write = merge_code(f_code); + writer.write_all(code_write.as_bytes()).map_err(|_| {})?; + } + + code_write = "// template declarations\n".to_string(); + writer.write_all(code_write.as_bytes()).map_err(|_| {})?; + + for t in &self.templates { + let (t_code, _) = t.produce_c(producer, None); + code_write = merge_code(t_code); + writer.write_all(code_write.as_bytes()).map_err(|_| {})?; + //code.append(&mut t_code); + } + + + // Epilogue + let run_circuit = "void run".to_string(); + let run_circuit_args = vec![declare_circom_calc_wit()]; + let main_template_create = if producer.main_is_parallel{ + producer.main_header.clone() + "_create_parallel" + } else{ + producer.main_header.clone() + "_create" + }; + // We use 0 to indicate that the main component has no father + let create_args = vec!["1".to_string(), "0".to_string(), CIRCOM_CALC_WIT.to_string(), "\"main\"".to_string(), "0".to_string()]; + let create_call = build_call(main_template_create, create_args); + // let ctx_index = format!("{} = {};", declare_ctx_index(), create_call); + let ctx_index = format!("{};", create_call); + // let start_msg = "printf(\"Starting...\\n\");".to_string(); + // let end_msg = "printf(\"End\\n\");".to_string(); + + let main_template_run = if producer.main_is_parallel{ + producer.main_header.clone() + "_run_parallel" + } else{ + producer.main_header.clone() + "_run" + }; + let mut run_args = vec![]; + // run_args.push(CTX_INDEX.to_string()); + run_args.push("0".to_string()); + run_args.push(CIRCOM_CALC_WIT.to_string()); + let run_call = format!("{};", build_call(main_template_run, run_args.clone())); + + let main_run_body = vec![ctx_index, run_call]; + code_write = build_callable(run_circuit, run_circuit_args, main_run_body) + "\n"; + writer.write_all(code_write.as_bytes()).map_err(|_| {})?; + writer.flush().map_err(|_| {}) + + } + } impl Circuit { diff --git a/compiler/src/hir/sugar_cleaner.rs b/compiler/src/hir/sugar_cleaner.rs index 3572ce7cd..b2aa3f1c1 100644 --- a/compiler/src/hir/sugar_cleaner.rs +++ b/compiler/src/hir/sugar_cleaner.rs @@ -267,16 +267,58 @@ fn extend_parallel(expr: &mut Expression, state: &mut State, context: &Context) } fn extend_infix(expr: &mut Expression, state: &mut State, context: &Context) -> ExtendedSyntax { - use Expression::InfixOp; - if let InfixOp { lhe, rhe, .. } = expr { + use program_structure::ast::Expression::Number; + use program_structure::ast::Expression::Variable; + use program_structure::ast::Expression::InfixOp; + use program_structure::ast::ExpressionInfixOpcode::Pow; + use program_structure::ast::ExpressionInfixOpcode::Add; + use num_bigint_dig::BigInt; + if let InfixOp { lhe, rhe, infix_op, .. } = expr { let mut lh_expand = extend_expression(lhe, state, context); let mut rh_expand = extend_expression(rhe, state, context); lh_expand.initializations.append(&mut rh_expand.initializations); let mut extended = lh_expand; let mut expr = vec![*lhe.clone(), *rhe.clone()]; sugar_filter(&mut expr, state, &mut extended.initializations); - *rhe = Box::new(expr.pop().unwrap()); - *lhe = Box::new(expr.pop().unwrap()); + + let mut expr_rhe = expr.pop().unwrap(); + let expr_lhe = expr.pop().unwrap(); + + // remove when fixed fr in wasm + if *infix_op == Pow{ + match (&expr_rhe, &expr_lhe){ + (Variable{name: name_1, ..}, Variable{name: name_2, ..}) + if name_1 == name_2 =>{ + expr_rhe = Expression::InfixOp{ + meta: rhe.get_meta().clone(), + lhe: Box::new(Number(rhe.get_meta().clone(), BigInt::from(0))), + rhe: Box::new(expr_rhe), + infix_op: Add, + }; + } + (Number(_, value_1 ), Number(_, value_2)) + if value_1 == value_2 =>{ + expr_rhe = Expression::InfixOp{ + meta: rhe.get_meta().clone(), + lhe: Box::new(Number(rhe.get_meta().clone(), BigInt::from(0))), + rhe: Box::new(expr_rhe), + infix_op: Add, + }; + } + _ =>{ + + } + + } + + *rhe = Box::new(expr_rhe); + *lhe = Box::new(expr_lhe); + + + } + + // just to solve the case of X ** X in wasm + extended } else { unreachable!() diff --git a/constraint_generation/src/compute_constants.rs b/constraint_generation/src/compute_constants.rs index 9f07480f0..84d203e86 100644 --- a/constraint_generation/src/compute_constants.rs +++ b/constraint_generation/src/compute_constants.rs @@ -194,7 +194,7 @@ fn transform_big_int_to_usize(v: &BigInt) -> Option { fn report_invalid_dimension(meta: &Meta, reports: &mut ReportCollection) { use program_structure::error_code::ReportCode; use program_structure::error_definition::Report; - let error_code = ReportCode::InvalidArraySize; + let error_code = ReportCode::InvalidArraySize(0); let msg = "Invalid array size".to_string(); let mut report = Report::error(msg, error_code); let message = "This expression can not be used as an array size".to_string(); diff --git a/constraint_generation/src/environment_utils/component_representation.rs b/constraint_generation/src/environment_utils/component_representation.rs index c1f85b796..61ff9d33f 100644 --- a/constraint_generation/src/environment_utils/component_representation.rs +++ b/constraint_generation/src/environment_utils/component_representation.rs @@ -188,11 +188,15 @@ impl ComponentRepresentation { return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedComponent)); } if self.outputs.contains_key(signal_name) && !self.unassigned_inputs.is_empty() { - return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::MissingInputs)); + // we return the name of an input that has not been assigned + let ex_signal = self.unassigned_inputs.iter().next().unwrap().0.clone(); + return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::MissingInputs(ex_signal))); } if !self.is_initialized { - return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedComponent)); + // we return the name of an input with tags that has not been assigned + let ex_signal = self.unassigned_tags.iter().next().unwrap().clone(); + return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::MissingInputTags(ex_signal))); } let slice = if self.inputs.contains_key(signal_name) { @@ -245,7 +249,7 @@ impl ComponentRepresentation { let tags_input = component.inputs_tags.get_mut(signal_name).unwrap(); for (t, value) in tags_input{ if !tags.contains_key(t){ - return Result::Err(MemoryError::AssignmentMissingTags); + return Result::Err(MemoryError::AssignmentMissingTags(t.clone())); } else{ if component.unassigned_tags.contains(signal_name){ *value = tags.get(t).unwrap().clone(); diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index 1fe21d291..fd852e7a7 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -441,6 +441,13 @@ fn execute_statement( index += 1; } println!(""); + } else{ + for arglog in args { + if let LogArgument::LogExp(arg) = arglog{ + let f_result = execute_expression(arg, program_archive, runtime, flag_verbose)?; + let _arith = safe_unwrap_to_single_arithmetic_expression(f_result, line!()); + } + } } Option::None } @@ -464,21 +471,21 @@ fn execute_statement( let arithmetic_slice = safe_unwrap_to_arithmetic_slice(f_result, line!()); if *op == AssignOp::AssignConstraintSignal{ for i in 0..AExpressionSlice::get_number_of_cells(&arithmetic_slice){ - let _value_cell = treat_result_with_memory_error( + let value_cell = treat_result_with_memory_error( AExpressionSlice::access_value_by_index(&arithmetic_slice, i), meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - //let constraint_expression = AExpr::transform_expression_to_constraint_form( - // value_cell, - // runtime.constants.get_p(), - //).unwrap(); - //if let Option::Some(node) = actual_node { - //for signal in constraint_expression.take_signals(){ - // node.add_underscored_signal(signal); - //} - //} + let constraint_expression = AExpr::transform_expression_to_constraint_form( + value_cell, + runtime.constants.get_p(), + ).unwrap(); + if let Option::Some(node) = actual_node { + for signal in constraint_expression.take_signals(){ + node.add_underscored_signal(signal); + } + } } } Option::None @@ -626,7 +633,7 @@ fn execute_expression( let expr_id = expr.get_meta().elem_id; let res_p = res.arithmetic_slice.clone(); if let Some(slice) = res_p { - if slice.is_single() { + if slice.is_single() && !expr.is_call(){ let value = AExpressionSlice::unwrap_to_single(slice); Analysis::computed(&mut runtime.analysis, expr_id, value); } @@ -1521,7 +1528,7 @@ fn signal_to_arith(symbol: String, slice: SignalSlice) -> Result Result::Ok(()), - Result::Err(MemoryError::MismatchedDimensionsWeak) => { - let report = Report::warning("Typing warning: Mismatched dimensions, assigning to an array an expression of smaller length, the remaining positions are assigned to 0".to_string(), RuntimeError); + Result::Err(MemoryError::MismatchedDimensionsWeak(dim_given, dim_original)) => { + let report = Report::warning( + format!("Typing warning: Mismatched dimensions, assigning to an array an expression of smaller length, the remaining positions are assigned to 0.\n Expected length: {}, given {}", + dim_original, dim_given), + RuntimeError); add_report_to_runtime(report, meta, runtime_errors, call_trace); Ok(()) }, @@ -2061,16 +2080,24 @@ fn treat_result_with_memory_error_void( let report = match memory_error { MemoryError::InvalidAccess(type_invalid_access) => { match type_invalid_access{ - TypeInvalidAccess::MissingInputs =>{ - Report::error("Exception caused by invalid access: trying to access to an output signal of a component with not all its inputs initialized".to_string(), + TypeInvalidAccess::MissingInputs(input) =>{ + Report::error( + format!("Exception caused by invalid access: trying to access to an output signal of a component with not all its inputs initialized.\n Missing input: {}", + input), + RuntimeError) + }, + TypeInvalidAccess::MissingInputTags(input) =>{ + Report::error( + format!("Exception caused by invalid access: trying to access to a signal of a component with not all its inputs with tags initialized.\n Missing input (with tags): {}", + input), RuntimeError) }, TypeInvalidAccess::NoInitializedComponent =>{ Report::error("Exception caused by invalid access: trying to access to a component that is not initialized" .to_string(), RuntimeError) }, - TypeInvalidAccess::BadDimensions =>{ - Report::error("Exception caused by invalid access: invalid dimensions" .to_string(), + TypeInvalidAccess::NoInitializedSignal =>{ + Report::error("Exception caused by invalid access: trying to access to a signal that is not initialized" .to_string(), RuntimeError) } } @@ -2090,15 +2117,19 @@ fn treat_result_with_memory_error_void( MemoryError::OutOfBoundsError => { Report::error("Out of bounds exception".to_string(), RuntimeError) }, - MemoryError::MismatchedDimensions => { - Report::error("Typing error found: mismatched dimensions".to_string(), RuntimeError) + MemoryError::MismatchedDimensions(given, orig) => { + Report::error( + format!("Typing error found: mismatched dimensions.\n Expected length: {}, given {}", + orig, given), + RuntimeError) }, MemoryError::UnknownSizeDimension => { Report::error("Array dimension with unknown size".to_string(), RuntimeError) }, - MemoryError::AssignmentMissingTags => Report::error( - "Invalid assignment: missing tags required by input signal".to_string(), + MemoryError::AssignmentMissingTags(tag) => Report::error( + format!("Invalid assignment: missing tags required by input signal. \n Missing tag: {}", + tag), RuntimeError, ), MemoryError::AssignmentTagAfterInit => Report::error( @@ -2113,7 +2144,7 @@ fn treat_result_with_memory_error_void( "Invalid assignment: this tag belongs to an input which already got a value".to_string(), RuntimeError, ), - MemoryError::MismatchedDimensionsWeak => unreachable!() + MemoryError::MismatchedDimensionsWeak(..) => unreachable!() , MemoryError::TagValueNotInitializedAccess => Report::error( "Tag value has not been previously initialized".to_string(), @@ -2143,16 +2174,24 @@ fn treat_result_with_memory_error( let report = match memory_error { MemoryError::InvalidAccess(type_invalid_access) => { match type_invalid_access{ - TypeInvalidAccess::MissingInputs =>{ - Report::error("Exception caused by invalid access: trying to access to an output signal of a component with not all its inputs initialized".to_string(), + TypeInvalidAccess::MissingInputs(input) =>{ + Report::error( + format!("Exception caused by invalid access: trying to access to an output signal of a component with not all its inputs initialized.\n Missing input: {}", + input), + RuntimeError) + }, + TypeInvalidAccess::MissingInputTags(input) =>{ + Report::error( + format!("Exception caused by invalid access: trying to access to a signal of a component with not all its inputs with tags initialized.\n Missing input (with tags): {}", + input), RuntimeError) }, TypeInvalidAccess::NoInitializedComponent =>{ Report::error("Exception caused by invalid access: trying to access to a component that is not initialized" .to_string(), RuntimeError) }, - TypeInvalidAccess::BadDimensions =>{ - Report::error("Exception caused by invalid access: invalid dimensions" .to_string(), + TypeInvalidAccess::NoInitializedSignal =>{ + Report::error("Exception caused by invalid access: trying to access to a signal that is not initialized" .to_string(), RuntimeError) } } @@ -2169,8 +2208,9 @@ fn treat_result_with_memory_error( }, } }, - MemoryError::AssignmentMissingTags => Report::error( - "Invalid assignment: missing tags required by input signal".to_string(), + MemoryError::AssignmentMissingTags(tag) => Report::error( + format!("Invalid assignment: missing tags required by input signal. \n Missing tag: {}", + tag), RuntimeError, ), MemoryError::AssignmentTagAfterInit => Report::error( @@ -2188,8 +2228,11 @@ fn treat_result_with_memory_error( MemoryError::OutOfBoundsError => { Report::error("Out of bounds exception".to_string(), RuntimeError) }, - MemoryError::MismatchedDimensions => { - Report::error(" Typing error found: mismatched dimensions".to_string(), RuntimeError) + MemoryError::MismatchedDimensions(given, orig) => { + Report::error( + format!("Typing error found: mismatched dimensions.\n Expected length: {}, given {}", + orig, given), + RuntimeError) }, MemoryError::UnknownSizeDimension => { Report::error("Array dimension with unknown size".to_string(), RuntimeError) @@ -2198,7 +2241,7 @@ fn treat_result_with_memory_error( Report::error("Tag value has not been previously initialized".to_string(), RuntimeError) } - MemoryError::MismatchedDimensionsWeak => { + MemoryError::MismatchedDimensionsWeak(..) => { unreachable!() }, MemoryError::MissingInputs(name) => Report::error( diff --git a/constraint_generation/src/execution_data/executed_program.rs b/constraint_generation/src/execution_data/executed_program.rs index 5b6dda57a..bfdf979bb 100644 --- a/constraint_generation/src/execution_data/executed_program.rs +++ b/constraint_generation/src/execution_data/executed_program.rs @@ -100,7 +100,7 @@ impl ExecutedProgram { node_index } - pub fn export(mut self, mut program: ProgramArchive, flag_verbose: bool) -> ExportResult { + pub fn export(mut self, mut program: ProgramArchive, flag_verbose: bool, flag_inspect: bool) -> ExportResult { use super::executed_template::templates_in_mixed_arrays; fn merge_mixed(org: Vec, new: Vec) -> Vec { let mut result = Vec::with_capacity(org.len()); @@ -132,8 +132,11 @@ impl ExecutedProgram { } temp_instances[dag.main_id()].is_not_parallel_component = true; - let mut w = dag.constraint_analysis()?; - warnings.append(&mut w); + dag.clean_constraints(); + if flag_inspect{ + let mut w = dag.constraint_analysis()?; + warnings.append(&mut w); + } let dag_stats = produce_dags_stats(&dag); crate::compute_constants::manage_functions(&mut program, flag_verbose, &self.prime)?; diff --git a/constraint_generation/src/execution_data/executed_template.rs b/constraint_generation/src/execution_data/executed_template.rs index f0651408b..a56fc328a 100644 --- a/constraint_generation/src/execution_data/executed_template.rs +++ b/constraint_generation/src/execution_data/executed_template.rs @@ -78,6 +78,7 @@ pub struct ExecutedTemplate { pub is_parallel: bool, pub has_parallel_sub_cmp: bool, pub is_custom_gate: bool, + pub underscored_signals: Vec, connexions: Vec, } @@ -112,6 +113,7 @@ impl ExecutedTemplate { components: ComponentCollector::new(), number_of_components: 0, connexions: Vec::new(), + underscored_signals: Vec::new(), } } @@ -180,6 +182,10 @@ impl ExecutedTemplate { self.constraints.push(constraint); } + pub fn add_underscored_signal(&mut self, signal: &str) { + self.underscored_signals.push(signal.to_string()); + } + pub fn template_name(&self) -> &String { &self.template_name } @@ -279,12 +285,19 @@ impl ExecutedTemplate { dag.set_number_of_subcomponents_indexes(self.number_of_components); } fn build_constraints(&self, dag: &mut DAG) { + for c in &self.constraints { let correspondence = dag.get_main().unwrap().correspondence(); let cc = Constraint::apply_correspondence(c, correspondence); dag.add_constraint(cc); } + for s in &self.underscored_signals{ + let correspondence = dag.get_main().unwrap().correspondence(); + let new_s = correspondence.get(s).unwrap().clone(); + dag.add_underscored_signal(new_s); + } } + pub fn export_to_circuit(self, instances: &mut [TemplateInstance]) -> TemplateInstance { use SignalType::*; fn build_triggers( diff --git a/constraint_generation/src/lib.rs b/constraint_generation/src/lib.rs index 7b0fe53f0..a893a0f8e 100644 --- a/constraint_generation/src/lib.rs +++ b/constraint_generation/src/lib.rs @@ -41,7 +41,7 @@ pub fn build_circuit(program: ProgramArchive, config: BuildConfig) -> BuildRespo Report::print_reports(&r, &files); })?; Report::print_reports(&warnings, &files); - let (mut dag, mut vcp, warnings) = export(exe, program, config.flag_verbose).map_err(|r| { + let (mut dag, mut vcp, warnings) = export(exe, program, config.flag_verbose, config.inspect_constraints).map_err(|r| { Report::print_reports(&r, &files); })?; if config.inspect_constraints { @@ -71,8 +71,8 @@ fn instantiation(program: &ProgramArchive, flag_verbose: bool, prime: &String) - } } -fn export(exe: ExecutedProgram, program: ProgramArchive, flag_verbose: bool) -> ExportResult { - let exported = exe.export(program, flag_verbose); +fn export(exe: ExecutedProgram, program: ProgramArchive, flag_verbose: bool, flag_inspect: bool) -> ExportResult { + let exported = exe.export(program, flag_verbose, flag_inspect); exported } diff --git a/constraint_list/src/r1cs_porting.rs b/constraint_list/src/r1cs_porting.rs index e6a2e5493..7f012dda6 100644 --- a/constraint_list/src/r1cs_porting.rs +++ b/constraint_list/src/r1cs_porting.rs @@ -50,8 +50,9 @@ pub fn port_r1cs(list: &ConstraintList, output: &str, custom_gates: bool) -> Res SignalSection::write_signal_usize(&mut signal_section, id)?; } let r1cs = signal_section.end_section()?; - - if custom_gates { + if !custom_gates { + R1CSWriter::finish_writing(r1cs)?; + } else { let mut custom_gates_used_section = R1CSWriter::start_custom_gates_used_section(r1cs)?; let (usage_data, occurring_order) = { let mut usage_data = vec![]; @@ -114,9 +115,9 @@ pub fn port_r1cs(list: &ConstraintList, output: &str, custom_gates: bool) -> Res find_indexes(occurring_order, application_data) }; custom_gates_applied_section.write_custom_gates_applications(application_data)?; - let _r1cs = custom_gates_applied_section.end_section()?; + let r1cs = custom_gates_applied_section.end_section()?; + R1CSWriter::finish_writing(r1cs)?; } - Log::print(&log); Ok(()) } diff --git a/constraint_list/src/sym_porting.rs b/constraint_list/src/sym_porting.rs index edb4341f4..e891e2cbb 100644 --- a/constraint_list/src/sym_porting.rs +++ b/constraint_list/src/sym_porting.rs @@ -6,7 +6,8 @@ pub fn port_sym(list: &ConstraintList, file_name: &str) -> Result<(), ()> { let iter = EncodingIterator::new(&list.dag_encoding); let mut dot_sym = SymFile::new(file_name)?; signal_iteration(iter, &list.signal_map, &mut dot_sym)?; - SymFile::close(dot_sym); + SymFile::finish_writing(dot_sym)?; + //SymFile::close(dot_sym); Ok(()) } diff --git a/constraint_writers/src/r1cs_writer.rs b/constraint_writers/src/r1cs_writer.rs index 13b223d17..7bc0fd7bc 100644 --- a/constraint_writers/src/r1cs_writer.rs +++ b/constraint_writers/src/r1cs_writer.rs @@ -29,10 +29,10 @@ fn bigint_as_bytes(number: &BigInt, with_bytes: usize) -> (Vec, usize) { fn initialize_section(writer: &mut BufWriter, header: &[u8]) -> Result { writer.write_all(header).map_err(|_err| {})?; - writer.flush().map_err(|_err| {})?; + //writer.flush().map_err(|_err| {})?; let go_back = writer.seek(SeekFrom::Current(0)).map_err(|_err| {})?; writer.write_all(PLACE_HOLDER).map_err(|_| {})?; - writer.flush().map_err(|_err| {})?; + //writer.flush().map_err(|_err| {})?; Result::Ok(go_back) } @@ -42,7 +42,8 @@ fn end_section(writer: &mut BufWriter, go_back: u64, size: usize) -> Resul let (stream, _) = bigint_as_bytes(&BigInt::from(size), 8); writer.write_all(&stream).map_err(|_err| {})?; writer.seek(SeekFrom::Start(go_back_1)).map_err(|_err| {})?; - writer.flush().map_err(|_| {}) + //writer.flush().map_err(|_| {}) + Result::Ok(()) } fn obtain_linear_combination_block( @@ -81,21 +82,21 @@ fn write_constraint( let (block_b, size_b) = obtain_linear_combination_block(b, field_size); let (block_c, size_c) = obtain_linear_combination_block(c, field_size); file.write_all(&block_a).map_err(|_err| {})?; - file.flush().map_err(|_err| {})?; + //file.flush().map_err(|_err| {})?; file.write_all(&block_b).map_err(|_err| {})?; - file.flush().map_err(|_err| {})?; + //file.flush().map_err(|_err| {})?; file.write_all(&block_c).map_err(|_err| {})?; - file.flush().map_err(|_err| {})?; + //file.flush().map_err(|_err| {})?; Result::Ok(size_a + size_b + size_c) } fn initialize_file(writer: &mut BufWriter, num_sections: u8) -> Result<(), ()> { writer.write_all(MAGIC).map_err(|_err| {})?; - writer.flush().map_err(|_err| {})?; + //writer.flush().map_err(|_err| {})?; writer.write_all(VERSION).map_err(|_err| {})?; - writer.flush().map_err(|_err| {})?; + //writer.flush().map_err(|_err| {})?; writer.write_all(&[num_sections, 0, 0, 0]).map_err(|_err| {})?; - writer.flush().map_err(|_err| {})?; + //writer.flush().map_err(|_err| {})?; Result::Ok(()) } @@ -225,6 +226,10 @@ impl R1CSWriter { sections: r1cs.sections }) } + + pub fn finish_writing(mut r1cs: R1CSWriter) -> Result<(), ()> { + r1cs.writer.flush().map_err(|_err| {}) + } } pub struct HeaderData { @@ -243,7 +248,7 @@ impl HeaderSection { let (length_stream, bytes_size) = bigint_as_bytes(&BigInt::from(self.field_size), 4); self.writer.write_all(&length_stream).map_err(|_err| {})?; self.writer.write_all(&field_stream).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {})?; + //self.writer.flush().map_err(|_err| {})?; self.size += bytes_field + bytes_size; let data_stream = [ @@ -258,7 +263,7 @@ impl HeaderSection { let (stream, size) = bigint_as_bytes(&BigInt::from(data[0]), data[1]); self.size += size; self.writer.write_all(&stream).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {})?; + //self.writer.flush().map_err(|_err| {})?; } Result::Ok(()) } @@ -326,8 +331,8 @@ impl SignalSection { ) -> Result<(), ()> where T: AsRef<[u8]> { let (bytes, size) = into_format(bytes.as_ref(), 8); self.size += size; - self.writer.write_all(&bytes).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {}) + self.writer.write_all(&bytes).map_err(|_err| {})//?; + //self.writer.flush().map_err(|_err| {}) } pub fn write_signal_usize(&mut self, signal: usize) -> Result<(), ()> { @@ -356,7 +361,7 @@ impl CustomGatesUsedSection { bigint_as_bytes(&BigInt::from(no_custom_gates), 4); self.size += no_custom_gates_size; self.writer.write_all(&no_custom_gates_stream).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {})?; + //self.writer.flush().map_err(|_err| {})?; for custom_gate in data { let custom_gate_name = custom_gate.0; @@ -364,7 +369,7 @@ impl CustomGatesUsedSection { self.size += custom_gate_name_stream.len() + 1; self.writer.write_all(custom_gate_name_stream).map_err(|_err| {})?; self.writer.write_all(&[0]).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {})?; + //self.writer.flush().map_err(|_err| {})?; let custom_gate_parameters = custom_gate.1; let no_custom_gate_parameters = custom_gate_parameters.len(); @@ -372,13 +377,13 @@ impl CustomGatesUsedSection { bigint_as_bytes(&BigInt::from(no_custom_gate_parameters), 4); self.size += no_custom_gate_parameters_size; self.writer.write_all(&no_custom_gate_parameters_stream).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {})?; + //self.writer.flush().map_err(|_err| {})?; for parameter in custom_gate_parameters { let (parameter_stream, parameter_size) = bigint_as_bytes(¶meter, self.field_size); self.size += parameter_size; self.writer.write(¶meter_stream).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {})?; + //self.writer.flush().map_err(|_err| {})?; } } @@ -406,7 +411,7 @@ impl CustomGatesAppliedSection { bigint_as_bytes(&BigInt::from(no_custom_gate_applications), 4); self.size += no_custom_gate_applications_size; self.writer.write_all(&no_custom_gate_applications_stream).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {})?; + //self.writer.flush().map_err(|_err| {})?; for custom_gate_application in data { let custom_gate_index = custom_gate_application.0; @@ -414,7 +419,7 @@ impl CustomGatesAppliedSection { bigint_as_bytes(&BigInt::from(custom_gate_index), 4); self.size += custom_gate_index_size; self.writer.write_all(&custom_gate_index_stream).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {})?; + //self.writer.flush().map_err(|_err| {})?; let custom_gate_signals = custom_gate_application.1; let no_custom_gate_signals = custom_gate_signals.len(); @@ -422,16 +427,16 @@ impl CustomGatesAppliedSection { bigint_as_bytes(&BigInt::from(no_custom_gate_signals), 4); self.size += no_custom_gate_signals_size; self.writer.write_all(&no_custom_gate_signals_stream).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {})?; + //self.writer.flush().map_err(|_err| {})?; for signal in custom_gate_signals { let (signal_stream, signal_size) = bigint_as_bytes(&BigInt::from(signal), 8); self.size += signal_size; self.writer.write(&signal_stream).map_err(|_err| {})?; - self.writer.flush().map_err(|_err| {})?; + //self.writer.flush().map_err(|_err| {})?; } } - + //self.writer.flush().map_err(|_err| {})?; Result::Ok(()) } diff --git a/constraint_writers/src/sym_writer.rs b/constraint_writers/src/sym_writer.rs index d0396a1c9..8299c07e9 100644 --- a/constraint_writers/src/sym_writer.rs +++ b/constraint_writers/src/sym_writer.rs @@ -26,9 +26,13 @@ impl SymFile { pub fn write_sym_elem(sym: &mut SymFile, elem: SymElem) -> Result<(), ()> { sym.writer.write_all(elem.to_string().as_bytes()).map_err(|_err| {})?; - sym.writer.write_all(b"\n").map_err(|_err| {})?; - sym.writer.flush().map_err(|_err| {}) + sym.writer.write_all(b"\n").map_err(|_err| {}) //?; + //sym.writer.flush().map_err(|_err| {}) + } + + pub fn finish_writing(mut sym: SymFile) -> Result<(), ()> { + sym.writer.flush().map_err(|_err| {}) } - pub fn close(_sym: SymFile) {} + // pub fn close(_sym: SymFile) {} } diff --git a/dag/src/constraint_correctness_analysis.rs b/dag/src/constraint_correctness_analysis.rs index 431d36b62..811e38992 100644 --- a/dag/src/constraint_correctness_analysis.rs +++ b/dag/src/constraint_correctness_analysis.rs @@ -5,113 +5,136 @@ use program_structure::error_definition::{Report, ReportCollection}; use std::collections::{HashMap, HashSet}; type C = Constraint; -const UNCONSTRAINED_SIGNAL: &'static str = "Unconstrained signal."; const UNCONSTRAINED_SIGNAL_CODE: ReportCode = ReportCode::UnconstrainedSignal; +const UNCONSTRAINED_IOSIGNAL_CODE: ReportCode = ReportCode::UnconstrainedIOSignal; -const ONE_CONSTRAINT_INTERMEDIATE: &'static str = "One constraint intermediate:"; -const ONE_CONSTRAINT_INTERMEDIATE_CODE: ReportCode = ReportCode::OneConstraintIntermediate; -const NO_OUTPUT: &'static str = "There is no output signal"; -const NO_OUTPUT_CODE: ReportCode = ReportCode::NoOutputInInstance; struct UnconstrainedSignal; impl UnconstrainedSignal { - pub fn new(signal: &str, template: &str) -> Report { - let msg = format!("In template \"{}\". {} \"{}\"", template, UNCONSTRAINED_SIGNAL, signal); - let hint = format!("Maybe use: {}*0 === 0", signal); - let mut report = Report::warning(msg, UNCONSTRAINED_SIGNAL_CODE); - report.add_note(hint); - report - } -} - -struct OneConstraintIntermediate; -impl OneConstraintIntermediate { - pub fn new(signal: &str, template: &str) -> Report { - let msg = - format!("In template \"{}\". {} \"{}\"", template, ONE_CONSTRAINT_INTERMEDIATE, signal); - let hint = format!("Maybe use: {}*0 === 0", signal); - let mut report = Report::warning(msg, ONE_CONSTRAINT_INTERMEDIATE_CODE); - report.add_note(hint); - report + pub fn new(signal: &str, template: &str, examples: &Vec) -> Report { + + if examples.len() == 1{ + let msg = format!("In template \"{}\": Local signal {} does not appear in any constraint", template, examples[0]); + let report = Report::warning(msg, UNCONSTRAINED_SIGNAL_CODE); + report + } else{ + let msg = format!("In template \"{}\": Array of local signals {} contains a total of {} signals that do not appear in any constraint", template, signal, examples.len()); + let mut report = Report::warning(msg, UNCONSTRAINED_SIGNAL_CODE); + let ex = format!("For example: {}, {}.", examples[0], examples[1]); + report.add_note(ex); + report + } } } -struct NoOutputInNode; -impl NoOutputInNode { - pub fn new(template: &str) -> Report { - let msg = format!("In template \"{}\". {}.", template, NO_OUTPUT); - Report::warning(msg, NO_OUTPUT_CODE) +struct UnconstrainedIOSignal; +impl UnconstrainedIOSignal { + pub fn new(signal: &str, template: &str, examples: &Vec) -> Report { + + if examples.len() == 1{ + let msg = format!("In template \"{}\": Subcomponent input/output signal {} does not appear in any constraint of the father component", template, examples[0]); + let report = Report::warning(msg, UNCONSTRAINED_IOSIGNAL_CODE); + report + } else{ + let msg = format!("In template \"{}\": Array of subcomponent input/output signals {} contains a total of {} signals that do not appear in any constraint of the father component", template, signal, examples.len()); + let mut report = Report::warning(msg, UNCONSTRAINED_IOSIGNAL_CODE); + let ex = format!("For example: {}, {}.", examples[0], examples[1]); + report.add_note(ex); + report + } } } #[derive(Copy, Clone, Eq, PartialEq)] enum SignalType { - IO, - Intermediate, + Local, + IOSubcomponent, } struct Analysis { template_name: String, - no_outputs: usize, // signal name, type and number of appearances signal_stats: Vec<(String, SignalType, usize)>, } +fn split_signal_name_index(name: &String)-> String{ + let split_components:Vec<&str> = name.split(".").collect(); // split the name of components + let mut signal_name = "".to_string(); + for i in 0..split_components.len()-1{ + signal_name = signal_name + split_components[i] + "."; // take the index of the components + } + // no take the index of the array position + let aux_last_component = split_components[split_components.len()-1].to_string(); + let split_index_last_component = + aux_last_component.split("[").next().unwrap(); + signal_name + split_index_last_component +} + fn analysis_interpretation(analysis: Analysis, result: &mut AnalysisResult) { let tmp_name = analysis.template_name; let stats = analysis.signal_stats; - if analysis.no_outputs == 0 { - result.warnings.push(NoOutputInNode::new(&tmp_name)); - } + + let mut signal2unconstrainedex: HashMap)> = HashMap::new(); + for (name, xtype, no_appearances) in stats { if no_appearances == 0 { - result.warnings.push(UnconstrainedSignal::new(&name, &tmp_name)); - } else if SignalType::Intermediate == xtype && no_appearances < 2 { - result.warnings.push(OneConstraintIntermediate::new(&name, &tmp_name)); + let signal_name = split_signal_name_index(&name); + + match signal2unconstrainedex.get_mut(&signal_name){ + Some((_, examples)) =>{ + examples.push(name.clone()); + }, + None =>{ + signal2unconstrainedex.insert(signal_name.to_string(), (xtype, vec![name.clone()])); + } + } + } + } + for (name, (xtype, examples)) in signal2unconstrainedex{ + if xtype == SignalType::Local{ + result.warnings.push(UnconstrainedSignal::new(&name, &tmp_name, &examples)); + } else{ + result.warnings.push(UnconstrainedIOSignal::new(&name, &tmp_name, &examples)); } } } -fn visit_node(node: &mut Node) -> Analysis { - let mut io = HashSet::new(); - for io_signal in &node.io_signals { - io.insert(*io_signal); - } +fn visit_node(node: &Node) -> Analysis { let mut constraint_counter = HashMap::new(); let mut rev_correspondence = HashMap::new(); for (name, id) in &node.signal_correspondence { - rev_correspondence.insert(*id, name.to_string()); - constraint_counter.insert(*id, 0); + if node.is_reachable_signal(*id){ + rev_correspondence.insert(*id, name.to_string()); + constraint_counter.insert(*id, 0); + } } - let length_bound = Vec::len(&node.constraints); - let work = std::mem::replace(&mut node.constraints, Vec::with_capacity(length_bound)); - for mut constraint in work { + for constraint in &node.constraints { let signals = constraint.take_cloned_signals(); for signal in signals { let prev = constraint_counter.remove(&signal).unwrap(); constraint_counter.insert(signal, prev + 1); } - C::remove_zero_value_coefficients(&mut constraint); - if !C::is_empty(&constraint) { - Vec::push(&mut node.constraints, constraint); - } + } + + for signal in &node.underscored_signals{ + let prev = constraint_counter.remove(&signal).unwrap(); + constraint_counter.insert(*signal, prev + 1); } let mut signal_stats = vec![]; for (id, appearances) in constraint_counter { let name = rev_correspondence.remove(&id).unwrap(); - let signal_type = if io.contains(&id) || !node.is_local_signal(id) { - SignalType::IO + let signal_type = if node.is_local_signal(id) { + SignalType::Local } else { - SignalType::Intermediate + SignalType::IOSubcomponent }; signal_stats.push((name, signal_type, appearances)); } signal_stats.sort_by(|a, b| a.0.cmp(&b.0)); Analysis { template_name: node.template_name.clone(), - no_outputs: node.outputs_length, signal_stats, } } @@ -120,11 +143,31 @@ pub struct AnalysisResult { pub errors: ReportCollection, pub warnings: ReportCollection, } -pub fn analyse(nodes: &mut [Node]) -> AnalysisResult { +pub fn clean_constraints(nodes: &mut [Node]){ + for node in nodes{ + let length_bound = Vec::len(&node.constraints); + let work = std::mem::replace(&mut node.constraints, Vec::with_capacity(length_bound)); + for mut constraint in work { + C::remove_zero_value_coefficients(&mut constraint); + if !C::is_empty(&constraint) { + Vec::push(&mut node.constraints, constraint); + } + } + } +} + +pub fn analyse(nodes: &[Node]) -> AnalysisResult { let mut result = AnalysisResult { errors: vec![], warnings: vec![] }; + let mut visited : HashSet = HashSet::new(); for node in nodes { let analysis = visit_node(node); - analysis_interpretation(analysis, &mut result); + if !node.is_custom_gate() && !visited.contains(&node.template_name.clone()){ + let mut result2 = AnalysisResult { errors: vec![], warnings: vec![] }; + analysis_interpretation(analysis, &mut result2); + result.errors.append(&mut result2.errors); + result.warnings.append(&mut result2.warnings); + visited.insert(node.template_name.clone()); + } } result } diff --git a/dag/src/lib.rs b/dag/src/lib.rs index a7a675d1f..cd494b800 100644 --- a/dag/src/lib.rs +++ b/dag/src/lib.rs @@ -145,9 +145,11 @@ pub struct Node { signal_correspondence: HashMap, ordered_signals: Vec, locals: HashSet, + reachables: HashSet, // locals and io of subcomponents forbidden_if_main: HashSet, io_signals: Vec, constraints: Vec, + underscored_signals: Vec, is_parallel: bool, has_parallel_sub_cmp: bool, is_custom_gate: bool, @@ -182,6 +184,7 @@ impl Node { self.public_inputs_length += if is_public { 1 } else { 0 }; self.signal_correspondence.insert(name, id); self.locals.insert(id); + self.reachables.insert(id); self.number_of_signals += 1; self.entry.out_number += 1; self.inputs_length += 1; @@ -196,6 +199,7 @@ impl Node { self.signal_correspondence.insert(name, id); self.forbidden_if_main.insert(id); self.locals.insert(id); + self.reachables.insert(id); self.number_of_signals += 1; self.entry.out_number += 1; self.outputs_length += 1; @@ -205,6 +209,7 @@ impl Node { let id = self.number_of_signals + 1; self.signal_correspondence.insert(name, id); self.locals.insert(id); + self.reachables.insert(id); self.number_of_signals += 1; self.entry.out_number += 1; self.intermediates_length += 1; @@ -214,6 +219,10 @@ impl Node { self.constraints.push(constraint) } + fn add_underscored_signal(&mut self, signal: usize) { + self.underscored_signals.push(signal) + } + fn set_number_of_subcomponents_indexes(&mut self, number_scmp: usize) { self.number_of_subcomponents_indexes = number_scmp } @@ -226,6 +235,10 @@ impl Node { self.locals.contains(&s) } + fn is_reachable_signal(&self, s: usize) -> bool { + self.reachables.contains(&s) + } + pub fn number_of_signals(&self) -> usize { self.number_of_signals } @@ -333,14 +346,20 @@ impl DAG { }; // add correspondence to current node let mut correspondence = std::mem::take(&mut self.nodes[from].signal_correspondence); + let mut reachables = std::mem::take(&mut self.nodes[from].reachables); for (signal, id) in self.nodes[to].correspondence() { if self.nodes[to].is_local_signal(*id) { let concrete_name = format!("{}.{}", label, signal); let concrete_value = with.in_number + *id; correspondence.insert(concrete_name, concrete_value); + if *id <= self.nodes[to].inputs_length + self.nodes[to].outputs_length{ + // in case it is an input/output signal + reachables.insert(concrete_value); + } } } self.nodes[from].signal_correspondence = correspondence; + self.nodes[from].reachables = reachables; self.nodes[from].has_parallel_sub_cmp |= self.nodes[to].is_parallel; self.adjacency[from].push(with); self.adjacency[from].last() @@ -389,6 +408,12 @@ impl DAG { } } + pub fn add_underscored_signal(&mut self, signal: usize) { + if let Option::Some(node) = self.get_mut_main() { + node.add_underscored_signal(signal); + } + } + pub fn set_number_of_subcomponents_indexes(&mut self, number_scmp: usize){ if let Option::Some(node) = self.get_mut_main() { node.set_number_of_subcomponents_indexes(number_scmp); @@ -434,7 +459,7 @@ impl DAG { } pub fn constraint_analysis(&mut self) -> Result { - let reports = constraint_correctness_analysis::analyse(&mut self.nodes); + let reports = constraint_correctness_analysis::analyse(&self.nodes); if reports.errors.is_empty() { Ok(reports.warnings) } else { @@ -442,6 +467,10 @@ impl DAG { } } + pub fn clean_constraints(&mut self) { + constraint_correctness_analysis::clean_constraints(&mut self.nodes); + } + pub fn generate_r1cs_output(&self, output_file: &str, custom_gates: bool) -> Result<(), ()> { r1cs_porting::write(self, output_file, custom_gates) } diff --git a/dag/src/r1cs_porting.rs b/dag/src/r1cs_porting.rs index 396b60a28..27eae18bd 100644 --- a/dag/src/r1cs_porting.rs +++ b/dag/src/r1cs_porting.rs @@ -43,8 +43,10 @@ pub fn write(dag: &DAG, output: &str, custom_gates: bool) -> Result<(), ()> { signal_section.write_signal_usize(signal)?; } let r1cs = signal_section.end_section()?; - - if custom_gates { + + if !custom_gates { + R1CSWriter::finish_writing(r1cs)?; + } else { let mut custom_gates_used_section = R1CSWriter::start_custom_gates_used_section(r1cs)?; let (usage_data, occurring_order) = { let mut usage_data = vec![]; @@ -101,7 +103,8 @@ pub fn write(dag: &DAG, output: &str, custom_gates: bool) -> Result<(), ()> { find_indexes(occurring_order, application_data) }; custom_gates_applied_section.write_custom_gates_applications(application_data)?; - let _r1cs = custom_gates_applied_section.end_section()?; + let r1cs = custom_gates_applied_section.end_section()?; + R1CSWriter::finish_writing(r1cs)?; } Log::print(&log); diff --git a/dag/src/sym_porting.rs b/dag/src/sym_porting.rs index 3d18bab80..bd0993434 100644 --- a/dag/src/sym_porting.rs +++ b/dag/src/sym_porting.rs @@ -7,7 +7,8 @@ pub fn write(dag: &DAG, file_name: &str) -> Result<(), ()> { let tree = Tree::new(dag); let mut dot_sym = SymFile::new(file_name)?; visit_tree(&tree, &mut dot_sym)?; - SymFile::close(dot_sym); + SymFile::finish_writing(dot_sym)?; + //SymFile::close(dot_sym); Ok(()) } diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 7aabb379d..7203c262a 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -24,7 +24,10 @@ IdentifierListDef : Vec = { let mut v = v; v.push(e); v - } + }, + ",")*> ! => { + errors.push(produce_report(ReportCode::ExpectedIdentifier, s..e, file_id)); + v } }; Semicolon:() = { @@ -729,7 +732,7 @@ HEXNUMBER : BigInt = { }; IDENTIFIER : String = { - r"[$_]*[a-zA-Z][a-zA-Z$_0-9]*" => String::from(<>) + r"[$_]*[a-zA-Z][a-zA-Z$_0-9]*" => String::from(<>), }; STRING : String = { diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index 2de0c4344..cdd48c868 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -390,6 +390,7 @@ pub fn build_log_expression(expr: Expression) -> LogArgument { LogArgument::LogExp(expr) } + #[derive(Default, Clone)] pub struct TypeKnowledge { reduces_to: Option, @@ -519,6 +520,12 @@ impl MemoryKnowledge { report.add_primary(location, file_id, "here".to_string()); report }, + ExpectedIdentifier => { + let mut report = + Report::error("An identifier is expected".to_string(), ReportCode::ExpectedIdentifier); + report.add_primary(location, file_id, "This should be an identifier".to_string()); + report + }, _ => unreachable!(), }; report diff --git a/program_structure/src/program_library/error_code.rs b/program_structure/src/program_library/error_code.rs index e29f03582..ba1ca693a 100644 --- a/program_structure/src/program_library/error_code.rs +++ b/program_structure/src/program_library/error_code.rs @@ -12,14 +12,18 @@ pub enum ReportCode { UnrecognizedInclude, UnrecognizedVersion, UnrecognizedPragma, + ExpectedIdentifier, IncludeNotFound, IllegalExpression, MultiplePragma, - CompilerVersionError, NoCompilerVersionWarning, - // - AssertWrongType, //we can remove it, it is not used. - WrongTypesInAssignOperation, + CompilerVersionError, + WrongTypesInAssignOperationOperatorSignal, + WrongTypesInAssignOperationOperatorNoSignal, + WrongTypesInAssignOperationTemplate, + WrongTypesInAssignOperationExpression, + WrongTypesInAssignOperationArrayTemplates, + WrongTypesInAssignOperationDims(usize, usize), WrongNumberOfArguments(usize, usize), UndefinedFunction, UndefinedTemplate, @@ -41,7 +45,7 @@ pub enum ReportCode { FunctionPathWithoutReturn, FunctionReturnError, ForbiddenDeclarationInFunction, - NonHomogeneousArray, + NonHomogeneousArray(usize, usize), NonBooleanCondition, NonCompatibleBranchTypes, NonEqualTypesInExpression, @@ -58,11 +62,12 @@ pub enum ReportCode { InvalidArgumentInCall, InconsistentReturnTypesInBlock, InconsistentStaticInformation, - InvalidArrayAccess, + InvalidArrayAccess(usize, usize), InvalidSignalAccess, InvalidTagAccess, InvalidTagAccessAfterArray, - InvalidArraySize, + InvalidArraySize(usize), + InvalidArraySizeT, InvalidArrayType, ForStatementIllConstructed, BadArrayAccess, @@ -72,10 +77,11 @@ pub enum ReportCode { ConstraintGeneratorInFunction, WrongSignalTags, InvalidPartialArray, - MustBeSingleArithmetic, + MustBeSingleArithmetic(usize), + MustBeSingleArithmeticT, MustBeArithmetic, OutputTagCannotBeModifiedOutside, - MustBeSameDimension, + MustBeSameDimension(usize, usize), ExpectedDimDiffGotDim(usize, usize), RuntimeError, RuntimeWarning, @@ -85,8 +91,10 @@ pub enum ReportCode { NonComputableExpression, // Constraint analysis codes UnconstrainedSignal, - OneConstraintIntermediate, - NoOutputInInstance, + UnconstrainedIOSignal, + UnusedInput, + UnusedOutput, + ErrorWat2Wasm, CustomGateIntermediateSignalWarning, CustomGateConstraintError, @@ -94,8 +102,10 @@ pub enum ReportCode { CustomGatesPragmaError, CustomGatesVersionError, AnonymousCompError, + UnderscoreWithNoSignalWarning, TupleError, InvalidSignalTagAccess, + UninitializedComponent, } impl fmt::Display for ReportCode { @@ -106,6 +116,12 @@ impl fmt::Display for ReportCode { MultipleMain => "P1002", CompilerVersionError => "P1003", NoCompilerVersionWarning => "P1004", + WrongTypesInAssignOperationOperatorSignal => "T2000", + WrongTypesInAssignOperationOperatorNoSignal => "T2000", + WrongTypesInAssignOperationArrayTemplates => "T2000", + WrongTypesInAssignOperationTemplate => "T2000", + WrongTypesInAssignOperationExpression => "T2000", + WrongTypesInAssignOperationDims(..) => "T2000", UnclosedComment => "P1005", FileOs => "P1006", MissingSemicolon => "P1008", @@ -115,7 +131,7 @@ impl fmt::Display for ReportCode { IllegalExpression => "P1012", MultiplePragma => "P1013", IncludeNotFound => "P1014", - WrongTypesInAssignOperation => "T2000", + ExpectedIdentifier => "P1015", UndefinedFunction => "T2001", UndefinedTemplate => "T2002", UninitializedSymbolInExpression => "T2003", @@ -132,7 +148,7 @@ impl fmt::Display for ReportCode { FunctionPathWithoutReturn => "T2014", FunctionReturnError => "T2015", ForbiddenDeclarationInFunction => "T2016", - NonHomogeneousArray => "T2017", + NonHomogeneousArray(..) => "T2017", NonBooleanCondition => "T2018", NonCompatibleBranchTypes => "T2019", NonEqualTypesInExpression => "T2020", @@ -148,12 +164,13 @@ impl fmt::Display for ReportCode { InvalidArgumentInCall => "T2029", InconsistentReturnTypesInBlock => "T2030", InconsistentStaticInformation => "T2031", - InvalidArrayAccess => "T2032", + InvalidArrayAccess(..) => "T2032", InvalidSignalAccess => "T2046", InvalidSignalTagAccess => "T2047", InvalidTagAccess => "T2048", InvalidTagAccessAfterArray => "T2049", - InvalidArraySize => "T2033", + InvalidArraySize(..) => "T2033", + InvalidArraySizeT => "T2033", InvalidArrayType => "T2034", ForStatementIllConstructed => "T2035", BadArrayAccess => "T2035", @@ -162,12 +179,12 @@ impl fmt::Display for ReportCode { NotAllowedOperation => "T2038", ConstraintGeneratorInFunction => "T2039", WrongSignalTags => "T2040", - AssertWrongType => "T2041", UnknownIndex => "T2042", InvalidPartialArray => "T2043", - MustBeSingleArithmetic => "T2044", + MustBeSingleArithmetic(..) => "T2044", + MustBeSingleArithmeticT => "T2044", ExpectedDimDiffGotDim(..) => "T2045", - MustBeSameDimension => "T2046", + MustBeSameDimension(..) => "T2046", MustBeArithmetic => "T2047", OutputTagCannotBeModifiedOutside => "T2048", UnreachableTags => "T2049", @@ -181,10 +198,12 @@ impl fmt::Display for ReportCode { NonConstantArrayLength => "T20463", NonComputableExpression => "T20464", WrongNumberOfArguments(..) => "T20465", + UninitializedComponent => "T20466", // Constraint analysis codes UnconstrainedSignal => "CA01", - OneConstraintIntermediate => "CA02", - NoOutputInInstance => "CA03", + UnconstrainedIOSignal => "CA02", + UnusedInput => "CA03", + UnusedOutput => "CA04", ErrorWat2Wasm => "W01", CustomGateIntermediateSignalWarning => "CG01", CustomGateConstraintError => "CG02", @@ -193,10 +212,8 @@ impl fmt::Display for ReportCode { CustomGatesVersionError => "CG05", AnonymousCompError => "TAC01", TupleError => "TAC02", + UnderscoreWithNoSignalWarning => "TAC03", }; f.write_str(string_format) } } - - - diff --git a/program_structure/src/utils/memory_slice.rs b/program_structure/src/utils/memory_slice.rs index 8ac99ea45..51c6ee2a8 100644 --- a/program_structure/src/utils/memory_slice.rs +++ b/program_structure/src/utils/memory_slice.rs @@ -2,9 +2,10 @@ use num_bigint_dig::BigInt; use std::fmt::{Display, Formatter}; pub enum TypeInvalidAccess{ - MissingInputs, + MissingInputs(String), + MissingInputTags(String), NoInitializedComponent, - BadDimensions + NoInitializedSignal } pub enum TypeAssignmentError{ @@ -17,9 +18,9 @@ pub enum MemoryError { AssignmentError(TypeAssignmentError), InvalidAccess(TypeInvalidAccess), UnknownSizeDimension, - MismatchedDimensions, - MismatchedDimensionsWeak, - AssignmentMissingTags, + MismatchedDimensions(usize, usize), + MismatchedDimensionsWeak(usize, usize), + AssignmentMissingTags(String), AssignmentTagAfterInit, AssignmentTagTwice, AssignmentTagInput, @@ -111,14 +112,14 @@ impl MemorySlice { while i < new_values.route.len() { if new_values.route[i] < memory_slice.route[initial_index_new + i] { - if is_strict{ // case variables: we allow the assignment of smaller arrays - return Result::Err(MemoryError::MismatchedDimensions); - } else{ // case signals: we do not allow - return Result::Err(MemoryError::MismatchedDimensionsWeak); + if is_strict{ // case signals: we do not allow + return Result::Err(MemoryError::MismatchedDimensions(new_values.route[i], memory_slice.route[initial_index_new + i])); + } else{ // case variables: we allow the assignment of smaller arrays + return Result::Err(MemoryError::MismatchedDimensionsWeak(new_values.route[i], memory_slice.route[initial_index_new + i])); } } if new_values.route[i] > memory_slice.route[initial_index_new + i] { - return Result::Err(MemoryError::MismatchedDimensions); + return Result::Err(MemoryError::MismatchedDimensions(new_values.route[i], memory_slice.route[initial_index_new + i])); } i += 1; } @@ -207,7 +208,7 @@ impl MemorySlice { } Result::Ok(()) }, - Result::Err(MemoryError::MismatchedDimensionsWeak) => { + Result::Err(MemoryError::MismatchedDimensionsWeak(dim_1, dim_2)) => { let mut cell = MemorySlice::get_initial_cell(memory_slice, access)?; // if MemorySlice::get_number_of_cells(new_values) // > (MemorySlice::get_number_of_cells(memory_slice) - cell) @@ -218,7 +219,7 @@ impl MemorySlice { memory_slice.values[cell] = value.clone(); cell += 1; } - Result::Err(MemoryError::MismatchedDimensionsWeak) + Result::Err(MemoryError::MismatchedDimensionsWeak(dim_1, dim_2)) }, Result::Err(error) => return Err(error), } diff --git a/type_analysis/src/analyzers/signal_declaration_analysis.rs b/type_analysis/src/analyzers/signal_declaration_analysis.rs index 05845804e..aa63f7a60 100644 --- a/type_analysis/src/analyzers/signal_declaration_analysis.rs +++ b/type_analysis/src/analyzers/signal_declaration_analysis.rs @@ -27,9 +27,9 @@ fn treat_statement( use Statement::*; match stmt { IfThenElse { if_case, else_case, .. } => { - treat_statement(if_case, false, template_id, reports); + treat_statement(if_case, signal_declaration_allowed, template_id, reports); if let Option::Some(else_block) = else_case { - treat_statement(else_block, false, template_id, reports); + treat_statement(else_block, signal_declaration_allowed, template_id, reports); } } While { stmt, .. } => { @@ -37,14 +37,14 @@ fn treat_statement( } Block { stmts, .. } => { for stmt in stmts.iter() { - treat_statement(stmt, false, template_id, reports); + treat_statement(stmt, signal_declaration_allowed, template_id, reports); } } InitializationBlock { meta, xtype, .. } => match xtype { - VariableType::Signal(_, _) | VariableType::Component | VariableType::AnonymousComponent => { + VariableType::Signal(_, _) | VariableType::Component => { if !signal_declaration_allowed { let mut report = Report::error( - "Signal or component declaration outside initial scope".to_string(), + "Signal or component declaration inside While scope. Signal and component can only be defined in the initial scope or in If scopes with known condition".to_string(), ReportCode::SignalOutsideOriginalScope, ); let location = diff --git a/type_analysis/src/analyzers/type_check.rs b/type_analysis/src/analyzers/type_check.rs index 82c153c88..c40dd04fc 100644 --- a/type_analysis/src/analyzers/type_check.rs +++ b/type_analysis/src/analyzers/type_check.rs @@ -81,7 +81,7 @@ pub fn type_check(program_archive: &ProgramArchive) -> Result 0 { + if dim_type.is_template() { add_report( - ReportCode::InvalidArraySize, + ReportCode::InvalidArraySizeT, + dim_expression.get_meta(), + &mut analysis_information.reports, + ); + } + else if dim_type.dim() > 0 { + add_report( + ReportCode::InvalidArraySize(dim_type.dim()), dim_expression.get_meta(), &mut analysis_information.reports, ); @@ -171,7 +178,7 @@ fn type_statement( }; let access_information_result = - treat_access(var, access, meta, program_archive, analysis_information); + treat_access(access, meta, program_archive, analysis_information); let access_information = if let Result::Ok(info) = access_information_result { info @@ -207,46 +214,96 @@ fn type_statement( | (SymbolInformation::Var(_), AssignOp::AssignVar) | (SymbolInformation::Component(_), AssignOp::AssignVar) => {} | (SymbolInformation::Tag, AssignOp::AssignVar) => {} + (SymbolInformation::Signal(_), AssignOp::AssignVar)=>{ + return add_report( + ReportCode::WrongTypesInAssignOperationOperatorSignal, + meta, + &mut analysis_information.reports, + ); + } _ => { return add_report( - ReportCode::WrongTypesInAssignOperation, + ReportCode::WrongTypesInAssignOperationOperatorNoSignal, meta, &mut analysis_information.reports, ); } } match symbol_information { - SymbolInformation::Component(template) - if template.is_none() && rhe_type.is_template() => - { - let (current_template, _) = analysis_information - .environment - .get_mut_component_or_break(var, file!(), line!()); - *current_template = rhe_type.template; + SymbolInformation::Component(possible_template) =>{ + if rhe_type.is_template(){ + if possible_template.is_none(){ + let (current_template, _) = analysis_information + .environment + .get_mut_component_or_break(var, file!(), line!()); + *current_template = rhe_type.template; + } else{ + let template = possible_template.unwrap(); + let r_template = rhe_type.template.unwrap(); + if template != r_template { + add_report( + ReportCode::WrongTypesInAssignOperationArrayTemplates, + meta, + &mut analysis_information.reports, + ) + } + } + } else{ + add_report( + ReportCode::WrongTypesInAssignOperationTemplate, + meta, + &mut analysis_information.reports, + ) + } } - SymbolInformation::Component(possible_template) - if possible_template.is_some() && rhe_type.is_template() => - { - let template = possible_template.unwrap(); - let r_template = rhe_type.template.unwrap(); - if template != r_template { + SymbolInformation::Signal(dim) =>{ + if rhe_type.is_template(){ add_report( - ReportCode::WrongTypesInAssignOperation, + ReportCode::WrongTypesInAssignOperationExpression, + meta, + &mut analysis_information.reports, + ) + } else if dim != rhe_type.dim(){ + add_report( + ReportCode::WrongTypesInAssignOperationDims(dim, rhe_type.dim()), meta, &mut analysis_information.reports, ) } + + } + SymbolInformation::Var(dim) =>{ + if rhe_type.is_template(){ + add_report( + ReportCode::WrongTypesInAssignOperationExpression, + meta, + &mut analysis_information.reports, + ) + } else if dim != rhe_type.dim(){ + add_report( + ReportCode::WrongTypesInAssignOperationDims(dim, rhe_type.dim()), + meta, + &mut analysis_information.reports, + ) + } + + } + SymbolInformation::Tag =>{ + if rhe_type.is_template(){ + add_report( + ReportCode::WrongTypesInAssignOperationExpression, + meta, + &mut analysis_information.reports, + ) + } else if 0 != rhe_type.dim(){ + add_report( + ReportCode::WrongTypesInAssignOperationDims(0, rhe_type.dim()), + meta, + &mut analysis_information.reports, + ) + } + } - SymbolInformation::Signal(dim) - if dim == rhe_type.dim() && !rhe_type.is_template() => {} - SymbolInformation::Var(dim) - if dim == rhe_type.dim() && !rhe_type.is_template() => {} - SymbolInformation::Tag if !rhe_type.is_template() => {} - _ => add_report( - ReportCode::WrongTypesInAssignOperation, - meta, - &mut analysis_information.reports, - ), } } ConstraintEquality { lhe, rhe, .. } => { @@ -278,7 +335,7 @@ fn type_statement( } if rhe_type.dim() != lhe_type.dim() { add_report( - ReportCode::MustBeSameDimension, + ReportCode::MustBeSameDimension(rhe_type.dim(), lhe_type.dim()), rhe.get_meta(), &mut analysis_information.reports, ); @@ -287,15 +344,21 @@ fn type_statement( LogCall { args, meta } => { for arglog in args { if let LogArgument::LogExp(arg) = arglog{ - let arg_response = type_expression(arg, program_archive, analysis_information); + let arg_response = type_expression(&arg, program_archive, analysis_information); let arg_type = if let Result::Ok(t) = arg_response { t } else { return; }; - if arg_type.is_template() || arg_type.dim() > 0 { + if arg_type.is_template() { add_report( - ReportCode::MustBeSingleArithmetic, + ReportCode::MustBeSingleArithmeticT, + meta, + &mut analysis_information.reports, + ) + } else if arg_type.dim() > 0 { + add_report( + ReportCode::MustBeSingleArithmetic(arg_type.dim()), meta, &mut analysis_information.reports, ) @@ -310,9 +373,15 @@ fn type_statement( } else { return; }; - if arg_type.is_template() || arg_type.dim() > 0 { + if arg_type.is_template() { + add_report( + ReportCode::MustBeSingleArithmeticT, + meta, + &mut analysis_information.reports, + ) + } else if arg_type.dim() > 0 { add_report( - ReportCode::MustBeSingleArithmetic, + ReportCode::MustBeSingleArithmetic(arg_type.dim()), meta, &mut analysis_information.reports, ) @@ -347,9 +416,15 @@ fn type_statement( } else { return; }; - if cond_type.is_template() || cond_type.dim() > 0 { + if cond_type.is_template(){ add_report( - ReportCode::MustBeSingleArithmetic, + ReportCode::MustBeSingleArithmeticT, + cond.get_meta(), + &mut analysis_information.reports, + ) + }else if cond_type.dim() > 0 { + add_report( + ReportCode::MustBeSingleArithmetic(cond_type.dim()), cond.get_meta(), &mut analysis_information.reports, ) @@ -363,9 +438,15 @@ fn type_statement( } else { return; }; - if cond_type.is_template() || cond_type.dim() > 0 { + if cond_type.is_template(){ + add_report( + ReportCode::MustBeSingleArithmeticT, + cond.get_meta(), + &mut analysis_information.reports, + ) + }else if cond_type.dim() > 0 { add_report( - ReportCode::MustBeSingleArithmetic, + ReportCode::MustBeSingleArithmetic(cond_type.dim()), cond.get_meta(), &mut analysis_information.reports, ) @@ -424,7 +505,7 @@ fn type_expression( ); } else if inferred_dim != value_type.dim() { add_report( - ReportCode::NonHomogeneousArray, + ReportCode::NonHomogeneousArray(inferred_dim, value_type.dim()), expression.get_meta(), &mut analysis_information.reports, ); @@ -433,7 +514,7 @@ fn type_expression( Result::Ok(FoldedType::arithmetic_type(inferred_dim + 1)) } UniformArray { meta, value, dimension } => { - let value_type = type_expression(value, program_archive, analysis_information).unwrap(); + let value_type = type_expression(value, program_archive, analysis_information)?; if value_type.is_template() { add_report( ReportCode::InvalidArrayType, @@ -441,7 +522,7 @@ fn type_expression( &mut analysis_information.reports, ); }; - let dim_type = type_expression(dimension, program_archive, analysis_information).unwrap(); + let dim_type = type_expression(dimension, program_archive, analysis_information)?; if dim_type.is_template() { add_report( ReportCode::InvalidArrayType, @@ -517,12 +598,19 @@ fn type_expression( } else { return Result::Ok(if_true_type); }; - if cond_type.is_template() || cond_type.dim() > 0 { + if cond_type.is_template(){ add_report( - ReportCode::MustBeSingleArithmetic, + ReportCode::MustBeSingleArithmeticT, cond.get_meta(), &mut analysis_information.reports, - ); + ) + } + else if cond_type.dim() > 0 { + add_report( + ReportCode::MustBeSingleArithmetic(cond_type.dim()), + cond.get_meta(), + &mut analysis_information.reports, + ) } let if_false_type = if let Result::Ok(f) = if_false_response { @@ -542,7 +630,7 @@ fn type_expression( Variable { name, access, meta, .. } => { debug_assert!(analysis_information.environment.has_symbol(name)); let access_information = - treat_access(name, access, meta, program_archive, analysis_information)?; + treat_access( access, meta, program_archive, analysis_information)?; let environment = &analysis_information.environment; let reports = &mut analysis_information.reports; let symbol_information = apply_access_to_symbol( @@ -642,9 +730,9 @@ fn treat_sequence_of_statements( //************************************************* Expression support ************************************************* // 0: symbol dimensions accessed // 1: Signal accessed and dimensions accessed in that signal (optional) +// 2: Tag accessed (optional) type AccessInfo = (ArithmeticType, Option<(String, ArithmeticType)>, Option); fn treat_access( - var: &String, accesses: &[Access], meta: &Meta, program_archive: &ProgramArchive, @@ -652,74 +740,62 @@ fn treat_access( ) -> Result { use Access::*; let mut access_info: AccessInfo = (0, Option::None, Option::None); - let mut successful = Result::Ok(()); for access in accesses { match access { ArrayAccess(index) => { - let index_response = type_expression(index, program_archive, analysis_information); - if let Option::Some(signal_info) = &mut access_info.1 { - signal_info.1 += 1; - } else { - access_info.0 += 1; - } - if let Result::Ok(index_type) = index_response { - if index_type.is_template() || index_type.dim() > 0 { - add_report( - ReportCode::InvalidArraySize, - index.get_meta(), - &mut analysis_information.reports, - ); + let index_response = type_expression(&index, program_archive, analysis_information); + + if access_info.2.is_some(){ + add_report( + ReportCode::InvalidArrayAccess(0, 1), + index.get_meta(), + &mut analysis_information.reports, + ); + } else{ + if let Option::Some(signal_info) = &mut access_info.1 { + signal_info.1 += 1; + } else { + access_info.0 += 1; + } + if let Result::Ok(index_type) = index_response { + if index_type.is_template() { + add_report( + ReportCode::InvalidArraySizeT, + index.get_meta(), + &mut analysis_information.reports, + ); + } + else if index_type.dim() > 0 { + add_report( + ReportCode::InvalidArraySize(index_type.dim()), + index.get_meta(), + &mut analysis_information.reports, + ); + } } } } ComponentAccess(name) => { - if let Option::Some(signal_info) = & access_info.1 { - let accessed_comp = analysis_information.environment.get_component(var).unwrap().0.as_ref().unwrap(); - let comp_info = program_archive.get_template_data(accessed_comp); - let comp_outputs = comp_info.get_outputs(); - let comp_inputs = comp_info.get_inputs(); - if signal_info.1 > 0 { + if let Option::Some(_signal_info) = & access_info.1 { + if access_info.2.is_none(){ + access_info.2 = Some(name.clone()) + } else{ add_report( - ReportCode::InvalidArraySize, + ReportCode::InvalidSignalTagAccess, meta, &mut analysis_information.reports, ); } - else if comp_inputs.contains_key(&signal_info.0) { - successful = add_report_and_end( - ReportCode::InvalidSignalTagAccess, //We can report more exactly input signals cannot be accessed. - meta, - &mut analysis_information.reports, - ); - } else if comp_outputs.contains_key(&signal_info.0) { - let output_info = &comp_outputs.get(&signal_info.0).unwrap().1; - if !output_info.contains(name) || access_info.2.is_some() { - successful = add_report_and_end( - ReportCode::InvalidSignalTagAccess, - meta, - &mut analysis_information.reports, - ); - } else { - access_info.2 = Option::Some(name.clone()); - } - } - else { - successful = add_report_and_end( - ReportCode::InvalidSignalTagAccess, - meta, - &mut analysis_information.reports, - ); - } } else { access_info.1 = Option::Some((name.clone(), 0)); } } } } - successful?; Result::Ok(access_info) } + enum SymbolInformation { Component(Option), Var(ArithmeticType), @@ -746,45 +822,66 @@ fn apply_access_to_symbol( }; if access_information.0 > current_dim { - return add_report_and_end(ReportCode::InvalidArrayAccess, meta, reports); + return add_report_and_end(ReportCode::InvalidArrayAccess(current_dim, access_information.0), meta, reports); } else { current_dim -= access_information.0 } - if access_information.0 == 0 && environment.has_component(symbol) && access_information.1.is_some() && access_information.2.is_some() { - Result::Ok(SymbolInformation::Tag) - } - else if access_information.1.is_some() && environment.has_signal(symbol){ - if access_information.0 == 0 && contains_the_tag(access_information.1.clone(), &possible_tags) - { - Result::Ok(SymbolInformation::Tag) - } - else { - if access_information.0 == 0 { - add_report_and_end(ReportCode::InvalidTagAccess, meta, reports) + // Case signals or tags + if let Option::Some((signal_name, dims_accessed)) = access_information.1{ + if current_template.is_some(){ // we are inside component + + if current_dim != 0{ // only allowed complete accesses to component + return add_report_and_end(ReportCode::InvalidPartialArray, meta, reports); } - else { - add_report_and_end(ReportCode::InvalidTagAccessAfterArray, meta, reports) + + let template_name = current_template.unwrap(); + let input = program_archive.get_template_data(&template_name).get_input_info(&signal_name); + let output = program_archive.get_template_data(&template_name).get_output_info(&signal_name); + let tags; + (current_dim, tags) = match (input, output) { + (Option::Some((d, tags)), _) | (_, Option::Some((d, tags))) => (*d, tags), + _ => { + return add_report_and_end(ReportCode::InvalidSignalAccess, meta, reports); + } + }; + if access_information.2.is_some(){ // tag of io signal of component + if dims_accessed > 0{ + return add_report_and_end(ReportCode::InvalidTagAccessAfterArray, meta, reports); + } + else if !tags.contains(&access_information.2.unwrap()){ + return add_report_and_end(ReportCode::InvalidTagAccess, meta, reports); + } else{ + return Result::Ok(SymbolInformation::Tag); + } + } else{ // io signal of component + if dims_accessed > current_dim { + return add_report_and_end(ReportCode::InvalidArrayAccess(current_dim, dims_accessed), meta, reports); + } else { + return Result::Ok(SymbolInformation::Signal(current_dim - dims_accessed)); + } + } + } else{ // we are in template + if environment.has_signal(symbol){ + if access_information.0 != 0{ + add_report_and_end(ReportCode::InvalidTagAccessAfterArray, meta, reports) + } else if dims_accessed > 0{ + add_report_and_end( + ReportCode::InvalidArrayAccess(0, dims_accessed), + meta, + reports, + ) + } else if !possible_tags.contains(&signal_name){ + add_report_and_end(ReportCode::InvalidTagAccess, meta, reports) + } else{ + Result::Ok(SymbolInformation::Tag) + } + + } else if environment.has_component(symbol){ + add_report_and_end(ReportCode::UninitializedComponent, meta, reports) + } else{ + add_report_and_end(ReportCode::InvalidSignalTagAccess, meta, reports) } - } - } - else if access_information.1.is_some() && (current_dim > 0 || current_template.is_none()) { - add_report_and_end(ReportCode::InvalidSignalAccess, meta, reports) - } else if let Option::Some((signal_name, dims_accessed)) = access_information.1 { - let template_name = current_template.unwrap(); - let input = program_archive.get_template_data(&template_name).get_input_info(&signal_name); - let output = - program_archive.get_template_data(&template_name).get_output_info(&signal_name); - current_dim = match (input, output) { - (Option::Some((d, _)), _) | (_, Option::Some((d, _))) => *d, - _ => { - return add_report_and_end(ReportCode::InvalidSignalAccess, meta, reports); - } - }; - if dims_accessed > current_dim { - add_report_and_end(ReportCode::InvalidArrayAccess, meta, reports) - } else { - Result::Ok(SymbolInformation::Signal(current_dim - dims_accessed)) } } else if environment.has_variable(symbol) { Result::Ok(SymbolInformation::Var(current_dim)) @@ -797,12 +894,6 @@ fn apply_access_to_symbol( } } -fn contains_the_tag(access_information: Option<(String, usize)>, tags: &Vec) -> bool { - if let Option::Some(access) = access_information { - tags.contains(&access.0) - } - else {false} -} fn type_array_of_expressions( expressions: &[Expression], @@ -912,36 +1003,63 @@ fn add_report(error_code: ReportCode, meta: &Meta, reports: &mut ReportCollectio let mut report = Report::error("Typing error found".to_string(), error_code); let location = generate_file_location(meta.start, meta.end); let message = match error_code { - TypeCantBeUseAsCondition => "This type can not be used as a condition".to_string(), - BadArrayAccess => "This type can not be used as index".to_string(), + //TypeCantBeUseAsCondition => "This type can not be used as a condition".to_string(), + //BadArrayAccess => "This type can not be used as index".to_string(), EmptyArrayInlineDeclaration => "Empty arrays can not be declared inline".to_string(), - NonHomogeneousArray => "All the elements in a array must have the same type".to_string(), - InvalidArraySize => { - "Array indexes and lengths must be single arithmetic expressions".to_string() + NonHomogeneousArray(dim_1, dim_2) => + format!("All the elements in a array must have the same type.\n Found elements in the array with {} and {} dimensions.", + dim_1, dim_2), + InvalidArraySize(dim) => { + format!("Array indexes and lengths must be single arithmetic expressions.\n Found expression with {} dimensions.", + dim) } - InvalidArrayAccess => { - "Array access does not match the dimensions of the expression".to_string() + InvalidArraySizeT =>{ + "Array indexes and lengths must be single arithmetic expressions.\n Found component instead of expression.".to_string() } - InvalidSignalAccess => "Signal not found in component".to_string(), - InvalidSignalTagAccess => "The latest access cannot be done from component".to_string(), - InvalidTagAccess => "Tag not found in signal".to_string(), - InvalidTagAccessAfterArray => "Tag cannot be found after an array access".to_string(), + InvalidArrayAccess(expected, given) => { + format!("Array access does not match the dimensions of the expression. \n Expected {} dimensions, given {}.", + expected, given + ) + } + InvalidSignalAccess => "Signal not found in component: only accesses to input/output signals are allowed".to_string(), + InvalidSignalTagAccess => "Invalid tag access: could not find the tag".to_string(), + InvalidTagAccess => "Tag not found in signal: only accesses to tags that appear in the definition of the signal are allowed".to_string(), + InvalidTagAccessAfterArray => "Invalid access to the tag of an array element: tags belong to complete arrays, not to individual positions.\n Hint: instead of signal[pos].tag use signal.tag".to_string(), InvalidArrayType => "Components can not be declared inside inline arrays".to_string(), InfixOperatorWithWrongTypes | PrefixOperatorWithWrongTypes => { "Type not allowed by the operator".to_string() } ParallelOperatorWithWrongTypes => { - "Type not allowed by the operator parallel (needs a template)".to_string() + "Type not allowed by the operator parallel (parallel operator can only be applied to templates)".to_string() } InvalidPartialArray => "Only variable arrays can be accessed partially".to_string(), UninitializedSymbolInExpression => "The type of this symbol is not known".to_string(), - WrongTypesInAssignOperation => "Assignee and assigned types do not match".to_string(), + WrongTypesInAssignOperationOperatorSignal => { + format!("The operator does not match the types of the assigned elements.\n Assignments to signals do not allow the operator =, try using <== or <-- instead") + } + WrongTypesInAssignOperationOperatorNoSignal => { + format!("The operator does not match the types of the assigned elements.\n Only assignments to signals allow the operators <== and <--, try using = instead") + } + WrongTypesInAssignOperationArrayTemplates => "Assignee and assigned types do not match.\n All componentes of an array must be instances of the same template.".to_string(), + WrongTypesInAssignOperationTemplate => "Assignee and assigned types do not match.\n Expected template found expression.".to_string(), + WrongTypesInAssignOperationExpression => "Assignee and assigned types do not match.\n Expected expression found template.".to_string(), + WrongTypesInAssignOperationDims(expected, found) => { + format!("Assignee and assigned types do not match. \n Expected dimensions: {}, found {}", + expected, found) + } InvalidArgumentInCall => "Components can not be passed as arguments".to_string(), UnableToTypeFunction => "Unable to infer the type of this function".to_string(), - MustBeSingleArithmetic => "Must be a single arithmetic expression".to_string(), - MustBeArithmetic => "Must be a single arithmetic expression or an array".to_string(), + MustBeSingleArithmetic(dim) => { + format!("Must be a single arithmetic expression.\n Found expression of {} dimensions", dim) + } + MustBeSingleArithmeticT => { + format!("Must be a single arithmetic expression.\n Found component") + } + MustBeArithmetic => "Must be a single arithmetic expression or an array of arithmetic expressions. \n Found component".to_string(), OutputTagCannotBeModifiedOutside => "Output tag from a subcomponent cannot be modified".to_string(), - MustBeSameDimension => "Must be two arrays of the same dimensions".to_string(), + MustBeSameDimension(dim_1, dim_2) =>{ + format!("Must be two arrays of the same dimensions.\n Found {} and {} dimensions", dim_1, dim_2) + } MainComponentWithTags => "Main component cannot have inputs with tags".to_string(), ExpectedDimDiffGotDim(expected, got) => { format!("Function should return {} but returns {}", expected, got) @@ -949,6 +1067,7 @@ fn add_report(error_code: ReportCode, meta: &Meta, reports: &mut ReportCollectio WrongNumberOfArguments(expected, got) => { format!("Expecting {} arguments, {} where obtained", expected, got) } + UninitializedComponent => "Trying to access to a signal of a component that has not been initialized".to_string(), _ => panic!("Unimplemented error code"), }; report.add_primary(location, file_id, message); diff --git a/type_analysis/src/analyzers/unknown_known_analysis.rs b/type_analysis/src/analyzers/unknown_known_analysis.rs index 01c4cb582..5cb5541f2 100644 --- a/type_analysis/src/analyzers/unknown_known_analysis.rs +++ b/type_analysis/src/analyzers/unknown_known_analysis.rs @@ -17,6 +17,7 @@ struct ExitInformation { environment: Environment, constraints_declared: bool, tags_modified: bool, + signals_declared: bool, modified_variables: HashSet, } @@ -59,22 +60,24 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma mut reports: ReportCollection, mut environment: Environment, file_id: FileID, - ) -> (bool, bool, ReportCollection, Environment, HashSet) { + ) -> (bool, bool, bool, ReportCollection, Environment, HashSet) { let mut constraints_declared = false; let mut tags_modified = false; + let mut signals_declared = false; let mut modified_variables: HashSet = HashSet::new(); for stmt in stmts { let entry = EntryInformation { file_id, environment }; let exit = analyze(stmt, entry); constraints_declared = constraints_declared || exit.constraints_declared; tags_modified = tags_modified || exit.tags_modified; + signals_declared = signals_declared || exit.signals_declared; modified_variables.extend(exit.modified_variables); for report in exit.reports { reports.push(report); } environment = exit.environment; } - (constraints_declared, tags_modified, reports, environment, modified_variables) + (constraints_declared, tags_modified, signals_declared, reports, environment, modified_variables) } let file_id = entry_information.file_id; let mut reports = ReportCollection::new(); @@ -82,14 +85,18 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma let mut modified_variables = HashSet::new(); let mut constraints_declared = false; let mut tags_modified = false; + let mut signals_declared = false; match stmt { Declaration { xtype, name, dimensions, .. } => { if let VariableType::Signal(..) = xtype { environment.add_intermediate(name, Unknown); + signals_declared = true; } else if let VariableType::Component = xtype { environment.add_component(name, Unknown); + signals_declared = true; } else if let VariableType::AnonymousComponent = xtype { environment.add_component(name, Unknown); + signals_declared = true; } else { environment.add_variable(name, Unknown); modified_variables.insert(name.clone()); @@ -184,12 +191,14 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma environment: new_entry_else_case.environment, reports: ReportCollection::with_capacity(0), modified_variables : HashSet::new(), - tags_modified : false + tags_modified : false, + signals_declared: false, } }; constraints_declared = else_case_info.constraints_declared || if_case_info.constraints_declared; tags_modified = else_case_info.tags_modified || if_case_info.tags_modified; + signals_declared = else_case_info.signals_declared || if_case_info.signals_declared; modified_variables.extend(if_case_info.modified_variables); modified_variables.extend(else_case_info.modified_variables); for report in if_case_info.reports { @@ -227,6 +236,14 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma &mut reports, ); } + if tag_cond == Unknown && signals_declared { + add_report( + ReportCode::UnreachableSignals, + cond.get_meta(), + file_id, + &mut reports, + ); + } } While { cond, stmt, .. } => { let mut entry_info = environment.clone(); @@ -244,6 +261,7 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma constraints_declared = exit.constraints_declared; tags_modified = exit.tags_modified; + signals_declared = exit.signals_declared; for report in exit.reports { reports.push(report); } @@ -274,28 +292,40 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma &mut reports, ); } + if tag_out == Unknown && signals_declared { + add_report( + ReportCode::UnreachableSignals, + cond.get_meta(), + file_id, + &mut reports, + ); + } } Block { stmts, .. } => { environment.add_variable_block(); - let (nc, tags, nr, ne, nm) = iterate_statements(stmts, reports, environment, file_id); + let (nc, tags, ns, nr, ne, nm) = iterate_statements(stmts, reports, environment, file_id); constraints_declared = nc; reports = nr; environment = ne; modified_variables = nm; environment.remove_variable_block(); tags_modified = tags; + signals_declared = ns; } InitializationBlock { initializations, .. } => { - let (nc, tags, nr, ne, nm) = iterate_statements(initializations, reports, environment, file_id); + let (nc, tags, ns, nr, ne, nm) = iterate_statements(initializations, reports, environment, file_id); constraints_declared = nc; reports = nr; environment = ne; modified_variables = nm; tags_modified = tags; + signals_declared = ns; } _ => {} } - ExitInformation { reports, environment, constraints_declared, modified_variables, tags_modified } + ExitInformation { + reports, environment, constraints_declared, modified_variables, tags_modified, signals_declared + } } fn tag(expression: &Expression, environment: &Environment) -> Tag { @@ -509,6 +539,7 @@ fn add_report( NonQuadratic => "Non-quadratic constraint was detected statically, using unknown index will cause the constraint to be non-quadratic".to_string(), UnreachableConstraints => "There are constraints depending on the value of the condition and it can be unknown during the constraint generation phase".to_string(), UnreachableTags => "There are tag assignments depending on the value of the condition and it can be unknown during the constraint generation phase".to_string(), + UnreachableSignals => "There are signal or component declarations depending on the value of the condition and it can be unknown during the constraint generation phase".to_string(), _ => panic!("Unimplemented error code") }; report.add_primary(location, file_id, message); From 30e14cd2431b8442e32e4ea05014a997769e63ff Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 7 Mar 2023 17:44:20 +0100 Subject: [PATCH 33/70] Update RELEASES.md --- RELEASES.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 45fdf5d89..aa3a65d08 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,4 +1,22 @@ # Release notes +## March 7, 2023 circom 2.1.5 + +#### Extensions +- Definition of signals and component can be done now inside if blocks IF the condition is known at compilation time. If the condition is unknown and depends on the value of signals, then the compiler throws an error. +- Improving the --inspect option. It now detects underconstrained signals. +- Improving the efficiency of the compiler. It does not execute the inspect and constraint generation phase only if there are not the corresponding flags. +- Improving: syntax sugar remover, underscored substitution and tags propagation intermediate signals. +- Improving --O1 simplification: removing signals that do not appear in any constraint and avoiding unnecessary constraint normalizations. +- Improving parallel: array assignments of outputs and efficiency by updating numThread while waiting and setting maxThreads to 32. +- Handling better the parser errors and improving error messages to output more information. (parser, type checking and constraint generation errors). +- Showing warnings where errors are found. +- Reducing writing time for output files. +- Updating the documentation. + +#### Fixed Bugs +- Fixing a problem with the memory release of the components. +- Fixing a problem with the parallel execution during the witness generation (in C). +- Fixing a bug: functions must be always executed even if they outputs constants. ## February 10, 2023 circom 2.1.4 From 9e6647e3e8d346229cf93502a9844c7a09497d73 Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 7 Mar 2023 18:03:11 +0100 Subject: [PATCH 34/70] Update signals.md --- mkdocs/docs/circom-language/signals.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/mkdocs/docs/circom-language/signals.md b/mkdocs/docs/circom-language/signals.md index 000fd1aea..b6ab5fb04 100644 --- a/mkdocs/docs/circom-language/signals.md +++ b/mkdocs/docs/circom-language/signals.md @@ -99,7 +99,7 @@ pragma circom 2.0.0; template A(){ signal input in; signal output outA; - var i = 0; var out; + var i = 0; var out = 0; while (i < in){ out++; i++; } @@ -122,5 +122,21 @@ Signals can only be assigned using the operations `<--` or `<==` (see [Basic ope out[k] <-- (in >> k) & 1; ``` +Since circom 2.1.5, signals and components can be now declared inside `if` blocks, but only if the condition is known at compilation time. +```text +pragma circom 2.1.5; +template A(n){ + signal input in; + signal output outA; + var i = 0; + if(i < n){ + signal out <== 2; + i = out; + } + outA <== i; +} +component main = A(5); +``` +In the previous example, the condition `i < n` is known at compilation time, and then the declaration of signal `out` is allowed. However, if the condition where `in < n`, it is not known at compilation time and we outputs an error, because the declaration in this case is not allowed. From f4be4fe315a527b34aaf015b5640eb89c4a071b1 Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 7 Mar 2023 18:06:21 +0100 Subject: [PATCH 35/70] Update include.md --- mkdocs/docs/circom-language/include.md | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs/docs/circom-language/include.md b/mkdocs/docs/circom-language/include.md index c6e9c0edb..80187b084 100644 --- a/mkdocs/docs/circom-language/include.md +++ b/mkdocs/docs/circom-language/include.md @@ -10,3 +10,4 @@ include "babyjub.circom"; This piece of code includes the files `montgomery.circom`, `mux3.circom` and `babyjub.circom` from the circom library. +Since circom 2.0.8, option `-l` is available to indicate the paths where searching the files to be included. From 8dc5bd333b179efc412edc7eb861a6ef1846e9de Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 7 Mar 2023 18:10:02 +0100 Subject: [PATCH 36/70] Update reserved-keywords.md --- mkdocs/docs/circom-language/reserved-keywords.md | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs/docs/circom-language/reserved-keywords.md b/mkdocs/docs/circom-language/reserved-keywords.md index 3b813db66..a7b31a25b 100644 --- a/mkdocs/docs/circom-language/reserved-keywords.md +++ b/mkdocs/docs/circom-language/reserved-keywords.md @@ -19,6 +19,7 @@ The list of reserved keywords is the following: * **log:** Print the result of the evaluation. * **assert:** Check the condition at construction time. * **include:** Include code of the indicated file. +* ** parallel:** To generate C code with the parallel component or template. * **pragma circom**: Instruction to check the compiler version. * **pragma custom_templates**: Instruction to indicate the usage of custom templates. From 9510006d6d683025e41d7d2fb15398f2e4c177cc Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 7 Mar 2023 18:10:46 +0100 Subject: [PATCH 37/70] Update signals.md --- mkdocs/docs/circom-language/signals.md | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/mkdocs/docs/circom-language/signals.md b/mkdocs/docs/circom-language/signals.md index b6ab5fb04..38d1b1b0c 100644 --- a/mkdocs/docs/circom-language/signals.md +++ b/mkdocs/docs/circom-language/signals.md @@ -121,22 +121,3 @@ Signals can only be assigned using the operations `<--` or `<==` (see [Basic ope ```text out[k] <-- (in >> k) & 1; ``` - -Since circom 2.1.5, signals and components can be now declared inside `if` blocks, but only if the condition is known at compilation time. -```text -pragma circom 2.1.5; -template A(n){ - signal input in; - signal output outA; - var i = 0; - if(i < n){ - signal out <== 2; - i = out; - } - outA <== i; -} -component main = A(5); -``` - -In the previous example, the condition `i < n` is known at compilation time, and then the declaration of signal `out` is allowed. However, if the condition where `in < n`, it is not known at compilation time and we outputs an error, because the declaration in this case is not allowed. - From d39713620d68a3fbeab6fe21ce45adf3a7c24920 Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 7 Mar 2023 18:11:16 +0100 Subject: [PATCH 38/70] Update scoping.md --- mkdocs/docs/circom-language/scoping.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/mkdocs/docs/circom-language/scoping.md b/mkdocs/docs/circom-language/scoping.md index d3c343399..5ab948320 100644 --- a/mkdocs/docs/circom-language/scoping.md +++ b/mkdocs/docs/circom-language/scoping.md @@ -24,6 +24,26 @@ component main = Multiplier2(5); Signal `out2` must be declared at the top-level block. The next compilation error is produced: _"`out2` is outside the initial scope"_. +Since circom 2.1.5, signals and components can be now declared inside `if` blocks, but only if the condition is known at compilation time. +```text +pragma circom 2.1.5; +template A(n){ + signal input in; + signal output outA; + var i = 0; + if(i < n){ + signal out <== 2; + i = out; + } + outA <== i; +} +component main = A(5); +``` + +In the previous example, the condition `i < n` is known at compilation time, and then the declaration of signal `out` is allowed. However, if the condition where `in < n`, it is not known at compilation time and we outputs an error, because the declaration in this case is not allowed. + + + Regarding visibility, a signal x of component c is also visible in the template t that has declared c, using the notation c.x. No access to signals of nested sub-components is allowed. For instance, if c is built using another component d, the signals of d cannot be accessed from t. This can be seen in the next code: ```text From 52f5e92925d3bb55ccbcfdf5b9a941603393d6ca Mon Sep 17 00:00:00 2001 From: miguelis Date: Wed, 8 Mar 2023 12:22:43 +0100 Subject: [PATCH 39/70] Update RELEASES.md --- RELEASES.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index aa3a65d08..b55456bd7 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -2,21 +2,21 @@ ## March 7, 2023 circom 2.1.5 #### Extensions -- Definition of signals and component can be done now inside if blocks IF the condition is known at compilation time. If the condition is unknown and depends on the value of signals, then the compiler throws an error. +- Definition of signals and components can be done now inside if blocks IF the condition is known at compilation time. If the condition is unknown and depends on the value of signals, then the compiler throws an error. - Improving the --inspect option. It now detects underconstrained signals. - Improving the efficiency of the compiler. It does not execute the inspect and constraint generation phase only if there are not the corresponding flags. -- Improving: syntax sugar remover, underscored substitution and tags propagation intermediate signals. - Improving --O1 simplification: removing signals that do not appear in any constraint and avoiding unnecessary constraint normalizations. - Improving parallel: array assignments of outputs and efficiency by updating numThread while waiting and setting maxThreads to 32. - Handling better the parser errors and improving error messages to output more information. (parser, type checking and constraint generation errors). -- Showing warnings where errors are found. +- Showing warnings when errors are found. - Reducing writing time for output files. - Updating the documentation. #### Fixed Bugs -- Fixing a problem with the memory release of the components. +- Fixing a problem with the memory release of the components (in C). - Fixing a problem with the parallel execution during the witness generation (in C). -- Fixing a bug: functions must be always executed even if they outputs constants. +- Fixing a bug: functions must be always executed even if they output constants. +- Fixing a bug: During the witness generation, the computation of expressions like x**x was buggy (in wasm). ## February 10, 2023 circom 2.1.4 From 26ba68554f7e9d6b65c4e47161a49d1a40dea29e Mon Sep 17 00:00:00 2001 From: miguelis Date: Wed, 8 Mar 2023 12:24:38 +0100 Subject: [PATCH 40/70] Update scoping.md --- mkdocs/docs/circom-language/scoping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs/docs/circom-language/scoping.md b/mkdocs/docs/circom-language/scoping.md index 5ab948320..cd956f716 100644 --- a/mkdocs/docs/circom-language/scoping.md +++ b/mkdocs/docs/circom-language/scoping.md @@ -40,7 +40,7 @@ template A(n){ component main = A(5); ``` -In the previous example, the condition `i < n` is known at compilation time, and then the declaration of signal `out` is allowed. However, if the condition where `in < n`, it is not known at compilation time and we outputs an error, because the declaration in this case is not allowed. +In the previous example, the condition `i < n` is known at compilation time, and then the declaration of signal `out` is allowed. However, if the condition where `in < n`, it is not known at compilation time and we output an error, because the declaration in this case is not allowed. From e2b48a25877d38849ce015ec9e62d74fe3153e70 Mon Sep 17 00:00:00 2001 From: miguelis Date: Wed, 8 Mar 2023 20:41:57 +0100 Subject: [PATCH 41/70] Adding documentation for the --inspect option. --- .../circom-language/code-quality/inspect.md | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 mkdocs/docs/circom-language/code-quality/inspect.md diff --git a/mkdocs/docs/circom-language/code-quality/inspect.md b/mkdocs/docs/circom-language/code-quality/inspect.md new file mode 100644 index 000000000..c3113d69f --- /dev/null +++ b/mkdocs/docs/circom-language/code-quality/inspect.md @@ -0,0 +1,82 @@ +Improving security of circuits by using --inspect option + +When using --inspect option, the compiler searches signals that do not appear in constraints. In case, it finds one, then it throws a warning to let the programmer know those unconstrained signals. To avoid such a warning, the compiler could use the underscore notation '_' to inform the compiler that such a situation is expected. Let us see several cases where we can find that situation. + +- The compiler throws a warning if a signal defined in a template do not appear in any constraint of such template. + +``` +template B(n){ + signal input in; + signal input out; + out <== in + 1; +} +template A(n){ + signal aux; + Signal out; + if(n == 2){ + aux <== 2; + Out <== B()(aux); + } + else{ + out <== 5; + } +} + +component main = A(3); +``` + +In this example, `aux` is only used in the if branch. Thus, for the main component, `aux` remains unconstrained and the compiler throws a warning. + +```warning[CA01]: In template "A(3)": Local signal aux does not appear in any constraint``` + + To avoid the warning, we can add inside the `else` branch, the instruction `_ <== aux` to indicate the compiler that aux is not used in this case. + +- Another case when throwing a warning is using subcomponents inside a template: input and output signals of each subcomponent in a template should appear at least in one constraint of the template. +However, it is very common the use of subcomponents to check some property but ignoring the output of the subcomponent. + +To illustrate this, let us consider the well-known template `Num2Bits(n)` from the circomlib. This template receives an input signal and a parameter `n` which represents a number of bits and returns and output signal array with n elements, the binary representation of the input. + +``` +include "bitify.circom"; + +template check_bits(n){ + signal input in; + component check = Num2Bits(n); + check.in <== in; +} + +component main = check_bits(10); +``` + +The main component checks if the value of `in` can be represented using 10 bits, and it works as expected. The constraints introduced by the subcomponent check will guarantee that the R1CS system only has a solution if `in` can be represented with 10 bits, but it does not matter which is the specific representation. However, the compiler throws the next warning: + +```In template "check_bits(10)": Array of subcomponent input/output signals check.out contains a total of 10 signals that do not appear in any constraint of the father component + = For example: check.out[0], check.out[1].``` + +Since we are not interested in the binary representation, the template does not make use of array signal `check.out`. Thus, we should add `_ <== check.out` to indicate the compiler that the binary representation is irrelevant and avoid the warning. + +Notice also here that the --inspect option also shows the parameter of the instance that causes a warning (check_bits(10)). In general, we throw as many warnings as instances with different parameters for each template. + +- In the previous example, we have seen that none of the positions of array `check.out` are used, and the warning indicates some of the unused positions. Thus, if some of the positions are used, and others are not, the compiler also notifies some of the unused positions. + +``` +include "bitify.circom"; + +template parity(n){ + signal input in; + signal output out; + component check = Num2Bits(n); + check.in <== in; + out <== check.out[0]; +} + +component main = parity(10); +``` + +In this case, we are again using a component `Num2Bits(10)` to get the binary representation of signal `in`, but we are only interested in the least-significant bit to know its parity. Then, the warning throws the next warning: +``` +In template "parity(10)": Array of subcomponent input/output signals check.out contains a total of 9 signals that do not appear in any constraint of the father component + = For example: check.out[1], check.out[2].``` + +To fix this example, we can add ` _ <== check.out` at the end of the template to let the compiler know that the remaining positions are irrelevant. + From af6606c90d2101f12ba1ac535e3578c1edb176f5 Mon Sep 17 00:00:00 2001 From: miguelis Date: Wed, 8 Mar 2023 20:56:35 +0100 Subject: [PATCH 42/70] Update inspect.md --- .../circom-language/code-quality/inspect.md | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/mkdocs/docs/circom-language/code-quality/inspect.md b/mkdocs/docs/circom-language/code-quality/inspect.md index c3113d69f..30ec7bbf1 100644 --- a/mkdocs/docs/circom-language/code-quality/inspect.md +++ b/mkdocs/docs/circom-language/code-quality/inspect.md @@ -1,6 +1,10 @@ -Improving security of circuits by using --inspect option +--- +description: >- + Here you can find information about the --inspect option and how to solve the warnings. +--- +# Improving security of circuits by using --inspect option -When using --inspect option, the compiler searches signals that do not appear in constraints. In case, it finds one, then it throws a warning to let the programmer know those unconstrained signals. To avoid such a warning, the compiler could use the underscore notation '_' to inform the compiler that such a situation is expected. Let us see several cases where we can find that situation. +When using --inspect option, the compiler searches for signals that do not appear in any constraint. In case it finds some, then it throws a warning to let the programmer know those unconstrained signals. To avoid such a warning, the compiler could use the underscore notation '_' to inform the compiler that such a situation is expected. Let us see several cases where we can find that situation. - The compiler throws a warning if a signal defined in a template do not appear in any constraint of such template. @@ -12,10 +16,10 @@ template B(n){ } template A(n){ signal aux; - Signal out; + signal out; if(n == 2){ aux <== 2; - Out <== B()(aux); + out <== B()(aux); } else{ out <== 5; @@ -25,13 +29,13 @@ template A(n){ component main = A(3); ``` -In this example, `aux` is only used in the if branch. Thus, for the main component, `aux` remains unconstrained and the compiler throws a warning. +In this example, `aux` is only used in the `if` branch. Thus, for the main component (with `n = 3`) , `aux` remains unconstrained and the compiler throws a warning: ```warning[CA01]: In template "A(3)": Local signal aux does not appear in any constraint``` - To avoid the warning, we can add inside the `else` branch, the instruction `_ <== aux` to indicate the compiler that aux is not used in this case. + To avoid the warning, we can add inside the `else` branch, the instruction `_ <== aux;` to indicate the compiler that aux is not used in this case. -- Another case when throwing a warning is using subcomponents inside a template: input and output signals of each subcomponent in a template should appear at least in one constraint of the template. +- Another case when throwing a warning is using subcomponents inside a template: input and output signals of each subcomponent in a template should appear at least in one constraint of the father component. However, it is very common the use of subcomponents to check some property but ignoring the output of the subcomponent. To illustrate this, let us consider the well-known template `Num2Bits(n)` from the circomlib. This template receives an input signal and a parameter `n` which represents a number of bits and returns and output signal array with n elements, the binary representation of the input. @@ -41,21 +45,23 @@ include "bitify.circom"; template check_bits(n){ signal input in; - component check = Num2Bits(n); - check.in <== in; + component check = Num2Bits(n); + check.in <== in; } component main = check_bits(10); ``` -The main component checks if the value of `in` can be represented using 10 bits, and it works as expected. The constraints introduced by the subcomponent check will guarantee that the R1CS system only has a solution if `in` can be represented with 10 bits, but it does not matter which is the specific representation. However, the compiler throws the next warning: - -```In template "check_bits(10)": Array of subcomponent input/output signals check.out contains a total of 10 signals that do not appear in any constraint of the father component - = For example: check.out[0], check.out[1].``` +It is very common to use the `Num2Bits` template to check if `in` can be represented with `n`bits. In this case, the main component checks if the value of `in` can be represented using 10 bits, and it works as expected. The constraints introduced by the subcomponent check will guarantee that the R1CS system only has a solution if `in` can be represented with 10 bits, but it does not matter which is the specific representation. However, the compiler throws the next warning: +``` +In template "check_bits(10)": Array of subcomponent input/output signals check.out contains a total +of 10 signals that do not appear in any constraint of the father component = For example: check.out[0], check.out[1]. +``` + -Since we are not interested in the binary representation, the template does not make use of array signal `check.out`. Thus, we should add `_ <== check.out` to indicate the compiler that the binary representation is irrelevant and avoid the warning. +Since we are not interested in the binary representation, the template does not make use of array signal `check.out`. Thus, we should add `_ <== check.out` to inform that the binary representation is irrelevant and avoid the warning. -Notice also here that the --inspect option also shows the parameter of the instance that causes a warning (check_bits(10)). In general, we throw as many warnings as instances with different parameters for each template. +Notice also here that the `--inspect option also shows the parameter of the instance that causes a warning (`check_bits(10)`). In general, we throw as many warnings as instances with different parameters for each template. - In the previous example, we have seen that none of the positions of array `check.out` are used, and the warning indicates some of the unused positions. Thus, if some of the positions are used, and others are not, the compiler also notifies some of the unused positions. @@ -64,10 +70,10 @@ include "bitify.circom"; template parity(n){ signal input in; - signal output out; - component check = Num2Bits(n); - check.in <== in; - out <== check.out[0]; + signal output out; + component check = Num2Bits(n); + check.in <== in; + out <== check.out[0]; } component main = parity(10); @@ -75,8 +81,9 @@ component main = parity(10); In this case, we are again using a component `Num2Bits(10)` to get the binary representation of signal `in`, but we are only interested in the least-significant bit to know its parity. Then, the warning throws the next warning: ``` -In template "parity(10)": Array of subcomponent input/output signals check.out contains a total of 9 signals that do not appear in any constraint of the father component - = For example: check.out[1], check.out[2].``` +In template "parity(10)": Array of subcomponent input/output signals check.out contains a total of 9 signals +that do not appear in any constraint of the father component. = For example: check.out[1], check.out[2]. + ``` To fix this example, we can add ` _ <== check.out` at the end of the template to let the compiler know that the remaining positions are irrelevant. From dc0f9bc7c1ac71f02902ce5c13c2b6cd6e53da35 Mon Sep 17 00:00:00 2001 From: miguelis Date: Wed, 8 Mar 2023 20:58:49 +0100 Subject: [PATCH 43/70] Update compilation-options.md --- mkdocs/docs/getting-started/compilation-options.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs/docs/getting-started/compilation-options.md b/mkdocs/docs/getting-started/compilation-options.md index 641edab53..659d4db3c 100644 --- a/mkdocs/docs/getting-started/compilation-options.md +++ b/mkdocs/docs/getting-started/compilation-options.md @@ -46,7 +46,7 @@ In the following, we explain these options. #####Flags and options related to the constraint generation process * Flag ```--verbose``` shows logs with known values at compilation time during the constraint generation process. -* Flag ```--inspect``` does an additional check over the R1CS system produced. +* Flag ```--inspect``` does an additional check over the R1CS system produced. (see [--inspect](../circom-language/code-quality/inspect)). * Flag ```--use_old_simplification_heuristics``` allows to use an old heuristics of the optimization algorithm. However, it is not recommended since the new heuristics has produced better results in practice. @@ -69,4 +69,4 @@ Only one of these flags/options must be used during the compilation. * Option ```-l ``` adds the provided directory in ``````to the library search path. It is possible to add as much ```-l ``` as needed, but only one directory per option. * Flag ```-v / --version``` prints the version information. -* Flag ```-h / --help``` prints the help information. \ No newline at end of file +* Flag ```-h / --help``` prints the help information. From 62b0ea214b70461b720ce4e4f38aacaaed2f6719 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Thu, 9 Mar 2023 13:40:17 +0100 Subject: [PATCH 44/70] working on x**x treatment --- compiler/src/hir/sugar_cleaner.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/src/hir/sugar_cleaner.rs b/compiler/src/hir/sugar_cleaner.rs index b2aa3f1c1..f760981db 100644 --- a/compiler/src/hir/sugar_cleaner.rs +++ b/compiler/src/hir/sugar_cleaner.rs @@ -315,6 +315,9 @@ fn extend_infix(expr: &mut Expression, state: &mut State, context: &Context) -> *lhe = Box::new(expr_lhe); + } else{ + *rhe = Box::new(expr_rhe); + *lhe = Box::new(expr_lhe); } // just to solve the case of X ** X in wasm From 0ed2276e2219acedf0d9ffdbc4fc8e71b8be739a Mon Sep 17 00:00:00 2001 From: miguelis Date: Thu, 9 Mar 2023 14:53:46 +0100 Subject: [PATCH 45/70] Update inspect.md --- .../docs/circom-language/code-quality/inspect.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/mkdocs/docs/circom-language/code-quality/inspect.md b/mkdocs/docs/circom-language/code-quality/inspect.md index 30ec7bbf1..cf3791da4 100644 --- a/mkdocs/docs/circom-language/code-quality/inspect.md +++ b/mkdocs/docs/circom-language/code-quality/inspect.md @@ -4,7 +4,7 @@ description: >- --- # Improving security of circuits by using --inspect option -When using --inspect option, the compiler searches for signals that do not appear in any constraint. In case it finds some, then it throws a warning to let the programmer know those unconstrained signals. To avoid such a warning, the compiler could use the underscore notation '_' to inform the compiler that such a situation is expected. Let us see several cases where we can find that situation. +When using --inspect option, the compiler searches for signals that do not appear in any constraint. In case it finds some, then it throws a warning to let the programmer know those unconstrained signals. The compiler also throws a warning when some input or output signal of a subcomponent in a template do not appear in any onstraint of the father component. To avoid these warnings, the compiler could use the underscore notation '_' to inform the compiler that such a situation is expected. Let us see several cases where we can find that situation. - The compiler throws a warning if a signal defined in a template do not appear in any constraint of such template. @@ -34,6 +34,19 @@ In this example, `aux` is only used in the `if` branch. Thus, for the main compo ```warning[CA01]: In template "A(3)": Local signal aux does not appear in any constraint``` To avoid the warning, we can add inside the `else` branch, the instruction `_ <== aux;` to indicate the compiler that aux is not used in this case. + Since `circom 2.1.5`, we can also define signals inside `if` blocks with conditions known at compilation time and thus, use this feature to solve the previous warning as follows: + ``` +template A(n){ + signal out; + if(n == 2){ + signal aux <== 2; + out <== B()(aux); + } + else{ + out <== 5; + } +} + ``` - Another case when throwing a warning is using subcomponents inside a template: input and output signals of each subcomponent in a template should appear at least in one constraint of the father component. However, it is very common the use of subcomponents to check some property but ignoring the output of the subcomponent. From 3997a9a105ebddc4e1e0e6ace118fb1e7c62ec7a Mon Sep 17 00:00:00 2001 From: clararod9 Date: Fri, 10 Mar 2023 13:04:37 +0100 Subject: [PATCH 46/70] minor improvement --inspect --- dag/src/constraint_correctness_analysis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dag/src/constraint_correctness_analysis.rs b/dag/src/constraint_correctness_analysis.rs index 811e38992..edb216b4f 100644 --- a/dag/src/constraint_correctness_analysis.rs +++ b/dag/src/constraint_correctness_analysis.rs @@ -160,8 +160,8 @@ pub fn analyse(nodes: &[Node]) -> AnalysisResult { let mut result = AnalysisResult { errors: vec![], warnings: vec![] }; let mut visited : HashSet = HashSet::new(); for node in nodes { - let analysis = visit_node(node); if !node.is_custom_gate() && !visited.contains(&node.template_name.clone()){ + let analysis = visit_node(node); let mut result2 = AnalysisResult { errors: vec![], warnings: vec![] }; analysis_interpretation(analysis, &mut result2); result.errors.append(&mut result2.errors); From a4f65f54f72430dbc32431aa9d3e3427672f86a7 Mon Sep 17 00:00:00 2001 From: Spartucus Date: Mon, 13 Mar 2023 20:09:32 +0800 Subject: [PATCH 47/70] fix typo of doc --- mkdocs/docs/circom-language/signals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs/docs/circom-language/signals.md b/mkdocs/docs/circom-language/signals.md index 000fd1aea..8d3c26621 100644 --- a/mkdocs/docs/circom-language/signals.md +++ b/mkdocs/docs/circom-language/signals.md @@ -116,7 +116,7 @@ component main = B(); This example produces a compilation error since value of signal `outA` depends on the value of signal `in`, even though, such a value is the constant 3. -Signals can only be assigned using the operations `<--` or `<==` (see [Basic operators](../basic-operators)) with the signal on the left hand side and and `-->` or `==>` (see [Basic operators](../basic-operators)) with the signal on the right hand side. The safe options are `<==` and `==>`, since they assign values and also generate constraints at the same time. Using `<--` and `-->` is, in general, dangerous and should only be used when the assigned expression cannot be included in a constraint, like in the following example. +Signals can only be assigned using the operations `<--` or `<==` (see [Basic operators](../basic-operators)) with the signal on the left hand side and `-->` or `==>` (see [Basic operators](../basic-operators)) with the signal on the right hand side. The safe options are `<==` and `==>`, since they assign values and also generate constraints at the same time. Using `<--` and `-->` is, in general, dangerous and should only be used when the assigned expression cannot be included in a constraint, like in the following example. ```text out[k] <-- (in >> k) & 1; From 871a9708752e39d9982701ad5611e7e10d00ec90 Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 14 Mar 2023 12:09:28 +0100 Subject: [PATCH 48/70] Update unknowns.md --- mkdocs/docs/circom-language/circom-insight/unknowns.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mkdocs/docs/circom-language/circom-insight/unknowns.md b/mkdocs/docs/circom-language/circom-insight/unknowns.md index 029b67661..7a0fb0113 100644 --- a/mkdocs/docs/circom-language/circom-insight/unknowns.md +++ b/mkdocs/docs/circom-language/circom-insight/unknowns.md @@ -1,10 +1,10 @@ # Unknowns -As expressions accepted during [constraint generation](../constraint-generation) can at most be quadratic only, certain checks and constraints are imposed on the use of unknown values at compile. +As expressions accepted during [constraint generation](../constraint-generation) can at most be quadratic only, certain checks and conditions are imposed on the use of unknown values at compile time. In circom, **constant values** and **template parameters** are always considered known, while **signals** are always considered unknown. -Expressions depending only on knowns are considered knowns, while those depending on unknowns are considered unknowns. +Expressions depending only on knowns are considered knowns, while those depending on some unknowns are considered unknowns. ```text pragma circom 2.0.0; @@ -64,7 +64,7 @@ In the code above, an array is defined with an unknown size of value `in` (as si ## Control Flow -A constraint generated in a control flow must have a known condition. +If `if-else` or `for-loop`blocks have unknown conditions, then the block is considered unknown and no constraint can be generated inside it. Consequently, constraint can only be generated in a control flow with known conditions. Take an if-then statement as an example: @@ -84,7 +84,7 @@ template A(){ component main = A(); ``` -In the code above, a constraint is defined in an if-then statement with a comparitive condition involving an unknown value `in` (as signals are always considered unknown). +In the code above, a constraint is defined in an if-then statement with a comparative condition involving an unknown value `in` (as signals are always considered unknown). Similarly, using a for-loop as an example: From b589275f0b7d49402f2b5e4ee693b829cdd33c4b Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 14 Mar 2023 12:16:15 +0100 Subject: [PATCH 49/70] Update code-assertion.md --- .../circom-language/code-quality/code-assertion.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mkdocs/docs/circom-language/code-quality/code-assertion.md b/mkdocs/docs/circom-language/code-quality/code-assertion.md index 98fb05c53..4a690cd2d 100644 --- a/mkdocs/docs/circom-language/code-quality/code-assertion.md +++ b/mkdocs/docs/circom-language/code-quality/code-assertion.md @@ -4,7 +4,7 @@ This statement introduces conditions to be checked. Here, we distinguish two cases depending if **bool_expression** is unknown at compilation time: -- If the condition only depends on the value of template parameters or field constants, the assert is evaluated in compilation time. If the result of the evaluation is false, then the compilation fails. Consider the next piece of code: +- If the assert statement depends on a control flow with only known conditions (see [Unknowns](../circom-insight/unknowns)) and the **bool_expression** is known (e.g., if it only depends on the value of template parameters or field constants), the assert is evaluated in compilation time. If the result of the evaluation is false, then the compilation fails. Consider the next piece of code: ``` template A(n) { @@ -18,17 +18,16 @@ component main = A(0); Here, the assert can be evaluated during the compilation and the result of the evaluation is false. Thus, the compilation ends throwing error *error[T3001]: False assert reached*. If the main component was defined as `component main = A(2);`, then the compilation correctly finishes. -Recall that, when a constraint like `in * in === n;` is introduced with `===`, then an assert is automatically added in the witness generation code. In this case, `assert(in * in == n)`. - -- If the condition or the evaluation of the assert involves the value of a signal, the assert cannot be evaluated in compilation time. The compiler produces an assert which must be satisfied during the witness generation. If the input `in` passed as parameter to produce the witness does not satisfy the assert, then the witness will not be generated. +- Otherwise, the compiler adds an assert in the final witness-generation code that must be satisfied during the witness generation. In the next example, if the input `in` passed as parameter to produce the witness does not satisfy the assert, then the witness will not be generated. ```text template Translate(n) { -signal input in; -assert(in<=254); -….. + signal input in; + assert(in<=254); + . . . } ``` +Recall that, when a constraint like `in * in === n;` is introduced with `===`, then an assert is automatically added in the witness generation code. In this case, `assert(in * in == n)`. From 70be83a50dfb666dcf1ee8a31c97a604c9ca81d0 Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 14 Mar 2023 12:31:19 +0100 Subject: [PATCH 50/70] Update inspect.md --- mkdocs/docs/circom-language/code-quality/inspect.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mkdocs/docs/circom-language/code-quality/inspect.md b/mkdocs/docs/circom-language/code-quality/inspect.md index cf3791da4..8b68a8927 100644 --- a/mkdocs/docs/circom-language/code-quality/inspect.md +++ b/mkdocs/docs/circom-language/code-quality/inspect.md @@ -100,3 +100,13 @@ that do not appear in any constraint of the father component. = For example: che To fix this example, we can add ` _ <== check.out` at the end of the template to let the compiler know that the remaining positions are irrelevant. +- Finally, the `--inspect` option also searches for assignments with operator `<--` that can be transformed into assignments with operator `<==`, which automatically include the corresponding constraint to guarantee the code is correct. A typical scenario of this situation is shown below: + +``` + out <-- in / 4; + out*4 === in; +``` + +Here, many circom programmers avoid the use of `<==`, since they are using the `/` operator which in many cases turn the expression in non-quadratic. Then, programmer must add the corresponding constraint using `===` to guarantee the code is correct. However, it is important to notice that the inverse of 4 is another field element (which is computed by the compiler), and thus, `in / 4` is a linear expression. Consequently, the previous instructions can be replaced by `out <== in / 4`. In these cases, the compiler suggests to use `<==` instead of `<--`. + + From 399b81f4bb32312cb1512e12540ebc0870b07f7a Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 14 Mar 2023 12:32:14 +0100 Subject: [PATCH 51/70] Update inspect.md --- mkdocs/docs/circom-language/code-quality/inspect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs/docs/circom-language/code-quality/inspect.md b/mkdocs/docs/circom-language/code-quality/inspect.md index 8b68a8927..9f154a9eb 100644 --- a/mkdocs/docs/circom-language/code-quality/inspect.md +++ b/mkdocs/docs/circom-language/code-quality/inspect.md @@ -107,6 +107,6 @@ To fix this example, we can add ` _ <== check.out` at the end of the template to out*4 === in; ``` -Here, many circom programmers avoid the use of `<==`, since they are using the `/` operator which in many cases turn the expression in non-quadratic. Then, programmer must add the corresponding constraint using `===` to guarantee the code is correct. However, it is important to notice that the inverse of 4 is another field element (which is computed by the compiler), and thus, `in / 4` is a linear expression. Consequently, the previous instructions can be replaced by `out <== in / 4`. In these cases, the compiler suggests to use `<==` instead of `<--`. +Here, many circom programmers avoid the use of `<==`, since they are using the `/` operator which in many cases turn the expression in non-quadratic. Then, programmers must add the corresponding constraint using `===` to guarantee the code is correct. However, it is important to notice that the inverse of 4 is another field element (which is computed by the compiler), and thus, `in / 4` is a linear expression. Consequently, the previous instructions can be replaced by `out <== in / 4`. In these cases, the compiler suggests to use `<==` instead of `<--`. From b782567e6e4342f5742202a057e7e59f16f8dc49 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Tue, 14 Mar 2023 13:22:20 +0100 Subject: [PATCH 52/70] improving functions simplification --- constraint_generation/src/execute.rs | 143 +++++++++++++++------------ 1 file changed, 80 insertions(+), 63 deletions(-) diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index fd852e7a7..5e8f734b5 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -76,7 +76,12 @@ impl FoldedValue { impl Default for FoldedValue { fn default() -> Self { - FoldedValue { arithmetic_slice: Option::None, node_pointer: Option::None, is_parallel: Option::None, tags: Option::None } + FoldedValue { + arithmetic_slice: Option::None, + node_pointer: Option::None, + is_parallel: Option::None, + tags: Option::None, + } } } @@ -157,20 +162,22 @@ pub fn execute_constant_expression( } } +// returns the value and if it can be simplified fn execute_statement( stmt: &Statement, program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, actual_node: &mut Option, flag_verbose: bool, -) -> Result, ()> { +) -> Result<(Option, bool), ()> { use Statement::*; let id = stmt.get_meta().elem_id; Analysis::reached(&mut runtime.analysis, id); + let mut can_be_simplified = true; let res = match stmt { MultSubstitution { .. } => unreachable!(), InitializationBlock { initializations, .. } => { - let possible_fold = execute_sequence_of_statements( + let (possible_fold, _) = execute_sequence_of_statements( initializations, program_archive, runtime, @@ -253,59 +260,58 @@ fn execute_statement( let possible_constraint = perform_assign(meta, var, *op, &access_information, r_folded, actual_node, runtime, program_archive, flag_verbose)?; if let (Option::Some(node), AssignOp::AssignConstraintSignal) = (actual_node, op) { - debug_assert!(possible_constraint.is_some()); - let constrained = possible_constraint.unwrap(); - for i in 0..AExpressionSlice::get_number_of_cells(&constrained.right){ - let value_right = treat_result_with_memory_error( - AExpressionSlice::access_value_by_index(&constrained.right, i), + debug_assert!(possible_constraint.is_some()); + let constrained = possible_constraint.unwrap(); + for i in 0..AExpressionSlice::get_number_of_cells(&constrained.right){ + let value_right = treat_result_with_memory_error( + AExpressionSlice::access_value_by_index(&constrained.right, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + if let AssignOp::AssignConstraintSignal = op { + let access_left = treat_result_with_memory_error( + AExpressionSlice::get_access_index(&constrained.right, i), meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; + + let full_symbol = format!("{}{}", + constrained.left, + create_index_appendix(&access_left), + ); - if let AssignOp::AssignConstraintSignal = op { - let access_left = treat_result_with_memory_error( - AExpressionSlice::get_access_index(&constrained.right, i), + if value_right.is_nonquadratic() { + let err = Result::Err(ExecutionError::NonQuadraticConstraint); + treat_result_with_execution_error( + err, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - - let full_symbol = format!("{}{}", - constrained.left, - create_index_appendix(&access_left), - ); - - if value_right.is_nonquadratic() { - let err = Result::Err(ExecutionError::NonQuadraticConstraint); - treat_result_with_execution_error( - err, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - } else { - let p = runtime.constants.get_p().clone(); - let symbol = AExpr::Signal { symbol: full_symbol }; - let expr = AExpr::sub(&symbol, &value_right, &p); - let ctr = AExpr::transform_expression_to_constraint_form(expr, &p).unwrap(); - node.add_constraint(ctr); - } + } else { + let p = runtime.constants.get_p().clone(); + let symbol = AExpr::Signal { symbol: full_symbol }; + let expr = AExpr::sub(&symbol, &value_right, &p); + let ctr = AExpr::transform_expression_to_constraint_form(expr, &p).unwrap(); + node.add_constraint(ctr); } - else if let AssignOp::AssignSignal = op { - //debug_assert!(possible_constraint.is_some()); - if !value_right.is_nonquadratic() && !node.is_custom_gate { - let err : Result<(),ExecutionWarning> = Result::Err(ExecutionWarning::CanBeQuadraticConstraint); - treat_result_with_execution_warning( - err, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - } + } else if let AssignOp::AssignSignal = op {// needs fix, check case arrays + //debug_assert!(possible_constraint.is_some()); + if !value_right.is_nonquadratic() && !node.is_custom_gate { + let err : Result<(),ExecutionWarning> = Result::Err(ExecutionWarning::CanBeQuadraticConstraint); + treat_result_with_execution_warning( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; } } - } + } + } Option::None } ConstraintEquality { meta, lhe, rhe, .. } => { @@ -413,12 +419,14 @@ fn execute_statement( }, Block { stmts, .. } => { ExecutionEnvironment::add_variable_block(&mut runtime.environment); - let return_value = + let (return_value, can_simplify_block) = execute_sequence_of_statements(stmts, program_archive, runtime, actual_node, flag_verbose, false)?; ExecutionEnvironment::remove_variable_block(&mut runtime.environment); + can_be_simplified = can_simplify_block; return_value } LogCall { args, .. } => { + can_be_simplified = false; if flag_verbose{ let mut index = 0; for arglog in args { @@ -457,7 +465,11 @@ fn execute_statement( let possible_bool = AExpr::get_boolean_equivalence(&arith, runtime.constants.get_p()); let result = match possible_bool { Some(b) if !b => Err(ExecutionError::FalseAssert), - _ => Ok(None), + Some(b) if b => Ok(None), + _ => { + can_be_simplified = false; + Ok(None) + } }; treat_result_with_execution_error( result, @@ -491,7 +503,7 @@ fn execute_statement( Option::None } }; - Result::Ok(res) + Result::Ok((res, can_be_simplified)) } fn execute_expression( @@ -501,6 +513,7 @@ fn execute_expression( flag_verbose: bool ) -> Result { use Expression::*; + let mut can_be_simplified = true; let res = match expr { Number(_, value) => { let a_value = AExpr::Number { value: value.clone() }; @@ -620,7 +633,9 @@ fn execute_expression( } } Call { id, args, .. } => { - execute_call(id, args, program_archive, runtime, flag_verbose)? + let (value, can_simplify) = execute_call(id, args, program_archive, runtime, flag_verbose)?; + can_be_simplified = can_simplify; + value } ParallelOp{rhe, ..} => { let folded_value = execute_expression(rhe, program_archive, runtime, flag_verbose)?; @@ -633,7 +648,7 @@ fn execute_expression( let expr_id = expr.get_meta().elem_id; let res_p = res.arithmetic_slice.clone(); if let Some(slice) = res_p { - if slice.is_single() && !expr.is_call(){ + if slice.is_single() && can_be_simplified{ let value = AExpressionSlice::unwrap_to_single(slice); Analysis::computed(&mut runtime.analysis, expr_id, value); } @@ -650,7 +665,7 @@ fn execute_call( program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, flag_verbose: bool, -) -> Result { +) -> Result<(FoldedValue, bool), ()> { let mut arg_values = Vec::new(); for arg_expression in args.iter() { let f_arg = execute_expression(arg_expression, program_archive, runtime, flag_verbose)?; @@ -676,7 +691,7 @@ fn execute_call( Ok(folded_result) } else { // in this case we preexecute and check if it needs tags let folded_result = preexecute_template_call(id, &arg_values, program_archive, runtime)?; - Ok(folded_result) + Ok((folded_result, true)) } } @@ -1297,20 +1312,20 @@ fn execute_conditional_statement( let possible_cond_bool_value = AExpr::get_boolean_equivalence(&ae_cond, runtime.constants.get_p()); if let Some(cond_bool_value) = possible_cond_bool_value { - let ret_value = match false_case { + let (ret_value, _can_simplify) = match false_case { Some(else_stmt) if !cond_bool_value => { execute_statement(else_stmt, program_archive, runtime, actual_node, flag_verbose)? } - None if !cond_bool_value => None, + None if !cond_bool_value => (None, true), _ => execute_statement(true_case, program_archive, runtime, actual_node, flag_verbose)?, }; Result::Ok((ret_value, Option::Some(cond_bool_value))) } else { let previous_block_type = runtime.block_type; runtime.block_type = BlockType::Unknown; - let mut ret_value = execute_statement(true_case, program_archive, runtime, actual_node, flag_verbose)?; + let (mut ret_value, _can_simplify) = execute_statement(true_case, program_archive, runtime, actual_node, flag_verbose)?; if let Option::Some(else_stmt) = false_case { - let else_ret = execute_statement(else_stmt, program_archive, runtime, actual_node, flag_verbose)?; + let (else_ret, _can_simplify) = execute_statement(else_stmt, program_archive, runtime, actual_node, flag_verbose)?; if ret_value.is_none() { ret_value = else_ret; } @@ -1327,17 +1342,19 @@ fn execute_sequence_of_statements( actual_node: &mut Option, flag_verbose: bool, is_complete_template: bool -) -> Result, ()> { +) -> Result<(Option, bool), ()> { + let mut can_be_simplified = true; for stmt in stmts.iter() { - let f_value = execute_statement(stmt, program_archive, runtime, actual_node, flag_verbose)?; + let (f_value, can_simplify) = execute_statement(stmt, program_archive, runtime, actual_node, flag_verbose)?; + can_be_simplified &= can_simplify; if f_value.is_some() { - return Result::Ok(f_value); + return Result::Ok((f_value, can_be_simplified)); } } if is_complete_template{ execute_delayed_declarations(program_archive, runtime, actual_node, flag_verbose)?; } - Result::Ok(Option::None) + Result::Ok((Option::None, true)) } fn execute_delayed_declarations( @@ -1680,16 +1697,16 @@ fn execute_function_call( program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, flag_verbose: bool -) -> Result { +) -> Result<(FoldedValue, bool), ()> { let previous_block = runtime.block_type; runtime.block_type = BlockType::Known; let function_body = program_archive.get_function_data(id).get_body_as_vec(); - let function_result = + let (function_result, can_be_simplified) = execute_sequence_of_statements(function_body, program_archive, runtime, &mut Option::None, flag_verbose, true)?; runtime.block_type = previous_block; let return_value = function_result.unwrap(); debug_assert!(FoldedValue::valid_arithmetic_slice(&return_value)); - Result::Ok(return_value) + Result::Ok((return_value, can_be_simplified)) } fn execute_template_call( @@ -1749,7 +1766,7 @@ fn execute_template_call( is_parallel, is_custom_gate )); - let ret = execute_sequence_of_statements( + let (ret, _) = execute_sequence_of_statements( template_body, program_archive, runtime, From 6dd88c10d9956ba34cc95c9270f5186e6d2f5bdd Mon Sep 17 00:00:00 2001 From: clararod9 Date: Tue, 14 Mar 2023 18:18:11 +0100 Subject: [PATCH 53/70] improving functions simplification --- constraint_generation/src/execute.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index 5e8f734b5..32b237b3a 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -379,7 +379,7 @@ fn execute_statement( } IfThenElse { cond, if_case, else_case, .. } => { let else_case = else_case.as_ref().map(|e| e.as_ref()); - let (possible_return, _) = execute_conditional_statement( + let (possible_return, can_simplify, _) = execute_conditional_statement( cond, if_case, else_case, @@ -388,10 +388,11 @@ fn execute_statement( actual_node, flag_verbose )?; + can_be_simplified = can_simplify; possible_return } While { cond, stmt, .. } => loop { - let (returned, condition_result) = execute_conditional_statement( + let (returned, can_simplify, condition_result) = execute_conditional_statement( cond, stmt, Option::None, @@ -400,10 +401,11 @@ fn execute_statement( actual_node, flag_verbose )?; + can_be_simplified &= can_simplify; if returned.is_some() { break returned; } else if condition_result.is_none() { - let (returned, _) = execute_conditional_statement( + let (returned, _, _) = execute_conditional_statement( cond, stmt, None, @@ -1306,32 +1308,33 @@ fn execute_conditional_statement( runtime: &mut RuntimeInformation, actual_node: &mut Option, flag_verbose: bool, -) -> Result<(Option, Option), ()> { +) -> Result<(Option, bool, Option), ()> { let f_cond = execute_expression(condition, program_archive, runtime, flag_verbose)?; let ae_cond = safe_unwrap_to_single_arithmetic_expression(f_cond, line!()); let possible_cond_bool_value = AExpr::get_boolean_equivalence(&ae_cond, runtime.constants.get_p()); if let Some(cond_bool_value) = possible_cond_bool_value { - let (ret_value, _can_simplify) = match false_case { + let (ret_value, can_simplify) = match false_case { Some(else_stmt) if !cond_bool_value => { execute_statement(else_stmt, program_archive, runtime, actual_node, flag_verbose)? } None if !cond_bool_value => (None, true), _ => execute_statement(true_case, program_archive, runtime, actual_node, flag_verbose)?, }; - Result::Ok((ret_value, Option::Some(cond_bool_value))) + Result::Ok((ret_value, can_simplify, Option::Some(cond_bool_value))) } else { let previous_block_type = runtime.block_type; runtime.block_type = BlockType::Unknown; - let (mut ret_value, _can_simplify) = execute_statement(true_case, program_archive, runtime, actual_node, flag_verbose)?; + let (mut ret_value, mut can_simplify) = execute_statement(true_case, program_archive, runtime, actual_node, flag_verbose)?; if let Option::Some(else_stmt) = false_case { - let (else_ret, _can_simplify) = execute_statement(else_stmt, program_archive, runtime, actual_node, flag_verbose)?; + let (else_ret, can_simplify_else) = execute_statement(else_stmt, program_archive, runtime, actual_node, flag_verbose)?; + can_simplify &= can_simplify_else; if ret_value.is_none() { ret_value = else_ret; } } runtime.block_type = previous_block_type; - return Result::Ok((ret_value, Option::None)); + return Result::Ok((ret_value, can_simplify, Option::None)); } } @@ -1354,7 +1357,7 @@ fn execute_sequence_of_statements( if is_complete_template{ execute_delayed_declarations(program_archive, runtime, actual_node, flag_verbose)?; } - Result::Ok((Option::None, true)) + Result::Ok((Option::None, can_be_simplified)) } fn execute_delayed_declarations( From 48361d4d8db57c25cb4be57d8aa6ff93d4788485 Mon Sep 17 00:00:00 2001 From: miguelis Date: Tue, 14 Mar 2023 18:45:07 +0100 Subject: [PATCH 54/70] Fixing parser error notification --- parser/src/lang.lalrpop | 3 --- 1 file changed, 3 deletions(-) diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 7203c262a..1325d4857 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -25,9 +25,6 @@ IdentifierListDef : Vec = { v.push(e); v }, - ",")*> ! => { - errors.push(produce_report(ReportCode::ExpectedIdentifier, s..e, file_id)); - v } }; Semicolon:() = { From bff78aaf37db6c8104952a29bc68ade9dc403d5c Mon Sep 17 00:00:00 2001 From: clararod9 Date: Wed, 15 Mar 2023 09:57:56 +0100 Subject: [PATCH 55/70] adding warning can use ==> instead of --> in mode inspect --- .../src/compute_constants.rs | 47 ++-- constraint_generation/src/execute.rs | 259 +++++++++++------- .../src/execution_data/executed_program.rs | 9 +- constraint_generation/src/lib.rs | 22 +- 4 files changed, 199 insertions(+), 138 deletions(-) diff --git a/constraint_generation/src/compute_constants.rs b/constraint_generation/src/compute_constants.rs index 84d203e86..9d515d22b 100644 --- a/constraint_generation/src/compute_constants.rs +++ b/constraint_generation/src/compute_constants.rs @@ -8,6 +8,7 @@ use program_structure::ast::{Expression, Meta, Statement}; use program_structure::error_definition::ReportCollection; use program_structure::program_archive::ProgramArchive; use std::collections::HashMap; +use crate::FlagsExecution; type CCResult = Result<(), ReportCollection>; @@ -17,7 +18,7 @@ struct Context<'a> { program_archive: &'a ProgramArchive, } -pub fn manage_functions(program_archive: &mut ProgramArchive, flag_verbose: bool, prime: &String) -> CCResult { +pub fn manage_functions(program_archive: &mut ProgramArchive, flags: FlagsExecution, prime: &String) -> CCResult { let mut reports = vec![]; let mut processed = HashMap::new(); for (name, data) in program_archive.get_functions() { @@ -25,7 +26,7 @@ pub fn manage_functions(program_archive: &mut ProgramArchive, flag_verbose: bool let environment = EE::new(); let context = Context { program_archive, inside_template: false, environment: &environment }; - treat_statement(&mut code, &context, &mut reports, flag_verbose, prime); + treat_statement(&mut code, &context, &mut reports, flags, prime); processed.insert(name.clone(), code); } for (k, v) in processed { @@ -41,14 +42,14 @@ pub fn manage_functions(program_archive: &mut ProgramArchive, flag_verbose: bool pub fn compute_vct( instances: &mut Vec, program_archive: &ProgramArchive, - flag_verbose: bool, + flags: FlagsExecution, prime: &String ) -> CCResult { let mut reports = vec![]; for instance in instances { let environment = transform_header_into_environment(&instance.header); let context = Context { program_archive, inside_template: true, environment: &environment }; - treat_statement(&mut instance.code, &context, &mut reports, flag_verbose, prime); + treat_statement(&mut instance.code, &context, &mut reports, flags, prime); } if reports.is_empty() { Result::Ok(()) @@ -75,27 +76,27 @@ fn argument_into_slice(argument: &Argument) -> AExpressionSlice { AExpressionSlice::new_array(dimensions, arithmetic_expressions) } -fn treat_statement(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flag_verbose: bool, prime: &String) { +fn treat_statement(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flags: FlagsExecution, prime: &String) { if stmt.is_initialization_block() { - treat_init_block(stmt, context, reports, flag_verbose, prime) + treat_init_block(stmt, context, reports, flags, prime) } else if stmt.is_block() { - treat_block(stmt, context, reports, flag_verbose, prime) + treat_block(stmt, context, reports, flags, prime) } else if stmt.is_if_then_else() { - treat_conditional(stmt, context, reports, flag_verbose, prime) + treat_conditional(stmt, context, reports, flags, prime) } else if stmt.is_while() { - treat_while(stmt, context, reports, flag_verbose, prime) + treat_while(stmt, context, reports, flags, prime) } else if stmt.is_declaration(){ - treat_declaration(stmt, context, reports, flag_verbose, prime) + treat_declaration(stmt, context, reports, flags, prime) } else { } } -fn treat_init_block(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flag_verbose: bool, prime: &String) { +fn treat_init_block(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flags: FlagsExecution, prime: &String) { use Statement::InitializationBlock; if let InitializationBlock { initializations, .. } = stmt { for init in initializations { if init.is_declaration() { - treat_declaration(init, context, reports, flag_verbose, prime) + treat_declaration(init, context, reports, flags, prime) } } } else { @@ -103,39 +104,39 @@ fn treat_init_block(stmt: &mut Statement, context: &Context, reports: &mut Repor } } -fn treat_block(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flag_verbose: bool, prime: &String) { +fn treat_block(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flags: FlagsExecution, prime: &String) { use Statement::Block; if let Block { stmts, .. } = stmt { for s in stmts { - treat_statement(s, context, reports, flag_verbose, prime); + treat_statement(s, context, reports, flags, prime); } } else { unreachable!() } } -fn treat_while(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flag_verbose: bool, prime: &String) { +fn treat_while(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flags: FlagsExecution, prime: &String) { use Statement::While; if let While { stmt, .. } = stmt { - treat_statement(stmt, context, reports, flag_verbose, prime); + treat_statement(stmt, context, reports, flags, prime); } else { unreachable!() } } -fn treat_conditional(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flag_verbose: bool, prime: &String) { +fn treat_conditional(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flags: FlagsExecution, prime: &String) { use Statement::IfThenElse; if let IfThenElse { if_case, else_case, .. } = stmt { - treat_statement(if_case, context, reports, flag_verbose, prime); + treat_statement(if_case, context, reports, flags, prime); if let Option::Some(s) = else_case { - treat_statement(s, context, reports, flag_verbose, prime); + treat_statement(s, context, reports, flags, prime); } } else { unreachable!() } } -fn treat_declaration(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flag_verbose: bool, prime: &String) { +fn treat_declaration(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flags: FlagsExecution, prime: &String) { use Statement::Declaration; use program_structure::ast::VariableType::AnonymousComponent; if let Declaration { meta, dimensions, xtype, .. } = stmt { @@ -146,7 +147,7 @@ fn treat_declaration(stmt: &mut Statement, context: &Context, reports: &mut Repo }, _ => { for d in dimensions.iter_mut() { - let execution_response = treat_dimension(d, context, reports, flag_verbose, prime); + let execution_response = treat_dimension(d, context, reports, flags, prime); if let Option::Some(v) = execution_response { concrete_dimensions.push(v); } else { @@ -165,7 +166,7 @@ fn treat_dimension( dim: &Expression, context: &Context, reports: &mut ReportCollection, - flag_verbose: bool, + flags: FlagsExecution, prime: &String, ) -> Option { use crate::execute::execute_constant_expression; @@ -176,7 +177,7 @@ fn treat_dimension( } else { let program = context.program_archive; let env = context.environment; - let execution_result = execute_constant_expression(dim, program, env.clone(), flag_verbose, prime); + let execution_result = execute_constant_expression(dim, program, env.clone(), flags, prime); match execution_result { Result::Err(mut r) => { reports.append(&mut r); diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index 32b237b3a..6767c3da5 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -21,6 +21,7 @@ use super::{ }; use circom_algebra::num_bigint::BigInt; use std::collections::{HashMap, BTreeMap}; +use crate::FlagsExecution; type AExpr = ArithmeticExpressionGen; type AnonymousComponentsInfo = BTreeMap)>; @@ -92,12 +93,15 @@ enum ExecutionError { } enum ExecutionWarning { - CanBeQuadraticConstraint, + CanBeQuadraticConstraintSingle(), + CanBeQuadraticConstraintMultiple(Vec), } + + pub fn constraint_execution( program_archive: &ProgramArchive, - flag_verbose: bool, + flags: FlagsExecution, prime: &String, ) -> Result<(ExecutedProgram, ReportCollection), ReportCollection> { let main_file_id = program_archive.get_file_id_main(); @@ -110,7 +114,7 @@ pub fn constraint_execution( if let Call { id, args, .. } = &program_archive.get_main_expression() { let mut arg_values = Vec::new(); for arg_expression in args.iter() { - let f_arg = execute_expression(arg_expression, program_archive, &mut runtime_information, flag_verbose); + let f_arg = execute_expression(arg_expression, program_archive, &mut runtime_information, flags); arg_values.push(safe_unwrap_to_arithmetic_slice(f_arg.unwrap(), line!())); // improve } @@ -120,7 +124,7 @@ pub fn constraint_execution( BTreeMap::new(), program_archive, &mut runtime_information, - flag_verbose, + flags, ) } else { unreachable!("The main expression should be a call."); @@ -140,14 +144,14 @@ pub fn execute_constant_expression( expression: &Expression, program_archive: &ProgramArchive, environment: ExecutionEnvironment, - flag_verbose: bool, + flags: FlagsExecution, prime: &String, ) -> Result { let current_file = expression.get_meta().get_file_id(); let mut runtime_information = RuntimeInformation::new(current_file, program_archive.id_max, prime); runtime_information.environment = environment; let folded_value_result = - execute_expression(expression, program_archive, &mut runtime_information, flag_verbose); + execute_expression(expression, program_archive, &mut runtime_information, flags); match folded_value_result { Result::Err(_) => Result::Err(runtime_information.runtime_errors), Result::Ok(folded_value) => { @@ -168,7 +172,7 @@ fn execute_statement( program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, actual_node: &mut Option, - flag_verbose: bool, + flags: FlagsExecution, ) -> Result<(Option, bool), ()> { use Statement::*; let id = stmt.get_meta().elem_id; @@ -182,7 +186,7 @@ fn execute_statement( program_archive, runtime, actual_node, - flag_verbose, + flags, false )?; debug_assert!(possible_fold.is_none()); @@ -203,7 +207,7 @@ fn execute_statement( let mut arithmetic_values = Vec::new(); for dimension in dimensions.iter() { let f_dimensions = - execute_expression(dimension, program_archive, runtime, flag_verbose)?; + execute_expression(dimension, program_archive, runtime, flags)?; arithmetic_values .push(safe_unwrap_to_single_arithmetic_expression(f_dimensions, line!())); } @@ -255,23 +259,26 @@ fn execute_statement( Option::None } Substitution { meta, var, access, op, rhe, .. } => { - let access_information = treat_accessing(meta, access, program_archive, runtime, flag_verbose)?; - let r_folded = execute_expression(rhe, program_archive, runtime, flag_verbose)?; + let access_information = treat_accessing(meta, access, program_archive, runtime, flags)?; + let r_folded = execute_expression(rhe, program_archive, runtime, flags)?; let possible_constraint = - perform_assign(meta, var, *op, &access_information, r_folded, actual_node, runtime, program_archive, flag_verbose)?; - if let (Option::Some(node), AssignOp::AssignConstraintSignal) = (actual_node, op) { - debug_assert!(possible_constraint.is_some()); - let constrained = possible_constraint.unwrap(); - for i in 0..AExpressionSlice::get_number_of_cells(&constrained.right){ - let value_right = treat_result_with_memory_error( - AExpressionSlice::access_value_by_index(&constrained.right, i), - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; + perform_assign(meta, var, *op, &access_information, r_folded, actual_node, runtime, program_archive, flags)?; + if let Option::Some(node) = actual_node { + if *op == AssignOp::AssignConstraintSignal || (*op == AssignOp::AssignSignal && flags.inspect){ + debug_assert!(possible_constraint.is_some()); + let constrained = possible_constraint.unwrap(); + + let mut needs_double_arrow = Vec::new(); + for i in 0..AExpressionSlice::get_number_of_cells(&constrained.right){ + let value_right = treat_result_with_memory_error( + AExpressionSlice::access_value_by_index(&constrained.right, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; - if let AssignOp::AssignConstraintSignal = op { - let access_left = treat_result_with_memory_error( + + let access_left = treat_result_with_memory_error( AExpressionSlice::get_access_index(&constrained.right, i), meta, &mut runtime.runtime_errors, @@ -282,42 +289,62 @@ fn execute_statement( constrained.left, create_index_appendix(&access_left), ); + if let AssignOp::AssignConstraintSignal = op { + if value_right.is_nonquadratic() { + let err = Result::Err(ExecutionError::NonQuadraticConstraint); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } else { + let p = runtime.constants.get_p().clone(); + let symbol = AExpr::Signal { symbol: full_symbol }; + let expr = AExpr::sub(&symbol, &value_right, &p); + let ctr = AExpr::transform_expression_to_constraint_form(expr, &p).unwrap(); + node.add_constraint(ctr); + } + } else if let AssignOp::AssignSignal = op {// needs fix, check case arrays + //debug_assert!(possible_constraint.is_some()); + if !value_right.is_nonquadratic() && !node.is_custom_gate { + needs_double_arrow.push(full_symbol); + } + } + } - if value_right.is_nonquadratic() { - let err = Result::Err(ExecutionError::NonQuadraticConstraint); - treat_result_with_execution_error( + if !needs_double_arrow.is_empty() && flags.inspect{ + // in case we can subsitute the complete expression to ==> + if needs_double_arrow.len() == AExpressionSlice::get_number_of_cells(&constrained.right){ + let err : Result<(),ExecutionWarning> = + Result::Err(ExecutionWarning::CanBeQuadraticConstraintSingle()); + + treat_result_with_execution_warning( err, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - } else { - let p = runtime.constants.get_p().clone(); - let symbol = AExpr::Signal { symbol: full_symbol }; - let expr = AExpr::sub(&symbol, &value_right, &p); - let ctr = AExpr::transform_expression_to_constraint_form(expr, &p).unwrap(); - node.add_constraint(ctr); - } - } else if let AssignOp::AssignSignal = op {// needs fix, check case arrays - //debug_assert!(possible_constraint.is_some()); - if !value_right.is_nonquadratic() && !node.is_custom_gate { - let err : Result<(),ExecutionWarning> = Result::Err(ExecutionWarning::CanBeQuadraticConstraint); + } else{ + let err : Result<(),ExecutionWarning> = + Result::Err(ExecutionWarning::CanBeQuadraticConstraintMultiple(needs_double_arrow)); + treat_result_with_execution_warning( - err, + err, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; } } - } + } } Option::None } ConstraintEquality { meta, lhe, rhe, .. } => { debug_assert!(actual_node.is_some()); - let f_left = execute_expression(lhe, program_archive, runtime, flag_verbose)?; - let f_right = execute_expression(rhe, program_archive, runtime, flag_verbose)?; + let f_left = execute_expression(lhe, program_archive, runtime, flags)?; + let f_right = execute_expression(rhe, program_archive, runtime, flags)?; let arith_left = safe_unwrap_to_arithmetic_slice(f_left, line!()); let arith_right = safe_unwrap_to_arithmetic_slice(f_right, line!()); @@ -368,7 +395,7 @@ fn execute_statement( Option::None } Return { value, .. } => { - let mut f_return = execute_expression(value, program_archive, runtime, flag_verbose)?; + let mut f_return = execute_expression(value, program_archive, runtime, flags)?; if let Option::Some(slice) = &mut f_return.arithmetic_slice { if runtime.block_type == BlockType::Unknown { *slice = AExpressionSlice::new_with_route(slice.route(), &AExpr::NonQuadratic); @@ -386,7 +413,7 @@ fn execute_statement( program_archive, runtime, actual_node, - flag_verbose + flags )?; can_be_simplified = can_simplify; possible_return @@ -399,7 +426,7 @@ fn execute_statement( program_archive, runtime, actual_node, - flag_verbose + flags )?; can_be_simplified &= can_simplify; if returned.is_some() { @@ -412,7 +439,7 @@ fn execute_statement( program_archive, runtime, actual_node, - flag_verbose + flags )?; break returned; } else if !condition_result.unwrap() { @@ -422,18 +449,18 @@ fn execute_statement( Block { stmts, .. } => { ExecutionEnvironment::add_variable_block(&mut runtime.environment); let (return_value, can_simplify_block) = - execute_sequence_of_statements(stmts, program_archive, runtime, actual_node, flag_verbose, false)?; + execute_sequence_of_statements(stmts, program_archive, runtime, actual_node, flags, false)?; ExecutionEnvironment::remove_variable_block(&mut runtime.environment); can_be_simplified = can_simplify_block; return_value } LogCall { args, .. } => { can_be_simplified = false; - if flag_verbose{ + if flags.verbose{ let mut index = 0; for arglog in args { if let LogArgument::LogExp(arg) = arglog{ - let f_result = execute_expression(arg, program_archive, runtime, flag_verbose)?; + let f_result = execute_expression(arg, program_archive, runtime, flags)?; let arith = safe_unwrap_to_single_arithmetic_expression(f_result, line!()); if AExpr::is_number(&arith){ print!("{}", arith); @@ -454,7 +481,7 @@ fn execute_statement( } else{ for arglog in args { if let LogArgument::LogExp(arg) = arglog{ - let f_result = execute_expression(arg, program_archive, runtime, flag_verbose)?; + let f_result = execute_expression(arg, program_archive, runtime, flags)?; let _arith = safe_unwrap_to_single_arithmetic_expression(f_result, line!()); } } @@ -462,7 +489,7 @@ fn execute_statement( Option::None } Assert { arg, meta, .. } => { - let f_result = execute_expression(arg, program_archive, runtime, flag_verbose)?; + let f_result = execute_expression(arg, program_archive, runtime, flags)?; let arith = safe_unwrap_to_single_arithmetic_expression(f_result, line!()); let possible_bool = AExpr::get_boolean_equivalence(&arith, runtime.constants.get_p()); let result = match possible_bool { @@ -481,7 +508,7 @@ fn execute_statement( )? } UnderscoreSubstitution{ meta, rhe, op} =>{ - let f_result = execute_expression(rhe, program_archive, runtime, flag_verbose)?; + let f_result = execute_expression(rhe, program_archive, runtime, flags)?; let arithmetic_slice = safe_unwrap_to_arithmetic_slice(f_result, line!()); if *op == AssignOp::AssignConstraintSignal{ for i in 0..AExpressionSlice::get_number_of_cells(&arithmetic_slice){ @@ -512,7 +539,7 @@ fn execute_expression( expr: &Expression, program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool + flags: FlagsExecution ) -> Result { use Expression::*; let mut can_be_simplified = true; @@ -524,11 +551,11 @@ fn execute_expression( } Variable { meta, name, access, .. } => { if ExecutionEnvironment::has_signal(&runtime.environment, name) { - execute_signal(meta, name, access, program_archive, runtime, flag_verbose)? + execute_signal(meta, name, access, program_archive, runtime, flags)? } else if ExecutionEnvironment::has_component(&runtime.environment, name) { - execute_component(meta, name, access, program_archive, runtime, flag_verbose)? + execute_component(meta, name, access, program_archive, runtime, flags)? } else if ExecutionEnvironment::has_variable(&runtime.environment, name) { - execute_variable(meta, name, access, program_archive, runtime, flag_verbose)? + execute_variable(meta, name, access, program_archive, runtime, flags)? } else { unreachable!(); } @@ -536,7 +563,7 @@ fn execute_expression( ArrayInLine { meta, values, .. } => { let mut arithmetic_slice_array = Vec::new(); for value in values.iter() { - let f_value = execute_expression(value, program_archive, runtime, flag_verbose)?; + let f_value = execute_expression(value, program_archive, runtime, flags)?; let slice_value = safe_unwrap_to_arithmetic_slice(f_value, line!()); arithmetic_slice_array.push(slice_value); } @@ -566,7 +593,7 @@ fn execute_expression( FoldedValue { arithmetic_slice: Option::Some(array_slice), ..FoldedValue::default() } } UniformArray { meta, value, dimension, .. } => { - let f_dimension = execute_expression(dimension, program_archive, runtime, flag_verbose)?; + let f_dimension = execute_expression(dimension, program_archive, runtime, flags)?; let arithmetic_dimension = safe_unwrap_to_single_arithmetic_expression(f_dimension, line!()); let usable_dimension = if let Option::Some(dimension) = cast_index(&arithmetic_dimension) { dimension @@ -574,7 +601,7 @@ fn execute_expression( unreachable!() }; - let f_value = execute_expression(value, program_archive, runtime, flag_verbose)?; + let f_value = execute_expression(value, program_archive, runtime, flags)?; let slice_value = safe_unwrap_to_arithmetic_slice(f_value, line!()); let mut dims = vec![usable_dimension]; @@ -602,8 +629,8 @@ fn execute_expression( FoldedValue { arithmetic_slice: Option::Some(array_slice), ..FoldedValue::default() } } InfixOp { meta, lhe, infix_op, rhe, .. } => { - let l_fold = execute_expression(lhe, program_archive, runtime, flag_verbose)?; - let r_fold = execute_expression(rhe, program_archive, runtime, flag_verbose)?; + let l_fold = execute_expression(lhe, program_archive, runtime, flags)?; + let r_fold = execute_expression(rhe, program_archive, runtime, flags)?; let l_value = safe_unwrap_to_single_arithmetic_expression(l_fold, line!()); let r_value = safe_unwrap_to_single_arithmetic_expression(r_fold, line!()); let r_value = execute_infix_op(meta, *infix_op, &l_value, &r_value, runtime)?; @@ -611,7 +638,7 @@ fn execute_expression( FoldedValue { arithmetic_slice: Option::Some(r_slice), ..FoldedValue::default() } } PrefixOp { prefix_op, rhe, .. } => { - let folded_value = execute_expression(rhe, program_archive, runtime, flag_verbose)?; + let folded_value = execute_expression(rhe, program_archive, runtime, flags)?; let arithmetic_value = safe_unwrap_to_single_arithmetic_expression(folded_value, line!()); let arithmetic_result = execute_prefix_op(*prefix_op, &arithmetic_value, runtime)?; @@ -619,15 +646,15 @@ fn execute_expression( FoldedValue { arithmetic_slice: Option::Some(slice_result), ..FoldedValue::default() } } InlineSwitchOp { cond, if_true, if_false, .. } => { - let f_cond = execute_expression(cond, program_archive, runtime, flag_verbose)?; + let f_cond = execute_expression(cond, program_archive, runtime, flags)?; let ae_cond = safe_unwrap_to_single_arithmetic_expression(f_cond, line!()); let possible_bool_cond = AExpr::get_boolean_equivalence(&ae_cond, runtime.constants.get_p()); if let Option::Some(bool_cond) = possible_bool_cond { if bool_cond { - execute_expression(if_true, program_archive, runtime, flag_verbose)? + execute_expression(if_true, program_archive, runtime, flags)? } else { - execute_expression(if_false, program_archive, runtime, flag_verbose)? + execute_expression(if_false, program_archive, runtime, flags)? } } else { let arithmetic_slice = Option::Some(AExpressionSlice::new(&AExpr::NonQuadratic)); @@ -635,12 +662,12 @@ fn execute_expression( } } Call { id, args, .. } => { - let (value, can_simplify) = execute_call(id, args, program_archive, runtime, flag_verbose)?; + let (value, can_simplify) = execute_call(id, args, program_archive, runtime, flags)?; can_be_simplified = can_simplify; value } ParallelOp{rhe, ..} => { - let folded_value = execute_expression(rhe, program_archive, runtime, flag_verbose)?; + let folded_value = execute_expression(rhe, program_archive, runtime, flags)?; let (node_pointer, _) = safe_unwrap_to_valid_node_pointer(folded_value, line!()); FoldedValue { node_pointer: Option::Some(node_pointer), is_parallel: Option::Some(true), ..FoldedValue::default() } @@ -666,11 +693,11 @@ fn execute_call( args: &Vec, program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool, + flags: FlagsExecution, ) -> Result<(FoldedValue, bool), ()> { let mut arg_values = Vec::new(); for arg_expression in args.iter() { - let f_arg = execute_expression(arg_expression, program_archive, runtime, flag_verbose)?; + let f_arg = execute_expression(arg_expression, program_archive, runtime, flags)?; arg_values.push(safe_unwrap_to_arithmetic_slice(f_arg, line!())); } if program_archive.contains_function(id){ // in this case we execute @@ -683,7 +710,7 @@ fn execute_call( let previous_id = std::mem::replace(&mut runtime.current_file, new_file_id); runtime.call_trace.push(id.clone()); - let folded_result = execute_function_call(id, program_archive, runtime, flag_verbose)?; + let folded_result = execute_function_call(id, program_archive, runtime, flags)?; runtime.environment = previous_environment; runtime.current_file = previous_id; @@ -703,7 +730,7 @@ fn execute_template_call_complete( tags: BTreeMap, program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool, + flags: FlagsExecution, ) -> Result { if program_archive.contains_template(id){ // in this case we execute let new_environment = prepare_environment_for_call(id, &arg_values, program_archive); @@ -715,7 +742,7 @@ fn execute_template_call_complete( let previous_id = std::mem::replace(&mut runtime.current_file, new_file_id); runtime.call_trace.push(id.clone()); - let folded_result = execute_template_call(id, arg_values, tags, program_archive, runtime, flag_verbose)?; + let folded_result = execute_template_call(id, arg_values, tags, program_archive, runtime, flags)?; runtime.environment = previous_environment; runtime.current_file = previous_id; @@ -808,7 +835,7 @@ fn perform_assign( actual_node: &mut Option, runtime: &mut RuntimeInformation, program_archive: &ProgramArchive, - flag_verbose: bool + flags: FlagsExecution ) -> Result, ()> { use super::execution_data::type_definitions::SubComponentData; let full_symbol = create_symbol(symbol, &accessing_information); @@ -1126,7 +1153,7 @@ fn perform_assign( inputs_tags, program_archive, runtime, - flag_verbose, + flags, )?; let (node_pointer, is_parallel) = safe_unwrap_to_valid_node_pointer(result, line!()); @@ -1216,7 +1243,7 @@ fn perform_assign( inputs_tags, program_archive, runtime, - flag_verbose, + flags, )?; let (node_pointer, is_parallel) = safe_unwrap_to_valid_node_pointer(folded_result, line!()); @@ -1307,27 +1334,27 @@ fn execute_conditional_statement( program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, actual_node: &mut Option, - flag_verbose: bool, + flags: FlagsExecution, ) -> Result<(Option, bool, Option), ()> { - let f_cond = execute_expression(condition, program_archive, runtime, flag_verbose)?; + let f_cond = execute_expression(condition, program_archive, runtime, flags)?; let ae_cond = safe_unwrap_to_single_arithmetic_expression(f_cond, line!()); let possible_cond_bool_value = AExpr::get_boolean_equivalence(&ae_cond, runtime.constants.get_p()); if let Some(cond_bool_value) = possible_cond_bool_value { let (ret_value, can_simplify) = match false_case { Some(else_stmt) if !cond_bool_value => { - execute_statement(else_stmt, program_archive, runtime, actual_node, flag_verbose)? + execute_statement(else_stmt, program_archive, runtime, actual_node, flags)? } None if !cond_bool_value => (None, true), - _ => execute_statement(true_case, program_archive, runtime, actual_node, flag_verbose)?, + _ => execute_statement(true_case, program_archive, runtime, actual_node, flags)?, }; Result::Ok((ret_value, can_simplify, Option::Some(cond_bool_value))) } else { let previous_block_type = runtime.block_type; runtime.block_type = BlockType::Unknown; - let (mut ret_value, mut can_simplify) = execute_statement(true_case, program_archive, runtime, actual_node, flag_verbose)?; + let (mut ret_value, mut can_simplify) = execute_statement(true_case, program_archive, runtime, actual_node, flags)?; if let Option::Some(else_stmt) = false_case { - let (else_ret, can_simplify_else) = execute_statement(else_stmt, program_archive, runtime, actual_node, flag_verbose)?; + let (else_ret, can_simplify_else) = execute_statement(else_stmt, program_archive, runtime, actual_node, flags)?; can_simplify &= can_simplify_else; if ret_value.is_none() { ret_value = else_ret; @@ -1343,19 +1370,19 @@ fn execute_sequence_of_statements( program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, actual_node: &mut Option, - flag_verbose: bool, + flags: FlagsExecution, is_complete_template: bool ) -> Result<(Option, bool), ()> { let mut can_be_simplified = true; for stmt in stmts.iter() { - let (f_value, can_simplify) = execute_statement(stmt, program_archive, runtime, actual_node, flag_verbose)?; + let (f_value, can_simplify) = execute_statement(stmt, program_archive, runtime, actual_node, flags)?; can_be_simplified &= can_simplify; if f_value.is_some() { return Result::Ok((f_value, can_be_simplified)); } } if is_complete_template{ - execute_delayed_declarations(program_archive, runtime, actual_node, flag_verbose)?; + execute_delayed_declarations(program_archive, runtime, actual_node, flags)?; } Result::Ok((Option::None, can_be_simplified)) } @@ -1364,12 +1391,12 @@ fn execute_delayed_declarations( program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, actual_node: &mut Option, - flag_verbose: bool, + flags: FlagsExecution, )-> Result<(), ()> { for (component_name, (meta, dimensions)) in runtime.anonymous_components.clone(){ let mut arithmetic_values = Vec::new(); for dimension in dimensions.iter() { - let f_dimensions = execute_expression(dimension, program_archive, runtime, flag_verbose)?; + let f_dimensions = execute_expression(dimension, program_archive, runtime, flags)?; arithmetic_values .push(safe_unwrap_to_single_arithmetic_expression(f_dimensions, line!())); } @@ -1435,9 +1462,9 @@ fn execute_variable( access: &[Access], program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool + flags: FlagsExecution ) -> Result { - let access_information = treat_accessing(meta, access, program_archive, runtime, flag_verbose)?; + let access_information = treat_accessing(meta, access, program_archive, runtime, flags)?; if access_information.undefined { let arithmetic_slice = Option::Some(AExpressionSlice::new(&AExpr::NonQuadratic)); return Result::Ok(FoldedValue { arithmetic_slice, ..FoldedValue::default() }); @@ -1468,9 +1495,9 @@ fn execute_signal( access: &[Access], program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool + flags: FlagsExecution ) -> Result { - let access_information = treat_accessing(meta, access, program_archive, runtime, flag_verbose)?; + let access_information = treat_accessing(meta, access, program_archive, runtime, flags)?; if access_information.undefined { let arithmetic_slice = Option::Some(AExpressionSlice::new(&AExpr::NonQuadratic)); return Result::Ok(FoldedValue { arithmetic_slice, ..FoldedValue::default() }); @@ -1568,9 +1595,9 @@ fn execute_component( access: &[Access], program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool + flags: FlagsExecution ) -> Result { - let access_information = treat_accessing(meta, access, program_archive, runtime, flag_verbose)?; + let access_information = treat_accessing(meta, access, program_archive, runtime, flags)?; if access_information.undefined { let arithmetic_slice = Option::Some(AExpressionSlice::new(&AExpr::NonQuadratic)); return Result::Ok(FoldedValue { arithmetic_slice, ..FoldedValue::default() }); @@ -1699,13 +1726,13 @@ fn execute_function_call( id: &str, program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool + flags: FlagsExecution ) -> Result<(FoldedValue, bool), ()> { let previous_block = runtime.block_type; runtime.block_type = BlockType::Known; let function_body = program_archive.get_function_data(id).get_body_as_vec(); let (function_result, can_be_simplified) = - execute_sequence_of_statements(function_body, program_archive, runtime, &mut Option::None, flag_verbose, true)?; + execute_sequence_of_statements(function_body, program_archive, runtime, &mut Option::None, flags, true)?; runtime.block_type = previous_block; let return_value = function_result.unwrap(); debug_assert!(FoldedValue::valid_arithmetic_slice(&return_value)); @@ -1718,7 +1745,7 @@ fn execute_template_call( tag_values: BTreeMap, program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool + flags: FlagsExecution ) -> Result { debug_assert!(runtime.block_type == BlockType::Known); let is_main = std::mem::replace(&mut runtime.public_inputs, vec![]); @@ -1774,7 +1801,7 @@ fn execute_template_call( program_archive, runtime, &mut node_wrap, - flag_verbose, + flags, true )?; debug_assert!(ret.is_none()); @@ -1901,7 +1928,7 @@ fn treat_indexing( access: &[Access], program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool + flags: FlagsExecution ) -> Result<(Vec, Option, usize), ()> { let mut index_accesses = Vec::new(); let mut signal_name = Option::None; @@ -1912,7 +1939,7 @@ fn treat_indexing( } match &access[act] { Access::ArrayAccess(index) => { - let index_fold = execute_expression(index, program_archive, runtime, flag_verbose)?; + let index_fold = execute_expression(index, program_archive, runtime, flags)?; let index_arithmetic_expression = safe_unwrap_to_single_arithmetic_expression(index_fold, line!()); index_accesses.push(index_arithmetic_expression); @@ -2001,12 +2028,12 @@ fn treat_accessing( access: &[Access], program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool + flags: FlagsExecution ) -> Result { let (ae_before_signal, signal_name, signal_index) = - treat_indexing(0, access, program_archive, runtime, flag_verbose)?; + treat_indexing(0, access, program_archive, runtime, flags)?; let (ae_after_signal, tag_name , _tag_index) = - treat_indexing(signal_index + 1, access, program_archive, runtime, flag_verbose)?; + treat_indexing(signal_index + 1, access, program_archive, runtime, flags)?; treat_result_with_memory_error( valid_indexing(&ae_before_signal), meta, @@ -2337,10 +2364,32 @@ fn treat_result_with_execution_warning( Result::Ok(_) => Result::Ok(()), Result::Err(execution_error) => { let report = match execution_error { - CanBeQuadraticConstraint => Report::warning( - "Consider using <== instead of <-- to add the corresponding quadratic constraint".to_string(), - ReportCode::RuntimeWarning, - ) + CanBeQuadraticConstraintSingle() => { + let msg = format!( + "Consider using <== instead of <-- to add the corresponding quadratic constraint.\n The constraint representing the assignment satisfies the R1CS format and can be added to the constraint system." + ); + Report::warning( + msg, + ReportCode::RuntimeWarning, + ) + }, + CanBeQuadraticConstraintMultiple(positions) =>{ + let mut msg_positions = positions[0].clone(); + for i in 1..positions.len(){ + msg_positions = format!("{}, {}", msg_positions, positions[i].clone()) + }; + + let msg = format!( + "Consider using <== instead of <-- for some of positions of the array of signals being assigned.\n The constraints representing the assignment of the positions {} satisfy the R1CS format and can be added to the constraint system.", + msg_positions + ); + Report::warning( + msg, + ReportCode::RuntimeWarning, + ) + } + + }; add_report_to_runtime(report, meta, runtime_errors, call_trace); Result::Ok(()) diff --git a/constraint_generation/src/execution_data/executed_program.rs b/constraint_generation/src/execution_data/executed_program.rs index bfdf979bb..dcb1ce2da 100644 --- a/constraint_generation/src/execution_data/executed_program.rs +++ b/constraint_generation/src/execution_data/executed_program.rs @@ -1,4 +1,5 @@ use super::analysis::Analysis; +use crate::FlagsExecution; use super::executed_template::{ExecutedTemplate, PreExecutedTemplate}; use super::type_definitions::*; use compiler::hir::very_concrete_program::{Stats, VCPConfig, VCP}; @@ -100,7 +101,7 @@ impl ExecutedProgram { node_index } - pub fn export(mut self, mut program: ProgramArchive, flag_verbose: bool, flag_inspect: bool) -> ExportResult { + pub fn export(mut self, mut program: ProgramArchive, flags: FlagsExecution) -> ExportResult { use super::executed_template::templates_in_mixed_arrays; fn merge_mixed(org: Vec, new: Vec) -> Vec { let mut result = Vec::with_capacity(org.len()); @@ -133,14 +134,14 @@ impl ExecutedProgram { temp_instances[dag.main_id()].is_not_parallel_component = true; dag.clean_constraints(); - if flag_inspect{ + if flags.inspect{ let mut w = dag.constraint_analysis()?; warnings.append(&mut w); } let dag_stats = produce_dags_stats(&dag); - crate::compute_constants::manage_functions(&mut program, flag_verbose, &self.prime)?; - crate::compute_constants::compute_vct(&mut temp_instances, &program, flag_verbose, &self.prime)?; + crate::compute_constants::manage_functions(&mut program, flags, &self.prime)?; + crate::compute_constants::compute_vct(&mut temp_instances, &program, flags, &self.prime)?; let mut mixed = vec![]; let mut index = 0; for in_mixed in mixed_instances { diff --git a/constraint_generation/src/lib.rs b/constraint_generation/src/lib.rs index a893a0f8e..33af998bd 100644 --- a/constraint_generation/src/lib.rs +++ b/constraint_generation/src/lib.rs @@ -33,15 +33,25 @@ pub struct BuildConfig { pub prime: String, } +#[derive(Debug, Copy, Clone)] +pub struct FlagsExecution{ + pub verbose: bool, + pub inspect: bool, +} + pub type ConstraintWriter = Box; type BuildResponse = Result<(ConstraintWriter, VCP), ()>; pub fn build_circuit(program: ProgramArchive, config: BuildConfig) -> BuildResponse { let files = program.file_library.clone(); - let (exe, warnings) = instantiation(&program, config.flag_verbose, &config.prime).map_err(|r| { + let flags = FlagsExecution{ + verbose: config.flag_verbose, + inspect: config.inspect_constraints, + }; + let (exe, warnings) = instantiation(&program, flags, &config.prime).map_err(|r| { Report::print_reports(&r, &files); })?; Report::print_reports(&warnings, &files); - let (mut dag, mut vcp, warnings) = export(exe, program, config.flag_verbose, config.inspect_constraints).map_err(|r| { + let (mut dag, mut vcp, warnings) = export(exe, program, flags).map_err(|r| { Report::print_reports(&r, &files); })?; if config.inspect_constraints { @@ -57,8 +67,8 @@ pub fn build_circuit(program: ProgramArchive, config: BuildConfig) -> BuildRespo } type InstantiationResponse = Result<(ExecutedProgram, ReportCollection), ReportCollection>; -fn instantiation(program: &ProgramArchive, flag_verbose: bool, prime: &String) -> InstantiationResponse { - let execution_result = execute::constraint_execution(&program, flag_verbose, prime); +fn instantiation(program: &ProgramArchive, flags: FlagsExecution, prime: &String) -> InstantiationResponse { + let execution_result = execute::constraint_execution(&program, flags, prime); match execution_result { Ok((program_exe, warnings)) => { let no_nodes = program_exe.number_of_nodes(); @@ -71,8 +81,8 @@ fn instantiation(program: &ProgramArchive, flag_verbose: bool, prime: &String) - } } -fn export(exe: ExecutedProgram, program: ProgramArchive, flag_verbose: bool, flag_inspect: bool) -> ExportResult { - let exported = exe.export(program, flag_verbose, flag_inspect); +fn export(exe: ExecutedProgram, program: ProgramArchive, flags: FlagsExecution) -> ExportResult { + let exported = exe.export(program, flags); exported } From a430faed4baf2d590540d97a2fcd6da8382be835 Mon Sep 17 00:00:00 2001 From: Albert Rubio <34064782+alrubio@users.noreply.github.com> Date: Wed, 15 Mar 2023 10:11:48 +0100 Subject: [PATCH 56/70] Update inspect.md --- .../circom-language/code-quality/inspect.md | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/mkdocs/docs/circom-language/code-quality/inspect.md b/mkdocs/docs/circom-language/code-quality/inspect.md index 9f154a9eb..771d5be55 100644 --- a/mkdocs/docs/circom-language/code-quality/inspect.md +++ b/mkdocs/docs/circom-language/code-quality/inspect.md @@ -4,9 +4,9 @@ description: >- --- # Improving security of circuits by using --inspect option -When using --inspect option, the compiler searches for signals that do not appear in any constraint. In case it finds some, then it throws a warning to let the programmer know those unconstrained signals. The compiler also throws a warning when some input or output signal of a subcomponent in a template do not appear in any onstraint of the father component. To avoid these warnings, the compiler could use the underscore notation '_' to inform the compiler that such a situation is expected. Let us see several cases where we can find that situation. +When using --inspect option, the compiler searches for signals that may be underconstrained. In case it finds some, it throws a warning to let the programmer know whichs are those potentially underconstrained signals. For instance, the compiler throws a warning when some input or output signal of a subcomponent in a template do not appear in any constraint of the father component. In case this is intended, the programmer can use the underscore notation '_' to inform the compiler that such a situation is as expected. A warning is also shown when a signal is not used in any constraint in the component it belongs to. Let us see several cases where we can find that situation. -- The compiler throws a warning if a signal defined in a template do not appear in any constraint of such template. +1) The compiler throws a warning if a signal defined in a template do not appear in any constraint of such template for the given instantiation. ``` template B(n){ @@ -33,8 +33,23 @@ In this example, `aux` is only used in the `if` branch. Thus, for the main compo ```warning[CA01]: In template "A(3)": Local signal aux does not appear in any constraint``` - To avoid the warning, we can add inside the `else` branch, the instruction `_ <== aux;` to indicate the compiler that aux is not used in this case. - Since `circom 2.1.5`, we can also define signals inside `if` blocks with conditions known at compilation time and thus, use this feature to solve the previous warning as follows: +To avoid the warning, we can add inside the `else` branch, the instruction `_ <== aux;` to indicate the compiler that aux is not used in this case. +``` +template A(n){ + signal aux; + signal out; + if(n == 2){ + aux <== 2; + out <== B()(aux); + } + else{ + _ <== aux; + out <== 5; + } +} + ``` + +Alternatively, since `circom 2.1.5`, we can also define signals inside `if` blocks with conditions known at compilation time and thus, we can use this feature to solve the previous warning as follows: ``` template A(n){ signal out; @@ -48,10 +63,9 @@ template A(n){ } ``` -- Another case when throwing a warning is using subcomponents inside a template: input and output signals of each subcomponent in a template should appear at least in one constraint of the father component. -However, it is very common the use of subcomponents to check some property but ignoring the output of the subcomponent. +- Another case where a warning is thrown is when using subcomponents inside a template, since it is required that every input and output signal of each subcomponent in a template should appear in at least one constraint of the father component. -To illustrate this, let us consider the well-known template `Num2Bits(n)` from the circomlib. This template receives an input signal and a parameter `n` which represents a number of bits and returns and output signal array with n elements, the binary representation of the input. +Although this is the common case, specially for inputs, there are cases where some of the outputs of the subcomponent are ignored on purpose as the component is only used to check some properties. To illustrate this, let us consider the well-known template `Num2Bits(n)` from the circomlib. This template receives an input signal and a parameter `n` which represents a number of bits and returns and output signal array with n elements, the binary representation of the input. ``` include "bitify.circom"; @@ -65,14 +79,28 @@ template check_bits(n){ component main = check_bits(10); ``` -It is very common to use the `Num2Bits` template to check if `in` can be represented with `n`bits. In this case, the main component checks if the value of `in` can be represented using 10 bits, and it works as expected. The constraints introduced by the subcomponent check will guarantee that the R1CS system only has a solution if `in` can be represented with 10 bits, but it does not matter which is the specific representation. However, the compiler throws the next warning: +It is quite common to use the `Num2Bits` template just to check if `in` can be represented with `n`bits. In this case, the main component checks if the value of `in` can be represented using 10 bits, and it works as expected. The constraints introduced by the subcomponent `check` will guarantee that the R1CS system only has a solution if `in` can be represented with 10 bits, but it does not matter which is the specific representation. However, the compiler throws the next warning: ``` In template "check_bits(10)": Array of subcomponent input/output signals check.out contains a total of 10 signals that do not appear in any constraint of the father component = For example: check.out[0], check.out[1]. ``` - Since we are not interested in the binary representation, the template does not make use of array signal `check.out`. Thus, we should add `_ <== check.out` to inform that the binary representation is irrelevant and avoid the warning. +``` +template check_bits(n){ + signal input in; + component check = Num2Bits(n); + check.in <== in; + _ <== check.out; +} +``` +or even using annonymous components we can write +``` +template check_bits(n){ + signal input in; + _ <== Num2Bits(n)(in); +} +``` Notice also here that the `--inspect option also shows the parameter of the instance that causes a warning (`check_bits(10)`). In general, we throw as many warnings as instances with different parameters for each template. @@ -98,7 +126,13 @@ In template "parity(10)": Array of subcomponent input/output signals check.out c that do not appear in any constraint of the father component. = For example: check.out[1], check.out[2]. ``` -To fix this example, we can add ` _ <== check.out` at the end of the template to let the compiler know that the remaining positions are irrelevant. +To fix this example, we can either add for loop at the end of the template to indicate those positions that are intendedly not used +``` +for (var i = 1; i < n; i++) { + _ <== check.out[i]; +} +``` +or simply add ` _ <== check.out` at the end of the template to let the compiler know that the remaining positions are irrelevant (as this is not going to affect to the use of check.out[0]). - Finally, the `--inspect` option also searches for assignments with operator `<--` that can be transformed into assignments with operator `<==`, which automatically include the corresponding constraint to guarantee the code is correct. A typical scenario of this situation is shown below: @@ -109,4 +143,3 @@ To fix this example, we can add ` _ <== check.out` at the end of the template to Here, many circom programmers avoid the use of `<==`, since they are using the `/` operator which in many cases turn the expression in non-quadratic. Then, programmers must add the corresponding constraint using `===` to guarantee the code is correct. However, it is important to notice that the inverse of 4 is another field element (which is computed by the compiler), and thus, `in / 4` is a linear expression. Consequently, the previous instructions can be replaced by `out <== in / 4`. In these cases, the compiler suggests to use `<==` instead of `<--`. - From 3d80b1f6771e8613b8fd3d04a8c1259a350302ad Mon Sep 17 00:00:00 2001 From: Albert Rubio <34064782+alrubio@users.noreply.github.com> Date: Wed, 15 Mar 2023 10:18:26 +0100 Subject: [PATCH 57/70] Update inspect.md --- mkdocs/docs/circom-language/code-quality/inspect.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs/docs/circom-language/code-quality/inspect.md b/mkdocs/docs/circom-language/code-quality/inspect.md index 771d5be55..5ae47d319 100644 --- a/mkdocs/docs/circom-language/code-quality/inspect.md +++ b/mkdocs/docs/circom-language/code-quality/inspect.md @@ -4,7 +4,7 @@ description: >- --- # Improving security of circuits by using --inspect option -When using --inspect option, the compiler searches for signals that may be underconstrained. In case it finds some, it throws a warning to let the programmer know whichs are those potentially underconstrained signals. For instance, the compiler throws a warning when some input or output signal of a subcomponent in a template do not appear in any constraint of the father component. In case this is intended, the programmer can use the underscore notation '_' to inform the compiler that such a situation is as expected. A warning is also shown when a signal is not used in any constraint in the component it belongs to. Let us see several cases where we can find that situation. +When using --inspect option, the compiler searches for signals that may be underconstrained. In case it finds some, it throws a warning to let the programmer know which are those potentially underconstrained signals. For instance, the compiler throws a warning when some input or output signal of a subcomponent in a template do not appear in any constraint of the father component. In case this is intended, the programmer can use the underscore notation '_' to inform the compiler that such a situation is as expected. A warning is also shown when a signal is not used in any constraint in the component it belongs to. Let us see several cases where we can find that situation. 1) The compiler throws a warning if a signal defined in a template do not appear in any constraint of such template for the given instantiation. @@ -94,7 +94,7 @@ template check_bits(n){ _ <== check.out; } ``` -or even using annonymous components we can write +or even using anonymous components we can write ``` template check_bits(n){ signal input in; From 248b8d9cdc0e72773cabd6c77743aa14c032eeed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clara=20Rodr=C3=ADguez?= Date: Wed, 15 Mar 2023 12:15:52 +0100 Subject: [PATCH 58/70] Update compiler-messages.md --- mkdocs/docs/circom-language/circom-insight/compiler-messages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs/docs/circom-language/circom-insight/compiler-messages.md b/mkdocs/docs/circom-language/circom-insight/compiler-messages.md index fb2f8bfa2..1c06c0ee7 100644 --- a/mkdocs/docs/circom-language/circom-insight/compiler-messages.md +++ b/mkdocs/docs/circom-language/circom-insight/compiler-messages.md @@ -12,7 +12,7 @@ This message means that it is allowed but uncommon, and hence it is better to ch This message means that it is allowed but should not happen in general. -For instance, if a signal is not used in any constraint, a warning message will be generated (when compiling the program with the `--inspect` option). Moreover, if it is an input signal x, then the compiler would suggest adding a constraint of the form x \* 0 === 0; +For instance, if a signal is not used in any constraint, a warning message will be generated when compiling the circuit with the `--inspect` option. ```text pragma circom 2.0.0; From c29bca5f0e1c464e36e0a5a0ad371c3f39e96179 Mon Sep 17 00:00:00 2001 From: clararod9 Date: Wed, 15 Mar 2023 15:20:44 +0100 Subject: [PATCH 59/70] minor: improving warning message <== --- constraint_generation/src/execute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index 6767c3da5..65ac187fe 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -2366,7 +2366,7 @@ fn treat_result_with_execution_warning( let report = match execution_error { CanBeQuadraticConstraintSingle() => { let msg = format!( - "Consider using <== instead of <-- to add the corresponding quadratic constraint.\n The constraint representing the assignment satisfies the R1CS format and can be added to the constraint system." + "Consider using <== instead of <-- to add the corresponding constraint.\n The constraint representing the assignment satisfies the R1CS format and can be added to the constraint system." ); Report::warning( msg, From d19d87080c0e1a514288dd36e298fb001a2637c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clara=20Rodr=C3=ADguez?= Date: Wed, 15 Mar 2023 15:26:25 +0100 Subject: [PATCH 60/70] Update RELEASES.md --- RELEASES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index b55456bd7..4ab42a98b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,9 +1,9 @@ # Release notes -## March 7, 2023 circom 2.1.5 +## March 15, 2023 circom 2.1.5 #### Extensions - Definition of signals and components can be done now inside if blocks IF the condition is known at compilation time. If the condition is unknown and depends on the value of signals, then the compiler throws an error. -- Improving the --inspect option. It now detects underconstrained signals. +- Improving the --inspect option. It now detects underconstrained signals and assignments using <-- in which <== could be used. - Improving the efficiency of the compiler. It does not execute the inspect and constraint generation phase only if there are not the corresponding flags. - Improving --O1 simplification: removing signals that do not appear in any constraint and avoiding unnecessary constraint normalizations. - Improving parallel: array assignments of outputs and efficiency by updating numThread while waiting and setting maxThreads to 32. @@ -15,7 +15,7 @@ #### Fixed Bugs - Fixing a problem with the memory release of the components (in C). - Fixing a problem with the parallel execution during the witness generation (in C). -- Fixing a bug: functions must be always executed even if they output constants. +- Fixing a bug: functions are executed even if they output signals when they contain logs or asserts. - Fixing a bug: During the witness generation, the computation of expressions like x**x was buggy (in wasm). ## February 10, 2023 circom 2.1.4 From 127414e9088cc017a357233f30f3fd7d91a8906c Mon Sep 17 00:00:00 2001 From: clararod9 Date: Wed, 15 Mar 2023 18:07:58 +0100 Subject: [PATCH 61/70] updating version to v2.1.5 --- Cargo.lock | 20 ++++++++++---------- circom/Cargo.toml | 2 +- code_producers/Cargo.toml | 2 +- compiler/Cargo.toml | 2 +- constraint_generation/Cargo.toml | 2 +- constraint_list/Cargo.toml | 2 +- constraint_writers/Cargo.toml | 2 +- dag/Cargo.toml | 2 +- parser/Cargo.toml | 2 +- program_structure/Cargo.toml | 2 +- type_analysis/Cargo.toml | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbfbbc8cc..062248767 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,7 +155,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "circom" -version = "2.1.4" +version = "2.1.5" dependencies = [ "ansi_term", "clap", @@ -205,7 +205,7 @@ dependencies = [ [[package]] name = "code_producers" -version = "2.1.4" +version = "2.1.5" dependencies = [ "handlebars", "lz_fnv", @@ -234,7 +234,7 @@ dependencies = [ [[package]] name = "compiler" -version = "2.1.4" +version = "2.1.5" dependencies = [ "code_producers", "constant_tracking", @@ -255,7 +255,7 @@ version = "2.0.0" [[package]] name = "constraint_generation" -version = "2.1.4" +version = "2.1.5" dependencies = [ "ansi_term", "circom_algebra", @@ -270,7 +270,7 @@ dependencies = [ [[package]] name = "constraint_list" -version = "2.1.4" +version = "2.1.5" dependencies = [ "circom_algebra", "constraint_writers", @@ -282,7 +282,7 @@ dependencies = [ [[package]] name = "constraint_writers" -version = "2.0.9" +version = "2.1.5" dependencies = [ "circom_algebra", "json", @@ -319,7 +319,7 @@ dependencies = [ [[package]] name = "dag" -version = "2.1.3" +version = "2.1.5" dependencies = [ "circom_algebra", "constraint_list", @@ -655,7 +655,7 @@ checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" [[package]] name = "parser" -version = "2.1.4" +version = "2.1.5" dependencies = [ "lalrpop", "lalrpop-util", @@ -764,7 +764,7 @@ dependencies = [ [[package]] name = "program_structure" -version = "2.1.4" +version = "2.1.5" dependencies = [ "codespan", "codespan-reporting", @@ -1188,7 +1188,7 @@ dependencies = [ [[package]] name = "type_analysis" -version = "2.1.4" +version = "2.1.5" dependencies = [ "num-bigint-dig", "num-traits", diff --git a/circom/Cargo.toml b/circom/Cargo.toml index b54a3559d..3283d799c 100644 --- a/circom/Cargo.toml +++ b/circom/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "circom" -version = "2.1.4" +version = "2.1.5" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/code_producers/Cargo.toml b/code_producers/Cargo.toml index 9a19c504a..8a59f4af6 100644 --- a/code_producers/Cargo.toml +++ b/code_producers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "code_producers" -version = "2.1.4" +version = "2.1.5" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index aa9438958..1d0f7b8cb 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "compiler" -version = "2.1.4" +version = "2.1.5" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/constraint_generation/Cargo.toml b/constraint_generation/Cargo.toml index 3621fd258..475e86d90 100644 --- a/constraint_generation/Cargo.toml +++ b/constraint_generation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "constraint_generation" -version = "2.1.4" +version = "2.1.5" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/constraint_list/Cargo.toml b/constraint_list/Cargo.toml index 41880292f..5f07ffbf7 100644 --- a/constraint_list/Cargo.toml +++ b/constraint_list/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "constraint_list" -version = "2.1.4" +version = "2.1.5" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/constraint_writers/Cargo.toml b/constraint_writers/Cargo.toml index db4303b32..11164af47 100644 --- a/constraint_writers/Cargo.toml +++ b/constraint_writers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "constraint_writers" -version = "2.0.9" +version = "2.1.5" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/dag/Cargo.toml b/dag/Cargo.toml index 4e0d4556f..598674f27 100644 --- a/dag/Cargo.toml +++ b/dag/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dag" -version = "2.1.3" +version = "2.1.5" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 6be5afa58..18cde53fa 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "2.1.4" +version = "2.1.5" authors = ["Costa Group UCM","iden3"] edition = "2018" build = "build.rs" diff --git a/program_structure/Cargo.toml b/program_structure/Cargo.toml index 5ee40ff9c..c41cd7380 100644 --- a/program_structure/Cargo.toml +++ b/program_structure/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "program_structure" -version = "2.1.4" +version = "2.1.5" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/type_analysis/Cargo.toml b/type_analysis/Cargo.toml index 432b5c1ea..7efcaaf9e 100644 --- a/type_analysis/Cargo.toml +++ b/type_analysis/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "type_analysis" -version = "2.1.4" +version = "2.1.5" authors = ["Costa Group UCM","iden3"] edition = "2018" From 5425db30904e5d9cff692d6e6e5cc9558486df1b Mon Sep 17 00:00:00 2001 From: miguelis Date: Wed, 15 Mar 2023 18:19:18 +0100 Subject: [PATCH 62/70] Update mkdocs.yml --- mkdocs/mkdocs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mkdocs/mkdocs.yml b/mkdocs/mkdocs.yml index 52244976d..3c48d601b 100644 --- a/mkdocs/mkdocs.yml +++ b/mkdocs/mkdocs.yml @@ -57,7 +57,8 @@ nav: - Scoping: 'circom-language/scoping.md' - Code Quality: - Code Assertion: 'circom-language/code-quality/code-assertion.md' - - Debugging Operations: 'circom-language/code-quality/debugging-operations.md' + - Debugging Operations:'circom-language/code-quality/debugging-operations.md' + - Inspect Option: 'circom-language/code-quality/inspect.md' - Circom Insight: - Compiler Phases: 'circom-language/circom-insight/circom-phases.md' - Compiler Messages: 'circom-language/circom-insight/compiler-messages.md' @@ -65,4 +66,4 @@ nav: - Circom Library: 'circom-language/circom-insight/circom-library.md' - More circuits: - Basic circuits: 'more-circuits/more-basic-circuits.md' - - Downloads: 'downloads/downloads.md' \ No newline at end of file + - Downloads: 'downloads/downloads.md' From bfd4d1bdd0721c2444a41021c609712a28e541e9 Mon Sep 17 00:00:00 2001 From: miguelis Date: Wed, 15 Mar 2023 18:20:00 +0100 Subject: [PATCH 63/70] Update mkdocs.yml --- mkdocs/mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs/mkdocs.yml b/mkdocs/mkdocs.yml index 3c48d601b..e24123172 100644 --- a/mkdocs/mkdocs.yml +++ b/mkdocs/mkdocs.yml @@ -57,7 +57,7 @@ nav: - Scoping: 'circom-language/scoping.md' - Code Quality: - Code Assertion: 'circom-language/code-quality/code-assertion.md' - - Debugging Operations:'circom-language/code-quality/debugging-operations.md' + - Debugging Operations: 'circom-language/code-quality/debugging-operations.md' - Inspect Option: 'circom-language/code-quality/inspect.md' - Circom Insight: - Compiler Phases: 'circom-language/circom-insight/circom-phases.md' From 8637d684cc8681e18a9f3580838223403f277aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clara=20Rodr=C3=ADguez?= Date: Fri, 24 Mar 2023 17:43:18 +0100 Subject: [PATCH 64/70] Update tags.md --- mkdocs/docs/circom-language/tags.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs/docs/circom-language/tags.md b/mkdocs/docs/circom-language/tags.md index d08b703d9..0911b8788 100644 --- a/mkdocs/docs/circom-language/tags.md +++ b/mkdocs/docs/circom-language/tags.md @@ -1,4 +1,4 @@ -#Signal Tags +# Signal Tags circom 2.1.0 introduces a new feature called __signal tags__. Tags can be defined during the declaration of any signal in a template. The tag list is indicated between brackets right before the signal name. ``` @@ -100,5 +100,5 @@ template Bits2Num(n) { } ``` -###Tags in signal arrays +### Tags in signal arrays Every signal in an array has exactly the same tag value. Then, the tag is accessed directly from the array name instead of accessing from a particular signal in the array. Similarly to the previous erroneous example: if a particular position of the array is modified, then the tag value of the whole array cannot be modified at all. From 20c50afb37c61332d97496cdd4897ddf045091b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clara=20Rodr=C3=ADguez?= Date: Fri, 24 Mar 2023 17:49:38 +0100 Subject: [PATCH 65/70] Update templates-and-components.md --- .../templates-and-components.md | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/mkdocs/docs/circom-language/templates-and-components.md b/mkdocs/docs/circom-language/templates-and-components.md index 4f832ce27..3d58645e9 100644 --- a/mkdocs/docs/circom-language/templates-and-components.md +++ b/mkdocs/docs/circom-language/templates-and-components.md @@ -59,56 +59,6 @@ template wrong (N) { component main {public [a]} = wrong(1); ``` -Regarding the signals defined in the template that will be part of the component, the following compiler messages will be generated if we use the option `--inspect` to compile the code: - -- If a signal is not used in any constraint, a warning message will be generated. Moreover, if it is an input signal x then the compiler would suggest adding a constraint of the form x \* 0 === 0; - -```text -pragma circom 2.0.0; - -template A(N){ - signal input in; - signal intermediate; - signal output out; - intermediate <== 1; - out <== intermediate; -} - -component main {public [in]} = A(1); -``` -During the compilation of this code, we obtain the next warning message: _"In template "A\(1\)". Unconstrained signal. "in" = Maybe use: in\*0 === 0"_ - -- If an intermediary signal is used only in one constraint, a hint message will be generated. - - -```text -pragma circom 2.0.0; - -template A(N){ - signal input in; - signal inter; - inter <== 1; - signal output out; - out <== in; -} -component main {public [in]} = A(1); -``` - -During the compilation of this code, we obtain the next warning message: "_In template "A\(1\)". One constraint intermediate: "inter" = Maybe use: inter\*0 === 0". - -- If there is no output signal a warning message will be generated. - - -```text -pragma circom 2.0.0; - -template A(N){ - signal input in; -} -component main {public [in]} = A(1); -``` - -During the compilation of this code, we obtain the next warning message: _"There is no output signal."_. ## Components From d1c76a22fa9f2257fb27b90185bcb396b1bb2ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clara=20Rodr=C3=ADguez?= Date: Fri, 24 Mar 2023 17:53:10 +0100 Subject: [PATCH 66/70] Update mkdocs.yml --- mkdocs/mkdocs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mkdocs/mkdocs.yml b/mkdocs/mkdocs.yml index e24123172..d893b232b 100644 --- a/mkdocs/mkdocs.yml +++ b/mkdocs/mkdocs.yml @@ -55,6 +55,9 @@ nav: - Control Flow: 'circom-language/control-flow.md' - Data Types: 'circom-language/data-types.md' - Scoping: 'circom-language/scoping.md' + - New features circom 2.1: + - Anonymous Components and tuples: 'circom-language/anonymous-components-and-tuples.md' + - Tags: 'circom-language/tags.md' - Code Quality: - Code Assertion: 'circom-language/code-quality/code-assertion.md' - Debugging Operations: 'circom-language/code-quality/debugging-operations.md' From 436dd417c1ea5c73389ca43103d9bcc5e16649f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Isabel=20M=C3=A1rquez?= Date: Wed, 5 Apr 2023 10:24:51 +0200 Subject: [PATCH 67/70] Updating lalrpop dependencies upto version 0.19.9 --- parser/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 18cde53fa..544970cde 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -7,13 +7,13 @@ build = "build.rs" [build-dependencies] rustc-hex = "2.0.1" -lalrpop = { version = "0.18.1", features = ["lexer"] } +lalrpop = { version = "0.19.9", features = ["lexer"] } num-bigint-dig = "0.6.0" num-traits = "0.2.6" [dependencies] program_structure = {path = "../program_structure"} -lalrpop-util = "0.18.1" +lalrpop-util = "0.19.9" regex = "1.1.2" rustc-hex = "2.0.1" num-bigint-dig = "0.6.0" From eb6b5f8b847b0ae1e6501aca405872a1bc6755c5 Mon Sep 17 00:00:00 2001 From: miguelis Date: Sat, 15 Apr 2023 02:02:13 +0200 Subject: [PATCH 68/70] Changing a constraint in the example to be R1CS. --- .../docs/circom-language/anonymous-components-and-tuples.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs/docs/circom-language/anonymous-components-and-tuples.md b/mkdocs/docs/circom-language/anonymous-components-and-tuples.md index 70e25b48c..97d88410a 100644 --- a/mkdocs/docs/circom-language/anonymous-components-and-tuples.md +++ b/mkdocs/docs/circom-language/anonymous-components-and-tuples.md @@ -178,7 +178,7 @@ template A(n){ signal input a, b, c; signal output d; d <== a*b+c; - a * b === c*c; + a * b === c; } template B(n){ signal input in[n]; @@ -187,7 +187,7 @@ template B(n){ component main = B(3); ``` -In the previous example, we are interesting in adding to the R1CS the constraint `a * b = c * c`, but we can ignore the output signal `d`. +In the previous example, we are interesting in adding to the R1CS the constraint `a * b = c`, but we can ignore the output signal `d`. In case the anonymous component has one more than one output, we can ignore the ones we are not interested. From 3536636fa8d67ee27e70c1bc9b10dfe3f182af8a Mon Sep 17 00:00:00 2001 From: "yoyismee.eth" Date: Tue, 18 Apr 2023 14:03:24 +0700 Subject: [PATCH 69/70] fix tap in templates-and-components.md --- mkdocs/docs/circom-language/templates-and-components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs/docs/circom-language/templates-and-components.md b/mkdocs/docs/circom-language/templates-and-components.md index 3d58645e9..64f7da7fe 100644 --- a/mkdocs/docs/circom-language/templates-and-components.md +++ b/mkdocs/docs/circom-language/templates-and-components.md @@ -142,7 +142,7 @@ template MultiAND(n) { out <== and.out; } else { and = AND(); - var n1 = n\2; + var n1 = n\2; var n2 = n-n\2; ands[0] = MultiAND(n1); ands[1] = MultiAND(n2); From e8b9db2a30dc1e63d4fc9dfc0797389de04c8bc8 Mon Sep 17 00:00:00 2001 From: hecmas Date: Thu, 20 Apr 2023 16:27:36 +0200 Subject: [PATCH 70/70] Added documentation about missing primes and minor typos fixed --- mkdocs/docs/background/background.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/mkdocs/docs/background/background.md b/mkdocs/docs/background/background.md index bc94e7b7f..87aea0bb9 100644 --- a/mkdocs/docs/background/background.md +++ b/mkdocs/docs/background/background.md @@ -8,7 +8,7 @@ description: >- ## Zero-knowledge proofs -Recently, a set of cryptographic primitives called **zero-knowledge proofs** \(ZKPs\) agitated the world of public blockchains and distributed ledgers. ZKPs came up first as a **solution to privacy** issues but they have lately also stood up as a perfect **solution to scalability** issues. As a result, these cryptographic proofs have become very attractive tools to the blockchain community, and the most efficient algorithms have already been deployed and integrated in several applications. +Recently, a set of cryptographic primitives called **zero-knowledge proofs** (ZKPs) agitated the world of public blockchains and distributed ledgers. ZKPs came up first as a **solution to privacy** issues but they have lately also stood up as a perfect **solution to scalability** issues. As a result, these cryptographic proofs have become very attractive tools to the blockchain community, and the most efficient algorithms have already been deployed and integrated in several applications. A zero-knowledge proof is a protocol that enables one party, called the **prover**, to convince another, the **verifier**, that a statement is true without revealing any information beyond the veracity of the statement. For example, a prover can create proofs for statements like the following: @@ -17,9 +17,9 @@ A zero-knowledge proof is a protocol that enables one party, called the **prover * _"I know the preimage of this hash value"_ : in this case, the proof would show that the prover knows the preimage but it would not reveal any information about the value of that preimage. * _"This is the hash of a blockchain block that does not produce negative balances"_ : in this case, the proof would not reveal any information about the amount, origin or destination of the transactions included in the block. -**Non-interactive zero-knowledge proofs** \(NIZK\) are a particular type of zero-knowledge proofs in which the prover can generate the proof without interaction with the verifier. NIZK protocols are very suitable for Ethereum blockchain applications, because they **allow a smart contract to act as a verifier**. This way, anyone can generate a proof and send it as part of a transaction to the smart contract, which can perform some action depending on whether the proof is valid or not. +**Non-interactive zero-knowledge proofs** (NIZK) proofs are a particular type of zero-knowledge proofs in which the prover can generate the proof without interaction with the verifier. NIZK protocols are very suitable for Ethereum blockchain applications, because they **allow a smart contract to act as a verifier**. This way, anyone can generate a proof and send it as part of a transaction to the smart contract, which can perform some action depending on whether the proof is valid or not. -In this context, the most preferable NIZK are **zk-SNARK** proofs \(Zero-knowledge Succinct Non Interactive ARgument of Knowledge\), a set of non-interactive zero-knowledge protocols that have **succinct proof size** and **sublinear verification time**. The importance of these protocols is double: on the one hand, they help improve privacy guarantees, but on the other, their small proof size has been used in scalability solutions. +In this context, the most preferable NIZK proofs are **zk-SNARK** proofs (Zero-knowledge Succinct Non Interactive ARgument of Knowledge), a set of non-interactive zero-knowledge protocols that have **succinct proof size** and **sublinear verification time**. The importance of these protocols is double: on the one hand, they help improve privacy guarantees, but on the other, their small proof size has been used in scalability solutions. ## Arithmetic circuits @@ -44,6 +44,13 @@ To generate and validate zk-SNARK proofs in **Ethereum**, we need to work with ` ```text p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 ``` +👉 This prime `p` is the order of the scalar field of the BN254 curve (also known as the ALT_BN128 curve) as defined in [EIP 196](https://eips.ethereum.org/EIPS/eip-196). + +👉 Circom 2.0.6 introduces two new prime numbers to work with, namely the order of the scalar field of the [BLS12-381](https://electriccoin.co/blog/new-snark-curve/) +```text +52435875175126190479447740508185965837690552500527637822603658699938581184513 +``` +and the goldilocks prime `18446744069414584321`, originally used in [Plonky2](https://github.com/mir-protocol/plonky2). In the figure below, we have defined an `F_7`-arithmetic circuit that performs the operation: `out = a*b + c`. The circuit has 5 signals: the signals `a`, `b` and `c` are input signals, `d` is an intermediate signal and the`out` signal is the output of the circuit. @@ -59,7 +66,7 @@ If we have an arithmetic circuit with signals `s_1,...,s_n`, then we define a ** `(a_1*s_1 + ... + a_n*s_n) * (b_1*s_1 + ... + b_n*s_n) + (c_1*s_1 + ... + c_n*s_n) = 0` -Note that constraint **must be quadratic, linear or constant equations**, and sometimes, by doing small modifications \(like a change of variable or gathering two constraints\), it is possible to reduce the number of constraints or variables. In general, circuits will have several constraints \(typically, one per multiplicative gate\). The set of constraints describing the circuit is called **rank-1 constraint system** \(R1CS\): +Note that constraint **must be quadratic, linear or constant equations**, and sometimes, by doing small modifications (like a change of variable or gathering two constraints), it is possible to reduce the number of constraints or variables. In general, circuits will have several constraints (typically, one per multiplicative gate). The set of constraints describing the circuit is called **rank-1 constraint system** (R1CS): `(a_11*s_1 + ... + a_1n*s_n)*(b_11*s_1 + ... + b_1n*s_n) + (c_11*s_1 + ... + c_1n*s_n) = 0 ` @@ -102,5 +109,5 @@ An assignment of the signals is called a **witness**. For example, `{a = 2, b = ## Summary -​**In summary, zk-SNARK proofs are an specific type of zero-knowledge proofs that allow you to prove that you know a set of signals \(witness\) that match all the constraints of a circuit without revealing any of the signals except the public inputs and the outputs.** +​**In summary, zk-SNARK proofs are an specific type of zero-knowledge proofs that allow you to prove that you know a set of signals (witness) that match all the constraints of a circuit without revealing any of the signals except the public inputs and the outputs.**