diff --git a/RELEASES.md b/RELEASES.md index 45fdf5d89..4ab42a98b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,4 +1,22 @@ # Release notes +## 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 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. +- Handling better the parser errors and improving error messages to output more information. (parser, type checking and constraint generation errors). +- 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 (in C). +- Fixing a problem with the parallel execution during the witness generation (in C). +- 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 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..f760981db 100644 --- a/compiler/src/hir/sugar_cleaner.rs +++ b/compiler/src/hir/sugar_cleaner.rs @@ -267,16 +267,61 @@ 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); + + + } else{ + *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..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); @@ -194,7 +195,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..65ac187fe 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)>; @@ -76,7 +77,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, + } } } @@ -87,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(); @@ -105,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 } @@ -115,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."); @@ -135,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) => { @@ -157,25 +166,27 @@ 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, ()> { + flags: FlagsExecution, +) -> 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, actual_node, - flag_verbose, + flags, false )?; debug_assert!(possible_fold.is_none()); @@ -196,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!())); } @@ -248,13 +259,16 @@ 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) { + 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), @@ -263,19 +277,19 @@ fn execute_statement( &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 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), - ); - + let full_symbol = format!("{}{}", + 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( @@ -291,27 +305,46 @@ fn execute_statement( let ctr = AExpr::transform_expression_to_constraint_form(expr, &p).unwrap(); node.add_constraint(ctr); } - } - else if let AssignOp::AssignSignal = op { + } 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, - )?; + needs_double_arrow.push(full_symbol); } } } - } + + 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 err : Result<(),ExecutionWarning> = + Result::Err(ExecutionWarning::CanBeQuadraticConstraintMultiple(needs_double_arrow)); + + treat_result_with_execution_warning( + 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!()); @@ -362,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); @@ -373,38 +406,40 @@ 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, program_archive, runtime, actual_node, - flag_verbose + flags )?; + 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, program_archive, runtime, actual_node, - flag_verbose + flags )?; + 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, program_archive, runtime, actual_node, - flag_verbose + flags )?; break returned; } else if !condition_result.unwrap() { @@ -413,17 +448,19 @@ fn execute_statement( }, Block { stmts, .. } => { ExecutionEnvironment::add_variable_block(&mut runtime.environment); - let return_value = - execute_sequence_of_statements(stmts, program_archive, runtime, actual_node, flag_verbose, false)?; + let (return_value, can_simplify_block) = + 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, .. } => { - if flag_verbose{ + can_be_simplified = false; + 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); @@ -441,16 +478,27 @@ 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, flags)?; + let _arith = safe_unwrap_to_single_arithmetic_expression(f_result, line!()); + } + } } 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 { 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, @@ -460,40 +508,41 @@ 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){ - 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 } }; - Result::Ok(res) + Result::Ok((res, can_be_simplified)) } 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; let res = match expr { Number(_, value) => { let a_value = AExpr::Number { value: value.clone() }; @@ -502,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!(); } @@ -514,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); } @@ -544,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 @@ -552,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]; @@ -580,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)?; @@ -589,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)?; @@ -597,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)); @@ -613,10 +662,12 @@ 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, 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() } @@ -626,7 +677,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() && can_be_simplified{ let value = AExpressionSlice::unwrap_to_single(slice); Analysis::computed(&mut runtime.analysis, expr_id, value); } @@ -642,11 +693,11 @@ fn execute_call( args: &Vec, program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool, -) -> Result { + 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 @@ -659,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; @@ -669,7 +720,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)) } } @@ -679,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); @@ -691,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; @@ -784,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); @@ -1102,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!()); @@ -1192,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!()); @@ -1283,33 +1334,34 @@ fn execute_conditional_statement( program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, actual_node: &mut Option, - flag_verbose: bool, -) -> Result<(Option, Option), ()> { - let f_cond = execute_expression(condition, program_archive, runtime, flag_verbose)?; + flags: FlagsExecution, +) -> Result<(Option, bool, Option), ()> { + 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 = 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)? + execute_statement(else_stmt, program_archive, runtime, actual_node, flags)? } - None if !cond_bool_value => None, - _ => execute_statement(true_case, program_archive, runtime, actual_node, flag_verbose)?, + None if !cond_bool_value => (None, true), + _ => execute_statement(true_case, program_archive, runtime, actual_node, flags)?, }; - 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 = 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 = 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; } } runtime.block_type = previous_block_type; - return Result::Ok((ret_value, Option::None)); + return Result::Ok((ret_value, can_simplify, Option::None)); } } @@ -1318,31 +1370,33 @@ 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, ()> { +) -> 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, flags)?; + 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)?; + execute_delayed_declarations(program_archive, runtime, actual_node, flags)?; } - Result::Ok(Option::None) + Result::Ok((Option::None, can_be_simplified)) } 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!())); } @@ -1408,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() }); @@ -1441,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() }); @@ -1521,7 +1575,7 @@ fn signal_to_arith(symbol: String, slice: SignalSlice) -> Result 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,44 +1622,40 @@ fn execute_component( &runtime.call_trace, )?; let resulting_component = safe_unwrap_to_single(slice_result, line!()); - let read_result = if resulting_component.is_ready_initialize() { - Result::Ok(resulting_component) - } else { - Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedComponent)) - }; - let checked_component = treat_result_with_memory_error( - read_result, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; + if let Some(acc) = access_information.tag_access { - if let Result::Ok(tags) = checked_component.get_signal(&access_information.signal_access.unwrap()) { - let btree_map = tags.0; - if btree_map.contains_key(&acc) { - let value_tag = btree_map.get(&acc).unwrap(); - if let Some(value_tag) = value_tag { - let a_value = AExpr::Number { value: value_tag.clone() }; - let ae_slice = AExpressionSlice::new(&a_value); - Result::Ok(FoldedValue { arithmetic_slice: Option::Some(ae_slice), ..FoldedValue::default() }) - } - else { - let error = MemoryError::TagValueNotInitializedAccess; - treat_result_with_memory_error( - Result::Err(error), - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )? - } + let (tags_signal, _) = treat_result_with_memory_error( + resulting_component.get_signal(&access_information.signal_access.unwrap()), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + if tags_signal.contains_key(&acc) { + let value_tag = tags_signal.get(&acc).unwrap(); + if let Some(value_tag) = value_tag { + let a_value = AExpr::Number { value: value_tag.clone() }; + let ae_slice = AExpressionSlice::new(&a_value); + Result::Ok(FoldedValue { arithmetic_slice: Option::Some(ae_slice), ..FoldedValue::default() }) } - else { unreachable!() } - } else { unreachable!()} + else { + let error = MemoryError::TagValueNotInitializedAccess; + treat_result_with_memory_error( + Result::Err(error), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + } + } else { + unreachable!() + } + } else if let Option::Some(signal_name) = &access_information.signal_access { let access_after_signal = &access_information.after_signal; let (tags_signal, signal) = treat_result_with_memory_error( - checked_component.get_signal(signal_name), + resulting_component.get_signal(signal_name), meta, &mut runtime.runtime_errors, &runtime.call_trace, @@ -1631,6 +1681,19 @@ fn execute_component( &runtime.call_trace, ) } else { + let read_result = if resulting_component.is_ready_initialize() { + Result::Ok(resulting_component) + } else { + Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedComponent)) + }; + + let checked_component = treat_result_with_memory_error( + read_result, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + Result::Ok(FoldedValue { node_pointer: checked_component.node_pointer, is_parallel: Some(false), @@ -1663,17 +1726,17 @@ fn execute_function_call( id: &str, program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, - flag_verbose: bool -) -> Result { + 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 = - execute_sequence_of_statements(function_body, program_archive, runtime, &mut Option::None, flag_verbose, true)?; + let (function_result, can_be_simplified) = + 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)); - Result::Ok(return_value) + Result::Ok((return_value, can_be_simplified)) } fn execute_template_call( @@ -1682,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![]); @@ -1733,12 +1796,12 @@ 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, &mut node_wrap, - flag_verbose, + flags, true )?; debug_assert!(ret.is_none()); @@ -1865,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; @@ -1876,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); @@ -1965,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, @@ -2052,8 +2115,11 @@ fn treat_result_with_memory_error_void( use ReportCode::RuntimeError; match memory_error { Result::Ok(()) => 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 +2127,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 +2164,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 +2191,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 +2221,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 +2255,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 +2275,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 +2288,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( @@ -2274,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 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 5b6dda57a..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) -> 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()); @@ -132,12 +133,15 @@ 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 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/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..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).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) -> ExportResult { - let exported = exe.export(program, flag_verbose); +fn export(exe: ExecutedProgram, program: ProgramArchive, flags: FlagsExecution) -> ExportResult { + let exported = exe.export(program, flags); 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..edb216b4f 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 analysis = visit_node(node); + 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/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; 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: 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)`. 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..5ae47d319 --- /dev/null +++ b/mkdocs/docs/circom-language/code-quality/inspect.md @@ -0,0 +1,145 @@ +--- +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 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. + +``` +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 (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. +``` +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; + if(n == 2){ + signal aux <== 2; + out <== B()(aux); + } + else{ + out <== 5; + } +} + ``` + +- 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. + +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"; + +template check_bits(n){ + signal input in; + component check = Num2Bits(n); + check.in <== in; +} + +component main = check_bits(10); +``` + +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 anonymous 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. + +- 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 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: + +``` + 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, 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 `<--`. + 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. 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. diff --git a/mkdocs/docs/circom-language/scoping.md b/mkdocs/docs/circom-language/scoping.md index d3c343399..cd956f716 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 output 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 diff --git a/mkdocs/docs/circom-language/signals.md b/mkdocs/docs/circom-language/signals.md index 000fd1aea..38d1b1b0c 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++; } @@ -121,6 +121,3 @@ Signals can only be assigned using the operations `<--` or `<==` (see [Basic ope ```text out[k] <-- (in >> k) & 1; ``` - - - 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. diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 7aabb379d..1325d4857 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -24,7 +24,7 @@ IdentifierListDef : Vec = { let mut v = v; v.push(e); v - } + }, }; Semicolon:() = { @@ -729,7 +729,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);