diff --git a/parser/src/include_logic.rs b/parser/src/include_logic.rs index f58c838..82876fd 100644 --- a/parser/src/include_logic.rs +++ b/parser/src/include_logic.rs @@ -1,6 +1,6 @@ use super::errors::FileOsError; use program_structure::error_definition::Report; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::path::PathBuf; pub struct FileStack { @@ -45,3 +45,109 @@ impl FileStack { } } } + +pub struct IncludesNode { + pub path: PathBuf, + pub custom_gates_pragma: bool, +} + +#[derive(Default)] +pub struct IncludesGraph { + nodes: Vec, + adjacency: HashMap>, + custom_gates_nodes: Vec, +} + +impl IncludesGraph { + pub fn new() -> IncludesGraph { + IncludesGraph::default() + } + + pub fn add_node( + &mut self, + path: PathBuf, + custom_gates_pragma: bool, + custom_gates_usage: bool + ) { + self.nodes.push(IncludesNode { path: path.clone(), custom_gates_pragma }); + if custom_gates_usage { + self.custom_gates_nodes.push(path.clone()); + } + } + + pub fn add_edge(&mut self, path: String) -> Result<(), Report> { + let mut crr = self.nodes.last().unwrap().path.clone(); + crr.pop(); + crr.push(path.clone()); + let path = std::fs::canonicalize(crr) + .map_err(|_| FileOsError { path: path }) + .map_err(|e| FileOsError::produce_report(e))?; + let edges = self.adjacency.entry(path).or_insert(vec![]); + edges.push(self.nodes.len() - 1); + Ok(()) + } + + pub fn get_problematic_paths(&self) -> Vec> { + let mut problematic_paths = Vec::new(); + for file in &self.custom_gates_nodes { + let path_covered = vec![file.clone()]; + let traversed_edges = HashSet::new(); + problematic_paths.append( + &mut self.traverse(file, true, path_covered, traversed_edges) + ); + } + problematic_paths + } + + fn traverse( + &self, + from: &PathBuf, + ok: bool, + path: Vec, + traversed_edges: HashSet<(PathBuf, PathBuf)> + ) -> Vec> { + let mut problematic_paths = Vec::new(); + if let Some(edges) = self.adjacency.get(from) { + for to in edges { + let next = &self.nodes[*to]; + let edge = (from.clone(), next.path.clone()); + if !traversed_edges.contains(&edge) { + let new_path = { + let mut new_path = path.clone(); + new_path.push(next.path.clone()); + new_path + }; + let new_traversed_edges = { + let mut new_traversed_edges = traversed_edges.clone(); + new_traversed_edges.insert((from.clone(), next.path.clone())); + new_traversed_edges + }; + problematic_paths.append( + &mut self.traverse( + &next.path, + ok && next.custom_gates_pragma, + new_path, + new_traversed_edges + ) + ); + } + } + problematic_paths + } else { + if ok { vec![] } else { vec![path] } + } + } + + 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 mut path_covered = format!("{}", path[0]); + for file in &path[1..] { + path_covered = format!("{} -> {}", path_covered, file); + } + path_covered + } +} diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 396f601..d1ec879 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -27,9 +27,9 @@ ParsePragma : Version = { // maybe change to usize instead of BigInt => version, }; -// Pragma to indicate that we are using PLONK with custom gates. +// Pragma to indicate that we are allowing the definition of custom gates. ParseCustomGates : () = { - "pragma" "ultraPlonk" ";" => () + "pragma" "custom_templates" ";" => () } // Includes are added at the start of the file. @@ -77,19 +77,12 @@ pub ParseDefinition : Definition = { Some(a) => build_function(Meta::new(s,e),name,a,args..arge,body), }, - "template" "(" ")" + "template" "(" ")" => match arg_names { None - => build_template(Meta::new(s,e), name, Vec::new(), args..arge, body, parallel.is_some(), false), + => build_template(Meta::new(s,e), name, Vec::new(), args..arge, body, parallel.is_some(), custom_gate.is_some()), Some(a) - => build_template(Meta::new(s,e), name, a, args..arge, body, parallel.is_some(), false), - }, - "custom_gate" "(" ")" - => match arg_names { - None - => build_template(Meta::new(s,e), name, Vec::new(), args..arge, body, parallel.is_some(), true), - Some(a) - => build_template(Meta::new(s,e), name, a, args..arge, body, parallel.is_some(), true), + => build_template(Meta::new(s,e), name, a, args..arge, body, parallel.is_some(), custom_gate.is_some()), }, }; @@ -196,13 +189,6 @@ ParseDeclaration : Statement = { symbols.push(symbol); ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols,AssignOp::AssignVar) }, - "custom_component" ",")*> => { - let mut symbols = symbols; - let meta = Meta::new(s,e); - let xtype = VariableType::Component; - symbols.push(symbol); - ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols,AssignOp::AssignVar) - }, ",")*> => { let mut symbols = symbols; diff --git a/parser/src/lib.rs b/parser/src/lib.rs index ebb807c..438cd2c 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -7,11 +7,11 @@ extern crate lalrpop_util; lalrpop_mod!(pub lang); - mod errors; mod include_logic; mod parser_logic; -use include_logic::FileStack; +use include_logic::{FileStack, IncludesGraph}; +use program_structure::error_code::ReportCode; use program_structure::error_definition::{Report, ReportCollection}; use program_structure::file_definition::{FileLibrary}; use program_structure::program_archive::ProgramArchive; @@ -20,30 +20,41 @@ use std::str::FromStr; pub type Version = (usize, usize, usize); - -pub fn run_parser(file: String, version: &str) -> Result<(ProgramArchive, ReportCollection), (FileLibrary, ReportCollection)> { +pub fn run_parser( + file: String, + version: &str +) -> Result<(ProgramArchive, ReportCollection), (FileLibrary, ReportCollection)> { let mut file_library = FileLibrary::new(); let mut definitions = Vec::new(); let mut main_components = Vec::new(); let mut file_stack = FileStack::new(PathBuf::from(file)); + let mut includes_graph = IncludesGraph::new(); let mut warnings = Vec::new(); while let Some(crr_file) = FileStack::take_next(&mut file_stack) { - let (path, src) = open_file(crr_file).map_err(|e| (file_library.clone(), vec![e]))?; + let (path, src) = open_file(crr_file.clone()) + .map_err(|e| (file_library.clone(), vec![e]))?; 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(), vec![e]))?; if let Some(main) = program.main_component { main_components.push((file_id, main)); } + includes_graph.add_node(crr_file, program.custom_gates, program.custom_gates_declared); let includes = program.includes; definitions.push((file_id, program.definitions)); for include in includes { - FileStack::add_include(&mut file_stack, include) + FileStack::add_include(&mut file_stack, include.clone()) .map_err(|e| (file_library.clone(), vec![e]))?; + includes_graph.add_edge(include).map_err(|e| (file_library.clone(), vec![e]))?; } - warnings.append(&mut check_number_version(path, program.compiler_version, parse_number_version(version)).map_err(|e| (file_library.clone(), vec![e]))?); + warnings.append( + &mut check_number_version( + path, + program.compiler_version, + parse_number_version(version) + ).map_err(|e| (file_library.clone(), vec![e]))? + ); } if main_components.len() == 0 { @@ -53,21 +64,34 @@ pub fn run_parser(file: String, version: &str) -> Result<(ProgramArchive, Report let report = errors::MultipleMainError::produce_report(); Err((file_library, vec![report])) } else { - let (main_id, main_component) = main_components.pop().unwrap(); - let result_program_archive = - ProgramArchive::new(file_library, main_id, main_component, definitions); - match result_program_archive { - Err((lib, rep)) => { - Err((lib, rep)) - } - Ok(program_archive) => { - Ok((program_archive, warnings)) + let errors: ReportCollection = includes_graph.get_problematic_paths().iter().map(|path| + Report::error( + format!( + "Missing custom gates' pragma in the following chain of includes {}", + IncludesGraph::display_path(path) + ), + ReportCode::CustomGatesPragmaError + ) + ).collect(); + if errors.len() > 0 { + Err((file_library, errors)) + } else { + let (main_id, main_component) = main_components.pop().unwrap(); + let result_program_archive = + ProgramArchive::new(file_library, main_id, main_component, definitions); + match result_program_archive { + Err((lib, rep)) => { + Err((lib, rep)) + } + Ok(program_archive) => { + Ok((program_archive, warnings)) + } } } } } -fn open_file(path: PathBuf) -> Result<(String, String), Report> /* path, src*/ { +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); @@ -77,28 +101,39 @@ fn open_file(path: PathBuf) -> Result<(String, String), Report> /* path, src*/ { .map_err(|e| FileOsError::produce_report(e)) } -fn parse_number_version(version: &str) -> Version{ +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()) } -fn check_number_version(file_path: String, version_file: Option, version_compiler: Version) -> Result{ +fn check_number_version( + file_path: String, + version_file: Option, + 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.2 <= version_compiler.2{ + && required_version.2 <= version_compiler.2 { Ok(vec![]) - } - else{ - let report = CompilerVersionError::produce_report(CompilerVersionError{path: file_path, required_version: required_version, version: version_compiler}); + } else { + let report = CompilerVersionError::produce_report( + CompilerVersionError{ + path: file_path, + required_version, + version: version_compiler + } + ); Err(report) } - } - else{ - let report = NoCompilerVersionWarning::produce_report(NoCompilerVersionWarning{path: file_path, version: version_compiler}); + } else { + let report = NoCompilerVersionWarning::produce_report( + NoCompilerVersionWarning{ + path: file_path, + version: version_compiler + } + ); Ok(vec![report]) } -} \ No newline at end of file +} diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index 8dc67c4..7e6caf5 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -81,6 +81,7 @@ pub struct AST { pub meta: Meta, pub compiler_version: Option, pub custom_gates: bool, + pub custom_gates_declared: bool, pub includes: Vec, pub definitions: Vec, pub main_component: Option, @@ -94,7 +95,18 @@ impl AST { definitions: Vec, main_component: Option, ) -> AST { - AST { meta, compiler_version, custom_gates, includes, definitions, main_component } + let custom_gates_declared = definitions.iter().any( + |definition| matches!(definition, Definition::Template { is_custom_gate: true, .. }) + ); + AST { + meta, + compiler_version, + custom_gates, + custom_gates_declared, + includes, + definitions, + main_component + } } } diff --git a/program_structure/src/program_library/error_code.rs b/program_structure/src/program_library/error_code.rs index 9d5c6b7..041df2d 100644 --- a/program_structure/src/program_library/error_code.rs +++ b/program_structure/src/program_library/error_code.rs @@ -68,6 +68,10 @@ pub enum ReportCode { OneConstraintIntermediate, NoOutputInInstance, ErrorWat2Wasm, + CustomGateIntermediateSignalWarning, + CustomGateConstraint, + CustomGateSubComponent, + CustomGatesPragmaError, } impl fmt::Display for ReportCode { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -138,6 +142,10 @@ impl fmt::Display for ReportCode { OneConstraintIntermediate => "CA02", NoOutputInInstance => "CA03", ErrorWat2Wasm => "W01", + CustomGateIntermediateSignalWarning => "CG01", + CustomGateConstraint => "CG02", + CustomGateSubComponent => "CG03", + CustomGatesPragmaError => "CG04", }; 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 new file mode 100644 index 0000000..44d29d7 --- /dev/null +++ b/type_analysis/src/analyzers/custom_gate_analysis.rs @@ -0,0 +1,118 @@ +use program_structure::ast::*; +use program_structure::error_code::ReportCode; +use program_structure::error_definition::{Report, ReportCollection}; + +pub fn custom_gate_analysis( + custom_gate_name: &str, + custom_gate_body: &Statement +) -> Result { + fn custom_gate_analysis( + custom_gate_name: &str, + stmt: &Statement, + errors: &mut ReportCollection, + warnings: &mut ReportCollection + ) { + use Statement::*; + match stmt { + IfThenElse { if_case, else_case, .. } => { + custom_gate_analysis(custom_gate_name, if_case, warnings, errors); + if let Some(else_case_s) = else_case { + custom_gate_analysis(custom_gate_name, else_case_s, errors, warnings); + } + } + While { stmt, .. } => { + custom_gate_analysis(custom_gate_name, stmt, errors, warnings); + } + InitializationBlock { initializations, .. } => { + for stmt in initializations { + custom_gate_analysis(custom_gate_name, stmt, errors, warnings); + } + } + Declaration { meta, xtype, name, .. } => { + use VariableType::*; + match xtype { + Signal(SignalType::Intermediate, _) => { + let mut warning = Report::warning( + String::from("Intermediate signal inside custom gate"), + ReportCode::CustomGateIntermediateSignalWarning + ); + warning.add_primary( + meta.location.clone(), + meta.file_id.unwrap(), + format!( + "Intermediate signal {} declared in custom gate {}", + name, + custom_gate_name + ) + ); + warnings.push(warning); + } + Component => { + let mut error = Report::error( + String::from("Component inside custom gate"), + ReportCode::CustomGateSubComponent + ); + error.add_primary( + meta.location.clone(), + meta.file_id.unwrap(), + format!( + "Component {} declared in custom gate {}", + name, + custom_gate_name + ) + ); + errors.push(error); + } + _ => {} + } + } + Substitution { meta, op, .. } => { + use AssignOp::*; + match op { + AssignConstraintSignal => { + let mut error = Report::error( + String::from("Added constraint inside custom gate"), + ReportCode::CustomGateConstraint + ); + error.add_primary( + meta.location.clone(), + meta.file_id.unwrap(), + String::from("Added constraint") + ); + errors.push(error); + } + _ => {} + } + } + ConstraintEquality { meta, .. } => { + let mut error = Report::error( + String::from("Added constraint inside custom gate"), + ReportCode::CustomGateConstraint + ); + error.add_primary( + meta.location.clone(), + meta.file_id.unwrap(), + String::from("Added constraint") + ); + errors.push(error); + } + Block { stmts, .. } => { + for stmt in stmts { + custom_gate_analysis(custom_gate_name, stmt, errors, warnings); + } + } + _ => {} + }; + } + + let mut warnings = vec![]; + let mut errors = vec![]; + + custom_gate_analysis(custom_gate_name, custom_gate_body, &mut warnings, &mut errors); + + if errors.is_empty() { + Result::Ok(warnings) + } else { + Result::Err(errors) + } +} diff --git a/type_analysis/src/analyzers/mod.rs b/type_analysis/src/analyzers/mod.rs index f077a6a..6c373dc 100644 --- a/type_analysis/src/analyzers/mod.rs +++ b/type_analysis/src/analyzers/mod.rs @@ -1,3 +1,4 @@ +pub use custom_gate_analysis::custom_gate_analysis; pub use functions_all_paths_with_return_statement::all_paths_with_return_check; pub use functions_free_of_template_elements::free_of_template_elements; pub use no_returns_in_template::free_of_returns; @@ -7,6 +8,7 @@ pub use tag_analysis::tag_analysis; pub use type_check::type_check; pub use unknown_known_analysis::unknown_known_analysis; +pub mod custom_gate_analysis; pub mod functions_all_paths_with_return_statement; pub mod functions_free_of_template_elements; pub mod no_returns_in_template; diff --git a/type_analysis/src/analyzers/symbol_analysis.rs b/type_analysis/src/analyzers/symbol_analysis.rs index da311b9..9411275 100644 --- a/type_analysis/src/analyzers/symbol_analysis.rs +++ b/type_analysis/src/analyzers/symbol_analysis.rs @@ -32,9 +32,6 @@ pub fn check_naming_correctness(program_archive: &ProgramArchive) -> Result<(), ); instances.push(instance); } - { - } - if let Err(mut r) = analyze_main(program_archive) { reports.append(&mut r); } @@ -67,7 +64,7 @@ fn analyze_main(program: &ProgramArchive) -> Result<(), Vec> { let mut reports = vec![]; if let Expression::Call { id, .. } = call { if program.contains_template(id) { - let inputs = program.get_template_data(id).get_inputs(); + let inputs = program.get_template_data(id).get_inputs(); for signal in signals { if !inputs.contains_key(signal) { let mut report = Report::error( diff --git a/type_analysis/src/check_types.rs b/type_analysis/src/check_types.rs index 2573ddc..a300352 100644 --- a/type_analysis/src/check_types.rs +++ b/type_analysis/src/check_types.rs @@ -125,16 +125,23 @@ fn function_level_decorators(program_archive: &mut ProgramArchive, reports: &mut fn semantic_analyses( program_archive: &ProgramArchive, errors: &mut ReportCollection, - _warnings: &mut ReportCollection, + warnings: &mut ReportCollection, ) { for template_name in program_archive.get_template_names().iter() { - let result_0 = unknown_known_analysis(template_name, program_archive); - let result_1 = tag_analysis(template_name, program_archive); - if let Result::Err(mut unknown_known_report) = result_0 { - errors.append(&mut unknown_known_report); - } - if let Result::Err(mut tag_analysis_reports) = result_1 { - errors.append(&mut tag_analysis_reports); + if let Result::Err(mut unknown_known_report) = + unknown_known_analysis(template_name, program_archive) { + errors.append(&mut unknown_known_report); + } + if let Result::Err(mut tag_analysis_reports) = + tag_analysis(template_name, program_archive) { + errors.append(&mut tag_analysis_reports); + } + if program_archive.get_template_data(template_name).is_custom_gate() { + let body = program_archive.get_template_data(template_name).get_body(); + match custom_gate_analysis(template_name, body) { + Result::Ok(mut custom_gate_report) => warnings.append(&mut custom_gate_report), + Result::Err(mut custom_gate_report) => errors.append(&mut custom_gate_report) + } } } }