diff --git a/Cargo.lock b/Cargo.lock index 77a72a2ad..0c3ab8a82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,7 +163,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "circom" -version = "2.1.8" +version = "2.1.9" dependencies = [ "ansi_term", "assert_cmd", @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "code_producers" -version = "2.1.8" +version = "2.1.9" dependencies = [ "ansi_term", "handlebars", @@ -263,7 +263,7 @@ dependencies = [ [[package]] name = "compiler" -version = "2.1.8" +version = "2.1.9" dependencies = [ "code_producers", "constant_tracking", @@ -303,7 +303,7 @@ version = "2.0.0" [[package]] name = "constraint_generation" -version = "2.1.8" +version = "2.1.9" dependencies = [ "ansi_term", "circom_algebra", @@ -972,7 +972,7 @@ dependencies = [ [[package]] name = "parser" -version = "2.1.8" +version = "2.1.9" dependencies = [ "lalrpop", "lalrpop-util", @@ -1129,7 +1129,7 @@ dependencies = [ [[package]] name = "program_structure" -version = "2.1.8" +version = "2.1.9" dependencies = [ "codespan", "codespan-reporting", @@ -1556,7 +1556,7 @@ checksum = "683ba5022fe6dbd7133cad150478ccf51bdb6d861515181e5fc6b4323d4fa424" [[package]] name = "type_analysis" -version = "2.1.8" +version = "2.1.9" dependencies = [ "num-bigint-dig", "num-traits", diff --git a/RELEASES.md b/RELEASES.md index a1bbed444..182e2b6ed 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,18 @@ # Release notes +## April 23, 2024 circom 2.1.9 +#### Extensions +- Improvement of error messages: providing more descriptive messages. +- Improvement of documentation and new sections: detailed explanation of the applied simplification techniques and precise description of constraint-related output formats. +- Allowing arbitrary number of different input names in the main component. + +#### Fixed bugs +- Removing non-determinism in R1CS generation: fixing assignment order of anonymous components inputs. +- Fixing an error when computing array accesses involving complex expressions in multidimensional arrays. +- Improving known/unknown analysis: arrays of vars combining known/unknown positions. +- Fixing minor panics: main component not calling to a template, anonymous component with wrong number of arguments, log messages containing line breaks. + + ## Jan 17, 2024 circom 2.1.8 #### Extensions diff --git a/circom/Cargo.toml b/circom/Cargo.toml index ff7ae85df..809511f88 100644 --- a/circom/Cargo.toml +++ b/circom/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "circom" -version = "2.1.8" +version = "2.1.9" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/circom/src/parser_user.rs b/circom/src/parser_user.rs index cd93e3b52..c77d711b9 100644 --- a/circom/src/parser_user.rs +++ b/circom/src/parser_user.rs @@ -1,4 +1,5 @@ use super::input_user::Input; +use program_structure::constants::UsefulConstants; use program_structure::error_definition::Report; use program_structure::program_archive::ProgramArchive; use crate::VERSION; @@ -6,7 +7,9 @@ use crate::VERSION; pub fn parse_project(input_info: &Input) -> Result { let initial_file = input_info.input_file().to_string(); - let result_program_archive = parser::run_parser(initial_file, VERSION, input_info.get_link_libraries().to_vec()); + //We get the prime number from the input + let prime = UsefulConstants::new(&input_info.prime()).get_p().clone(); + let result_program_archive = parser::run_parser(initial_file, VERSION, input_info.get_link_libraries().to_vec(), &prime); match result_program_archive { Result::Err((file_library, report_collection)) => { Report::print_reports(&report_collection, &file_library); diff --git a/circom_algebra/src/algebra.rs b/circom_algebra/src/algebra.rs index 5a374b92c..2a941db12 100644 --- a/circom_algebra/src/algebra.rs +++ b/circom_algebra/src/algebra.rs @@ -555,13 +555,13 @@ impl ArithmeticExpression { } // Bit operations - pub fn complement_256( + pub fn complement_254( elem: &ArithmeticExpression, field: &BigInt, ) -> ArithmeticExpression { use ArithmeticExpression::*; if let Number { value } = elem { - Number { value: modular_arithmetic::complement_256(value, field) } + Number { value: modular_arithmetic::complement_254(value, field) } } else { NonQuadratic } diff --git a/circom_algebra/src/modular_arithmetic.rs b/circom_algebra/src/modular_arithmetic.rs index 6fdfce35a..f1d5790f2 100644 --- a/circom_algebra/src/modular_arithmetic.rs +++ b/circom_algebra/src/modular_arithmetic.rs @@ -92,19 +92,20 @@ pub fn multi_inv(values: &Vec, field: &BigInt) -> Vec{ //Bit operations -// 256 bit complement -pub fn complement_256(elem: &BigInt, field: &BigInt) -> BigInt { +// 254 bit complement +pub fn complement_254(elem: &BigInt, field: &BigInt) -> BigInt { let (sign, mut bit_repr) = bit_representation(elem); - while bit_repr.len() > 256 { + let new_sign = if elem == &BigInt::from(0) { Sign::Plus } else { sign}; + while bit_repr.len() > 254 { bit_repr.pop(); } - for _i in bit_repr.len()..256 { + for _i in bit_repr.len()..254 { bit_repr.push(0); } for bit in &mut bit_repr { *bit = if *bit == 0 { 1 } else { 0 }; } - let cp = BigInt::from_radix_le(sign, &bit_repr, 2).unwrap(); + let cp = BigInt::from_radix_le(new_sign, &bit_repr, 2).unwrap(); modulus(&cp, field) } @@ -252,8 +253,8 @@ mod tests { .expect("generating the big int was not possible"); let big_num = BigInt::parse_bytes("1234".as_bytes(), 10) .expect("generating the big int was not possible"); - let big_num_complement = complement_256(&big_num, &field); - let big_num_complement_complement = complement_256(&big_num_complement, &field); + let big_num_complement = complement_254(&big_num, &field); + let big_num_complement_complement = complement_254(&big_num_complement, &field); let big_num_modulus = modulus(&big_num, &field); assert_eq!(big_num_complement_complement, big_num_modulus); } diff --git a/code_producers/Cargo.toml b/code_producers/Cargo.toml index e1926bfc4..a0f4854c4 100644 --- a/code_producers/Cargo.toml +++ b/code_producers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "code_producers" -version = "2.1.8" +version = "2.1.9" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/code_producers/src/c_elements/c_code_generator.rs b/code_producers/src/c_elements/c_code_generator.rs index 787a0a016..fdccfc7af 100644 --- a/code_producers/src/c_elements/c_code_generator.rs +++ b/code_producers/src/c_elements/c_code_generator.rs @@ -22,8 +22,8 @@ const S_IO_DEF: &str = "IODef"; const IO_DEF_FIELDS: [&str; 2] = ["offset", "lengths"]; // Global variables -pub const SIZE_INPUT_HASHMAP: usize = 256; -const G_INPUT_HASHMAP: &str = "inputHashMap"; // type HashSignalInfo[256] +//pub const SIZE_INPUT_HASHMAP: usize = 256; +const G_INPUT_HASHMAP: &str = "inputHashMap"; // type HashSignalInfo[max(256,needed)] const G_RM_INPUT_SIGNAL_COUNTER: &str = "remainingInputSignalCounter"; // type u32 const G_INPUT_SIGNAL_SET: &str = "inputSignalSetMap"; // type bool[M] const G_WITNESS_TO_SIGNAL: &str = "witness2signalList"; // type u64[W] @@ -469,15 +469,14 @@ pub fn collect_function_headers(functions: Vec) -> Vec { //--------------- generate all kinds of Data for the .dat file --------------- -pub fn generate_hash_map(signal_name_list: &Vec<(String, usize, usize)>) -> Vec<(u64, u64, u64)> { - assert!(signal_name_list.len() <= 256); - let len = 256; - let mut hash_map = vec![(0, 0, 0); len]; +pub fn generate_hash_map(signal_name_list: &Vec<(String, usize, usize)>, size: usize) -> Vec<(u64, u64, u64)> { + assert!(signal_name_list.len() <= size); + let mut hash_map = vec![(0, 0, 0); size]; for i in 0..signal_name_list.len() { let h = hasher(&signal_name_list[i].0); - let mut p = (h % 256) as usize; + let mut p = h as usize % size; while hash_map[p].1 != 0 { - p = (p + 1) % 256; + p = (p + 1) % size; } hash_map[p] = (h, signal_name_list[i].1 as u64, signal_name_list[i].2 as u64); } @@ -653,9 +652,9 @@ pub fn generate_dat_file(dat_file: &mut dyn Write, producer: &CProducer) -> std: //dfile.flush()?; let aux = producer.get_main_input_list(); - let map = generate_hash_map(&aux); + let map = generate_hash_map(&aux,producer.get_input_hash_map_entry_size()); let hashmap = generate_dat_from_hash_map(&map); //bytes u64 --> u64 - //let hml = 256 as u32; + //let hml = producer.get_input_hash_map_entry_size() as u32; //dfile.write_all(&hml.to_be_bytes())?; dat_file.write_all(&hashmap)?; //dat_file.flush()?; @@ -946,7 +945,7 @@ pub fn generate_c_file(name: String, producer: &CProducer) -> std::io::Result<() let full_name = name + ".cpp"; let mut cfile = File::create(full_name)?; let mut code = vec![]; - let len = 256; + let len = producer.get_input_hash_map_entry_size(); code.push("#include ".to_string()); code.push("#include ".to_string()); code.push("#include ".to_string()); diff --git a/code_producers/src/c_elements/common/calcwit.cpp b/code_producers/src/c_elements/common/calcwit.cpp index 949fea418..c39d50389 100644 --- a/code_producers/src/c_elements/common/calcwit.cpp +++ b/code_producers/src/c_elements/common/calcwit.cpp @@ -52,10 +52,10 @@ uint Circom_CalcWit::getInputSignalHashPosition(u64 h) { uint pos = (uint)(h % (u64)n); if (circuit->InputHashMap[pos].hash!=h){ uint inipos = pos; - pos++; + pos = (pos+1)%n; while (pos != inipos) { - if (circuit->InputHashMap[pos].hash==h) return pos; - if (circuit->InputHashMap[pos].hash==0) { + if (circuit->InputHashMap[pos].hash == h) return pos; + if (circuit->InputHashMap[pos].signalid == 0) { fprintf(stderr, "Signal not found\n"); assert(false); } diff --git a/code_producers/src/c_elements/mod.rs b/code_producers/src/c_elements/mod.rs index 6ac369a9a..ccc85c858 100644 --- a/code_producers/src/c_elements/mod.rs +++ b/code_producers/src/c_elements/mod.rs @@ -135,6 +135,9 @@ impl CProducer { pub fn get_main_input_list(&self) -> &InputList { &self.main_input_list } + pub fn get_input_hash_map_entry_size(&self) -> usize { + std::cmp::max(usize::pow(2,(self.main_input_list.len() as f32).log2().ceil() as u32),256) + } pub fn get_number_of_witness(&self) -> usize { self.signals_in_witness } diff --git a/code_producers/src/wasm_elements/mod.rs b/code_producers/src/wasm_elements/mod.rs index a2610beaa..800b406e5 100644 --- a/code_producers/src/wasm_elements/mod.rs +++ b/code_producers/src/wasm_elements/mod.rs @@ -180,6 +180,9 @@ impl WASMProducer { pub fn get_main_input_list(&self) -> &InputList { &self.main_input_list } + pub fn get_input_hash_map_entry_size(&self) -> usize { + std::cmp::max(usize::pow(2,(self.main_input_list.len() as f32).log2().ceil() as u32),256) + } pub fn get_number_of_witness(&self) -> usize { self.signals_in_witness } @@ -262,7 +265,7 @@ impl WASMProducer { (4 * self.size_32_bit) + 8 + self.get_shared_rw_memory_start() } pub fn get_remaining_input_signal_counter(&self) -> usize { - self.get_input_signals_hashmap_start() + 4096 // 256*(8(h)+4(pos)+4(size)) + self.get_input_signals_hashmap_start() + self.get_input_hash_map_entry_size()*16 // input_hash_map_entry_size*(8(h)+4(pos)+4(size)) } pub fn get_input_signal_set_map_start(&self) -> usize { self.get_remaining_input_signal_counter() + 4 diff --git a/code_producers/src/wasm_elements/wasm_code_generator.rs b/code_producers/src/wasm_elements/wasm_code_generator.rs index 6990de2fd..06eded1d2 100644 --- a/code_producers/src/wasm_elements/wasm_code_generator.rs +++ b/code_producers/src/wasm_elements/wasm_code_generator.rs @@ -226,15 +226,14 @@ pub fn get_initial_size_of_memory(producer: &WASMProducer) -> usize { //------------------- generate all kinds of Data ------------------ -pub fn generate_hash_map(signal_name_list: &Vec<(String, usize, usize)>) -> Vec<(u64, usize, usize)> { - assert!(signal_name_list.len() <= 256); - let len = 256; - let mut hash_map = vec![(0, 0, 0); len]; +pub fn generate_hash_map(signal_name_list: &Vec<(String, usize, usize)>, size: usize) -> Vec<(u64, usize, usize)> { + assert!(signal_name_list.len() <= size); + let mut hash_map = vec![(0, 0, 0); size]; for i in 0..signal_name_list.len() { let h = hasher(&signal_name_list[i].0); - let mut p = (h % 256) as usize; + let mut p = h as usize % size; while hash_map[p].1 != 0 { - p = (p + 1) % 256; + p = (p + 1) % size; } hash_map[p] = (h, signal_name_list[i].1, signal_name_list[i].2); } @@ -567,7 +566,7 @@ pub fn generate_data_list(producer: &WASMProducer) -> Vec { producer.get_shared_rw_memory_start() - 8, "\\00\\00\\00\\00\\00\\00\\00\\80" )); - let map = generate_hash_map(&producer.get_main_input_list()); + let map = generate_hash_map(&producer.get_main_input_list(),producer.get_input_hash_map_entry_size()); wdata.push(format!( "(data (i32.const {}) \"{}\")", producer.get_input_signals_hashmap_start(), @@ -852,6 +851,7 @@ pub fn init_generator(producer: &WASMProducer) -> Vec { pub fn get_input_signal_map_position_generator(producer: &WASMProducer) -> Vec { let mut instructions = vec![]; let header = "(func $getInputSignalMapPosition (type $_t_i64ri32)".to_string(); + let sizeones = producer.get_input_hash_map_entry_size()-1; instructions.push(header); instructions.push(" (param $hn i64)".to_string()); instructions.push("(result i32)".to_string()); @@ -860,7 +860,7 @@ pub fn get_input_signal_map_position_generator(producer: &WASMProducer) -> Vec Vec usize { Branch(b) => build_branch(b, fresh), Call(b) => build_call(b, fresh), Compute(b) => build_compute(b, fresh), - Load(b) => build_load(b, fresh), + Load(b) => build_load(b, fresh).0, Loop(b) => build_loop(b, fresh), Return(b) => build_return(b, fresh), Store(b) => build_store(b, fresh), @@ -49,8 +49,8 @@ pub fn build_call(bucket: &mut CallBucket, mut fresh: usize) -> usize { fresh += 1; } Final(data) => { - let v_0 = build_address_type(&mut data.dest_address_type, fresh); - let v_1 = build_location(&mut data.dest, fresh); + let (v_0, _) = build_address_type(&mut data.dest_address_type, fresh); + let (v_1, _) = build_location(&mut data.dest, fresh); max_stack = std::cmp::max(v_0, v_1); } } @@ -62,28 +62,46 @@ pub fn build_call(bucket: &mut CallBucket, mut fresh: usize) -> usize { max_stack } +// returns the depth and the updated fresh variable to be used in the rest of the expression +pub fn build_instruction_compute(instruction: &mut Instruction, fresh: usize) ->(usize, usize){ + use Instruction::*; + match instruction { + Compute(b) => + (build_compute(b, fresh), fresh + 1), // needs 1 expaux to store the result + Load(b) => + build_load(b, fresh), // returns the number of expaux needed + Value(b) => + (build_value(b, fresh), fresh + 1), // needs 1 expaux to store the result + _ => unreachable!(), // only possible instructions inside a compute + } +} + + pub fn build_compute(bucket: &mut ComputeBucket, mut fresh: usize) -> usize { - use crate::ir_processing::build_stack::OperatorType::{AddAddress, MulAddress}; - let consumes = if bucket.op.is_address_op() { 0 } else { 1 }; - bucket.op_aux_no = if bucket.op.is_address_op() { 0 } else { fresh }; - let mut max_stack = fresh + consumes; + + if bucket.op.is_address_op(){ + println!("Bucket: {}", bucket.to_string()); + unreachable!(); // just to check that addresses do not enter here + } + + bucket.op_aux_no = fresh; + fresh += 1; + let mut max_stack = fresh; + for i in &mut bucket.stack { - fresh += consumes; - let depth = build_instruction(i, fresh); + let (depth, new_fresh) = build_instruction_compute(i, fresh); max_stack = std::cmp::max(max_stack, depth); - - // in case it is an addition or multiplication between addresses the number of new fresh vars is the number of ToAddress inside the operand - if bucket.op == AddAddress || bucket.op == MulAddress{ - fresh += get_num_to_address_inside_compute_address(i); - } + fresh = new_fresh; } max_stack } -pub fn build_load(bucket: &mut LoadBucket, fresh: usize) -> usize { - let v0 = build_address_type(&mut bucket.address_type, fresh); - let v1 = build_location(&mut bucket.src, v0); - v1 + + +pub fn build_load(bucket: &mut LoadBucket, fresh: usize) -> (usize, usize) { + let (_v0, f0) = build_address_type(&mut bucket.address_type, fresh); + let (v1, f1) = build_location(&mut bucket.src, f0); + (v1, f1) } pub fn build_create_cmp(bucket: &mut CreateCmpBucket, fresh: usize) -> usize { @@ -101,7 +119,7 @@ pub fn build_return(bucket: &mut ReturnBucket, fresh: usize) -> usize { } pub fn build_log(bucket: &mut LogBucket, fresh: usize) -> usize { - let mut in_log = usize::min_value(); + let mut in_log = 0; for arglog in &mut bucket.argsprint { match arglog { LogBucketArg::LogExp(_) => { @@ -120,8 +138,8 @@ pub fn build_assert(bucket: &mut AssertBucket, fresh: usize) -> usize { pub fn build_store(bucket: &mut StoreBucket, fresh: usize) -> usize { let f_0 = build_instruction(&mut bucket.src, fresh); - let f_1 = build_location(&mut bucket.dest, fresh); - let f_2 = build_address_type(&mut bucket.dest_address_type, fresh); + let (f_1, _) = build_location(&mut bucket.dest, fresh); + let (f_2, _) = build_address_type(&mut bucket.dest_address_type, fresh); std::cmp::max(std::cmp::max(f_0, f_1), f_2) } @@ -130,42 +148,72 @@ pub fn build_value(bucket: &mut ValueBucket, fresh: usize) -> usize { fresh + 1 } -pub fn build_location(bucket: &mut LocationRule, fresh: usize) -> usize { +pub fn build_location(bucket: &mut LocationRule, mut fresh: usize) -> (usize, usize) { use LocationRule::*; match bucket { - Indexed { location, .. } => build_instruction(location, fresh), - Mapped { indexes, .. } => build_list(indexes, fresh), + Indexed { location, .. } => + build_instruction_address(location, fresh), + Mapped { indexes, .. } => { + let mut max_stack = fresh; + for i in indexes{ + let (depth, new_fresh) = build_instruction_address(i, fresh); + max_stack = std::cmp::max(max_stack, depth); + fresh = new_fresh; + } + (max_stack, fresh) + } } } -pub fn build_address_type(xtype: &mut AddressType, fresh: usize) -> usize { +pub fn build_address_type(xtype: &mut AddressType, mut fresh: usize) -> (usize, usize) { use AddressType::*; let mut max = fresh; if let SubcmpSignal { cmp_address, .. } = xtype { - let cmp_stack = build_instruction(cmp_address, fresh); + let (cmp_stack, new_fresh) = build_instruction_address(cmp_address, fresh); max = std::cmp::max(max, cmp_stack); + fresh = new_fresh } - max + (max, fresh) } +////////////////////////////////////////////////////////////////////////// +////////////////// INSTRUCTIONS FOR ADDRESSES OPERATIONS ///////////////// +////////////////////////////////////////////////////////////////////////// -pub fn get_num_to_address_inside_compute_address(instruction: &Instruction) -> usize { + +// returns the depth and the updated fresh variable to be used in the rest of the expression +pub fn build_instruction_address(instruction: &mut Instruction, fresh: usize) ->(usize, usize){ use Instruction::*; match instruction { - Compute(b) =>{ - match b.op{ - OperatorType::ToAddress => 1, - OperatorType::AddAddress | OperatorType::MulAddress{} =>{ - let mut num_to_address = 0; - for i in &b.stack{ - num_to_address += get_num_to_address_inside_compute_address(i); - } - num_to_address - }, - _ => unreachable!(), - } - }, - Value(_) => 0, - _ => unreachable!() + Instruction::Compute(b) => { + build_compute_address(b, fresh) + } + Value(_) =>{ + // we do not need to update the stack and fresh + (0, fresh) + } + _ => unreachable!(), + } +} + +pub fn build_compute_address(bucket: &mut ComputeBucket, mut fresh: usize) -> (usize, usize) { + use crate::ir_processing::build_stack::OperatorType::{AddAddress, MulAddress, ToAddress}; + let mut max_stack = fresh; + if bucket.op == AddAddress || bucket.op == MulAddress{ // in case it is ADD or MUL address + for i in &mut bucket.stack{ + let (depth, new_fresh) = build_instruction_address(i, fresh); + max_stack = std::cmp::max(max_stack, depth); + fresh = new_fresh; + } + } else if bucket.op == ToAddress{ + for i in &mut bucket.stack{ + let (depth, new_fresh) = build_instruction_compute(i, fresh); + max_stack = std::cmp::max(max_stack, depth); + fresh = new_fresh; + } + } else{ + unreachable!() // just to check that fr do not enter here } + (max_stack, fresh) + } \ No newline at end of file diff --git a/constraint_generation/Cargo.toml b/constraint_generation/Cargo.toml index 006851db2..faef4dd43 100644 --- a/constraint_generation/Cargo.toml +++ b/constraint_generation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "constraint_generation" -version = "2.1.8" +version = "2.1.9" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index 5ad07734a..a2477dfcb 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -90,6 +90,11 @@ impl Default for FoldedValue { enum ExecutionError { NonQuadraticConstraint, + ConstraintInUnknown, + DeclarationInUnknown, + TagAssignmentInUnknown, + UnknownTemplate, + NonValidTagAssignment, FalseAssert, ArraySizeTooBig } @@ -197,6 +202,16 @@ fn execute_statement( Declaration { meta, xtype, name, dimensions, .. } => { match xtype { VariableType::AnonymousComponent => { + if runtime.block_type == BlockType::Unknown{ + // Case not valid constraint Known/Unknown + let err = Result::Err(ExecutionError::DeclarationInUnknown); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } execute_anonymous_component_declaration( name, meta.clone(), @@ -232,25 +247,50 @@ fn execute_statement( )? }; match xtype { - VariableType::Component => execute_component_declaration( - name, - &usable_dimensions, - &mut runtime.environment, - actual_node, - ), + VariableType::Component => { + if runtime.block_type == BlockType::Unknown{ + // Case not valid constraint Known/Unknown + let err = Result::Err(ExecutionError::DeclarationInUnknown); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + execute_component_declaration( + name, + &usable_dimensions, + &mut runtime.environment, + actual_node, + ) + }, VariableType::Var => environment_shortcut_add_variable( &mut runtime.environment, name, &usable_dimensions, ), - VariableType::Signal(signal_type, tag_list) => execute_signal_declaration( - name, - &usable_dimensions, - tag_list, - *signal_type, - &mut runtime.environment, - actual_node, - ), + VariableType::Signal(signal_type, tag_list) => + { + if runtime.block_type == BlockType::Unknown{ + // Case not valid constraint Known/Unknown + let err = Result::Err(ExecutionError::DeclarationInUnknown); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + execute_signal_declaration( + name, + &usable_dimensions, + tag_list, + *signal_type, + &mut runtime.environment, + actual_node, + ) + }, _ =>{ unreachable!() } @@ -268,6 +308,19 @@ fn execute_statement( if let Option::Some(node) = actual_node { if *op == AssignOp::AssignConstraintSignal || (*op == AssignOp::AssignSignal && flags.inspect){ debug_assert!(possible_constraint.is_some()); + + if *op == AssignOp::AssignConstraintSignal && runtime.block_type == BlockType::Unknown{ + // Case not valid constraint Known/Unknown + let err = Result::Err(ExecutionError::ConstraintInUnknown); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + + let constrained = possible_constraint.unwrap(); let mut needs_double_arrow = Vec::new(); @@ -332,7 +385,7 @@ fn execute_statement( Result::Err(ExecutionWarning::CanBeQuadraticConstraintMultiple(needs_double_arrow)); treat_result_with_execution_warning( - err, + err, meta, &mut runtime.runtime_errors, &runtime.call_trace, @@ -345,11 +398,25 @@ fn execute_statement( } ConstraintEquality { meta, lhe, rhe, .. } => { debug_assert!(actual_node.is_some()); + + if runtime.block_type == BlockType::Unknown{ + // Case not valid constraint Known/Unknown + let err = Result::Err(ExecutionError::ConstraintInUnknown); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + 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!()); + + let correct_dims_result = AExpressionSlice::check_correct_dims(&arith_left, &Vec::new(), &arith_right, true); treat_result_with_memory_error_void( correct_dims_result, @@ -663,8 +730,8 @@ fn execute_expression( FoldedValue { arithmetic_slice, ..FoldedValue::default() } } } - Call { id, args, .. } => { - let (value, can_simplify) = execute_call(id, args, program_archive, runtime, flags)?; + Call { id, args, meta, .. } => { + let (value, can_simplify) = execute_call(id,meta, args, program_archive, runtime, flags)?; can_be_simplified = can_simplify; value } @@ -692,15 +759,32 @@ fn execute_expression( fn execute_call( id: &String, + meta: &Meta, args: &Vec, program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, flags: FlagsExecution, ) -> Result<(FoldedValue, bool), ()> { let mut arg_values = Vec::new(); + + let is_template = program_archive.contains_template(id); + for arg_expression in args.iter() { let f_arg = execute_expression(arg_expression, program_archive, runtime, flags)?; - arg_values.push(safe_unwrap_to_arithmetic_slice(f_arg, line!())); + let safe_f_arg = safe_unwrap_to_arithmetic_slice(f_arg, line!()); + if is_template{ // check that all the arguments are known + for value in MemorySlice::get_reference_values(&safe_f_arg){ + if !AExpr::is_number(&value){ + treat_result_with_execution_error( + Result::Err(ExecutionError::UnknownTemplate), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + } + } + arg_values.push(safe_f_arg); } if program_archive.contains_function(id){ // in this case we execute let new_environment = prepare_environment_for_call(id, &arg_values, program_archive); @@ -907,6 +991,18 @@ fn perform_assign( &runtime.call_trace, )? } + + if runtime.block_type == BlockType::Unknown{ + // Case not valid constraint Known/Unknown + let err = Result::Err(ExecutionError::TagAssignmentInUnknown); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + let tag = accessing_information.signal_access.clone().unwrap(); let environment_response = ExecutionEnvironment::get_mut_signal_res(&mut runtime.environment, symbol); let (reference_to_tags, reference_to_tags_defined, reference_to_signal_content) = treat_result_with_environment_error( @@ -925,7 +1021,8 @@ fn perform_assign( )? } else if let Some(a_slice) = r_folded.arithmetic_slice { - let value = AExpressionSlice::unwrap_to_single(a_slice); + + let value = AExpressionSlice::unwrap_to_single(a_slice); match value { ArithmeticExpressionGen::Number { value } => { let possible_tag = reference_to_tags.get(&tag.clone()); @@ -950,8 +1047,15 @@ fn perform_assign( } } else {unreachable!()} }, - _ => unreachable!(), - } + + _ =>{ + treat_result_with_execution_error( + Result::Err(ExecutionError::NonValidTagAssignment), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + }, } } else { unreachable!() @@ -961,6 +1065,16 @@ fn perform_assign( debug_assert!(accessing_information.signal_access.is_none()); debug_assert!(accessing_information.after_signal.is_empty()); + // to ensure that input signals are not assigned twice, improving error message + if ExecutionEnvironment::has_input(&runtime.environment, symbol) { + treat_result_with_memory_error( + Err(MemoryError::AssignmentError(TypeAssignmentError::AssignmentInput(symbol.to_string()))), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + } + let environment_response = ExecutionEnvironment::get_mut_signal_res(&mut runtime.environment, symbol); let (reference_to_tags, reference_to_tags_defined, reference_to_signal_content) = treat_result_with_environment_error( environment_response, @@ -1966,7 +2080,7 @@ fn execute_prefix_op( let result = match prefix_op { BoolNot => AExpr::not(value, field), Sub => AExpr::prefix_sub(value, field), - Complement => AExpr::complement_256(value, field), + Complement => AExpr::complement_254(value, field), }; Result::Ok(result) } @@ -2173,7 +2287,7 @@ fn treat_result_with_memory_error_void( Result::Ok(()) => Result::Ok(()), 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 {}", + format!("Typing warning: Mismatched dimensions, assigning to an array an expression of smaller length, the remaining positions are not modified. Initially all variables are initialized to 0.\n Expected length: {}, given {}", dim_original, dim_given), RuntimeError); add_report_to_runtime(report, meta, runtime_errors, call_trace); @@ -2211,6 +2325,11 @@ fn treat_result_with_memory_error_void( Report::error("Exception caused by invalid assignment: signal already assigned".to_string(), RuntimeError) }, + TypeAssignmentError::AssignmentInput(signal) => Report::error( + format!("Invalid assignment: input signals of a template already have a value when the template is executed and cannot be re-assigned. \n Problematic input signal: {}", + signal), + RuntimeError, + ), TypeAssignmentError::AssignmentOutput =>{ Report::error("Exception caused by invalid assignment: trying to assign a value to an output signal of a component".to_string(), RuntimeError) @@ -2314,6 +2433,11 @@ fn treat_result_with_memory_error( Report::error("Exception caused by invalid assignment: signal already assigned".to_string(), RuntimeError) }, + TypeAssignmentError::AssignmentInput(signal) => Report::error( + format!("Invalid assignment: input signals of a template already have a value when the template is executed and cannot be re-assigned. \n Problematic input signal: {}", + signal), + RuntimeError, + ), TypeAssignmentError::AssignmentOutput =>{ Report::error("Exception caused by invalid assignment: trying to assign a value to an output signal of a component".to_string(), RuntimeError) @@ -2413,6 +2537,14 @@ fn treat_result_with_execution_error( "Non quadratic constraints are not allowed!".to_string(), ReportCode::RuntimeError, ), + UnknownTemplate => Report::error( + "Every component instantiation must be resolved during the constraint generation phase. This component declaration uses a value that can be unknown during the constraint generation phase.".to_string(), + ReportCode::RuntimeError, + ), + NonValidTagAssignment => Report::error( + "Tags cannot be assigned to values that can be unknown during the constraint generation phase".to_string(), + ReportCode::RuntimeError, + ), FalseAssert => { Report::error("False assert reached".to_string(), ReportCode::RuntimeError) } @@ -2420,6 +2552,18 @@ fn treat_result_with_execution_error( "The size of the array is expected to be a usize".to_string(), ReportCode::RuntimeError, ), + ConstraintInUnknown => Report::error( + "There are constraints depending on the value of a condition that can be unknown during the constraint generation phase".to_string(), + ReportCode::RuntimeError, + ), + DeclarationInUnknown => Report::error( + "There are signal or component declarations depending on the value of a condition that can be unknown during the constraint generation phase".to_string(), + ReportCode::RuntimeError, + ), + TagAssignmentInUnknown => Report::error( + "There are tag assignments depending on the value of a condition that can be unknown during the constraint generation phase".to_string(), + ReportCode::RuntimeError, + ) }; add_report_to_runtime(report, meta, runtime_errors, call_trace); Result::Err(()) diff --git a/mkdocs/docs/circom-language/basic-operators.md b/mkdocs/docs/circom-language/basic-operators.md index 089615a22..7ff89ffec 100644 --- a/mkdocs/docs/circom-language/basic-operators.md +++ b/mkdocs/docs/circom-language/basic-operators.md @@ -105,9 +105,9 @@ All bitwise operators are performed modulo p. For all ```k``` with ```0=< k <= p/2``` (integer division) we have that * ```x >> k = x/(2**k)``` -* ```x << k = (x*(2{**}k)~ & ~mask) % p ``` +* ```x << k = (x*(2**k)~ & ~mask) % p ``` -where b is the number of significant bits of p and mask is ```2{**}b - 1```. +where b is the number of significant bits of p and mask is ```2**b - 1```. For all ```k``` with ```p/2 +1<= k < p``` we have that diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 89e1f8837..02a86510b 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "2.1.8" +version = "2.1.9" authors = ["Costa Group UCM","iden3"] edition = "2018" build = "build.rs" diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 90dc96f74..7a0db7940 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -9,8 +9,7 @@ use program_structure::ast::produce_report; use program_structure::error_definition::Report; use program_structure::error_code::ReportCode; -grammar<'err>(file_id: usize, errors:&'err mut Vec); - +grammar<'err>(file_id: usize, errors:&'err mut Vec, field: &BigInt); CommaSepList:Vec = { ",")*> => { e.push(t); @@ -342,10 +341,10 @@ ParseSubstitution : Statement = { => ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::BitXor,Meta::new(s,e),variable,rhe), "++" - => ast_shortcuts::plusplus(Meta::new(s,e),variable), + => ast_shortcuts::plusplus(Meta::new(s,e),variable, field), "--" - => ast_shortcuts::subsub(Meta::new(s,e),variable), + => ast_shortcuts::subsub(Meta::new(s,e),variable, field), "++" => { @@ -653,10 +652,10 @@ Expression0: Expression = { => build_variable(Meta::new(s,e),"_".to_string(),Vec::new()), - => build_number(Meta::new(s,e),value), + => build_number(Meta::new(s,e),value, field), - => build_number(Meta::new(s,e),value), + => build_number(Meta::new(s,e),value, field), "(" ")", @@ -664,7 +663,7 @@ Expression0: Expression = { ParseError::UnrecognizedToken { ref token, .. } => { errors.push(produce_report(ReportCode::IllegalExpression, token.0..token.2, file_id)); // doesn't matter - build_number(Meta::new(0,0),BigInt::from(0)) + build_number(Meta::new(0,0),BigInt::from(0), field) } _ => unreachable!(), } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 19a9aeb71..0a9313a1a 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -12,6 +12,7 @@ mod parser_logic; mod syntax_sugar_remover; use include_logic::{FileStack, IncludesGraph}; +use num_bigint::BigInt; use program_structure::ast::{produce_compiler_version_report, produce_report, produce_report_with_message, produce_version_warning_report, Expression}; use program_structure::error_code::ReportCode; use program_structure::error_definition::ReportCollection; @@ -60,6 +61,7 @@ pub fn run_parser( file: String, version: &str, link_libraries: Vec, + field: &BigInt, ) -> Result<(ProgramArchive, ReportCollection), (FileLibrary, ReportCollection)> { let mut file_library = FileLibrary::new(); let mut definitions = Vec::new(); @@ -78,7 +80,7 @@ pub fn run_parser( } let file_id = file_library.add_file(path.clone(), src.clone()); let program = - parser_logic::parse_file(&src, file_id).map_err(|e| (file_library.clone(), e))?; + parser_logic::parse_file(&src, file_id, field).map_err(|e| (file_library.clone(), e))?; if let Some(main) = program.main_component { main_components.push((file_id, main, program.custom_gates)); } diff --git a/parser/src/parser_logic.rs b/parser/src/parser_logic.rs index a572c53e7..04a28b772 100644 --- a/parser/src/parser_logic.rs +++ b/parser/src/parser_logic.rs @@ -1,4 +1,5 @@ use super::lang; +use num_bigint::BigInt; use program_structure::ast::{AST}; use program_structure::ast::produce_report; use program_structure::error_code::ReportCode; @@ -83,14 +84,14 @@ pub fn preprocess(expr: &str, file_id: FileID) -> Result Result { +pub fn parse_file(src: &str, file_id: FileID, field: &BigInt) -> Result { use lalrpop_util::ParseError::*; let mut errors = Vec::new(); let preprocess = preprocess(src, file_id)?; let ast = lang::ParseAstParser::new() - .parse(file_id, &mut errors, &preprocess) + .parse(file_id, &mut errors, field, &preprocess) // TODO: is this always fatal? .map_err(|parse_error| match parse_error { InvalidToken { location } => diff --git a/parser/src/syntax_sugar_remover.rs b/parser/src/syntax_sugar_remover.rs index fb9462b80..0c47d7af1 100644 --- a/parser/src/syntax_sugar_remover.rs +++ b/parser/src/syntax_sugar_remover.rs @@ -474,6 +474,9 @@ pub fn remove_anonymous_from_expression( if let Some(m) = names { // in case we have a list of names and assignments let inputs = template.unwrap().get_inputs(); let mut n_expr = 0; + if inputs.len() != m.len() { + return Result::Err(anonymous_general_error(meta.clone(),"The number of template input signals must coincide with the number of input parameters ".to_string())); + } for (operator, name) in m{ if operator != AssignOp::AssignConstraintSignal{ let error = format!("Anonymous components only admit the use of the operator <=="); @@ -494,6 +497,11 @@ pub fn remove_anonymous_from_expression( else{ let inputs = template.unwrap().get_declaration_inputs(); let mut n_expr = 0; + + if inputs.len() != signals.len() { + return Result::Err(anonymous_general_error(meta.clone(),"The number of template input signals must coincide with the number of input parameters ".to_string())); + } + for value in signals { inputs_to_assignments.insert(inputs[n_expr].0.clone(), (AssignOp::AssignConstraintSignal, value)); n_expr += 1; diff --git a/program_structure/Cargo.toml b/program_structure/Cargo.toml index 6337573df..19417f0aa 100644 --- a/program_structure/Cargo.toml +++ b/program_structure/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "program_structure" -version = "2.1.8" +version = "2.1.9" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs b/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs index 7f8f304a6..ad4c73818 100644 --- a/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs +++ b/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs @@ -27,13 +27,13 @@ pub fn assign_with_op_shortcut( build_substitution(meta, var, access, AssignOp::AssignVar, infix) } -pub fn plusplus(meta: Meta, variable: (String, Vec)) -> Statement { - let one = build_number(meta.clone(), BigInt::from(1)); +pub fn plusplus(meta: Meta, variable: (String, Vec), field: &BigInt) -> Statement { + let one = build_number(meta.clone(), BigInt::from(1), field); assign_with_op_shortcut(ExpressionInfixOpcode::Add, meta, variable, one) } -pub fn subsub(meta: Meta, variable: (String, Vec)) -> Statement { - let one = build_number(meta.clone(), BigInt::from(1)); +pub fn subsub(meta: Meta, variable: (String, Vec), field: &BigInt) -> Statement { + let one = build_number(meta.clone(), BigInt::from(1), field); assign_with_op_shortcut(ExpressionInfixOpcode::Sub, meta, variable, one) } @@ -67,21 +67,28 @@ pub fn split_declaration_into_single_nodes( let possible_init = symbol.init; let single_declaration = build_declaration(with_meta, has_type, name, dimensions.clone()); initializations.push(single_declaration); - if let Option::Some(init) = possible_init { - let substitution = - build_substitution(meta.clone(), symbol.name, vec![], op, init); - initializations.push(substitution); - } - else if xtype == Var { + + // For the variables, we need to initialize them to 0 in case: + // - They are not initialized to other value + // - They are arrays (and maybe not all positions are initialized) + + if xtype == Var && (possible_init.is_none() || dimensions.len() > 0){ let mut value = Expression:: Number(meta.clone(), BigInt::from(0)); for dim_expr in dimensions.iter().rev(){ value = build_uniform_array(meta.clone(), value, dim_expr.clone()); } let substitution = - build_substitution(meta.clone(), symbol.name, vec![], op, value); + build_substitution(meta.clone(), symbol.name.clone(), vec![], op, value); initializations.push(substitution); } + + if let Option::Some(init) = possible_init { + let substitution = + build_substitution(meta.clone(), symbol.name, vec![], op, init); + initializations.push(substitution); + } + } build_initialization_block(meta, xtype, initializations) } diff --git a/program_structure/src/abstract_syntax_tree/expression_builders.rs b/program_structure/src/abstract_syntax_tree/expression_builders.rs index 0e7d2a0ba..ed88cef48 100644 --- a/program_structure/src/abstract_syntax_tree/expression_builders.rs +++ b/program_structure/src/abstract_syntax_tree/expression_builders.rs @@ -43,7 +43,11 @@ pub fn build_variable(meta: Meta, name: String, access: Vec) -> Expressi Variable { meta, name, access } } -pub fn build_number(meta: Meta, value: BigInt) -> Expression { +pub fn build_number(meta: Meta, value: BigInt, field: &BigInt) -> Expression { + Expression::Number(meta, value % field) +} + +pub fn build_number_without_field(meta: Meta, value: BigInt) -> Expression { Expression::Number(meta, value) } diff --git a/program_structure/src/program_library/error_code.rs b/program_structure/src/program_library/error_code.rs index 0362f8980..b83ee53e8 100644 --- a/program_structure/src/program_library/error_code.rs +++ b/program_structure/src/program_library/error_code.rs @@ -51,6 +51,7 @@ pub enum ReportCode { NonEqualTypesInExpression, NonExistentSymbol, MainComponentWithTags, + IllegalMainExpression, TemplateCallAsArgument, TemplateWrongNumberOfArguments, TemplateWithReturnStatement, @@ -87,6 +88,7 @@ pub enum ReportCode { RuntimeWarning, UnknownTemplate, NonQuadratic, + NonValidTagAssignment, NonConstantArrayLength, NonComputableExpression, // Constraint analysis codes @@ -190,6 +192,8 @@ impl fmt::Display for ReportCode { UnreachableTags => "T2049", UnreachableSignals => "T2050", MainComponentWithTags => "T2051", + NonValidTagAssignment => "T2052", + IllegalMainExpression => "T2053", RuntimeError => "T3001", RuntimeWarning => "T3002", UnknownDimension => "T20460", diff --git a/program_structure/src/utils/memory_slice.rs b/program_structure/src/utils/memory_slice.rs index c654033fc..5a946ed8a 100644 --- a/program_structure/src/utils/memory_slice.rs +++ b/program_structure/src/utils/memory_slice.rs @@ -11,7 +11,8 @@ pub enum TypeInvalidAccess { pub enum TypeAssignmentError { MultipleAssignments, AssignmentOutput, - NoInitializedComponent + NoInitializedComponent, + AssignmentInput(String), } pub enum MemoryError { @@ -291,6 +292,12 @@ impl MemorySlice { return Result::Ok(memory_slice.values[index].clone()); } + pub fn get_reference_values<'a>( + memory_slice: &'a MemorySlice, + )-> &'a Vec{ + &memory_slice.values + } + pub fn get_reference_to_single_value<'a>( memory_slice: &'a MemorySlice, access: &[SliceCapacity], diff --git a/type_analysis/Cargo.toml b/type_analysis/Cargo.toml index 50832a069..bccfbdf64 100644 --- a/type_analysis/Cargo.toml +++ b/type_analysis/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "type_analysis" -version = "2.1.8" +version = "2.1.9" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/type_analysis/src/analyzers/type_check.rs b/type_analysis/src/analyzers/type_check.rs index c1bd56a3f..44bf9ead9 100644 --- a/type_analysis/src/analyzers/type_check.rs +++ b/type_analysis/src/analyzers/type_check.rs @@ -74,26 +74,11 @@ pub fn type_check(program_archive: &ProgramArchive) -> Result Result bool { +fn check_main_has_tags(initial_expression: &Expression, program_archive: &ProgramArchive, reports: &mut ReportCollection) { if let Call { id, .. } = initial_expression{ - let inputs = program_archive.get_template_data(id).get_inputs(); - let mut tag_in_inputs = false; - for input in inputs { - if !input.1.1.is_empty(){ - tag_in_inputs = true; - break; + if program_archive.contains_template(id){ + let inputs = program_archive.get_template_data(id).get_inputs(); + for input in inputs { + if !input.1.1.is_empty(){ + add_report( + ReportCode::MainComponentWithTags, + initial_expression.get_meta(), + reports, + ); + break; + } } + } else{ + add_report( + ReportCode::IllegalMainExpression, + initial_expression.get_meta(), + reports, + ) } - tag_in_inputs + + } + else { + add_report( + ReportCode::IllegalMainExpression, + initial_expression.get_meta(), + reports, + ) } - else { unreachable!()} } fn type_statement( @@ -1062,15 +1064,16 @@ fn add_report(error_code: ReportCode, meta: &Meta, reports: &mut ReportCollectio } MainComponentWithTags => "Main component cannot have inputs with tags".to_string(), ExpectedDimDiffGotDim(expected, got) => { - format!("Function should return {} but returns {}", expected, got) + format!("All branches of a function should return an element of the same dimensions.\n Found {} and {} dimensions", expected, got) } 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(), NonCompatibleBranchTypes => "Inline switch operator branches types are non compatible".to_string(), + IllegalMainExpression => "Invalid main component: the main component should be a template, not a function call or expression".to_string(), e => panic!("Unimplemented error code: {}", e), }; report.add_primary(location, file_id, message); reports.push(report); -} +} \ No newline at end of file diff --git a/type_analysis/src/analyzers/unknown_known_analysis.rs b/type_analysis/src/analyzers/unknown_known_analysis.rs index 5cb5541f2..bf93e8a22 100644 --- a/type_analysis/src/analyzers/unknown_known_analysis.rs +++ b/type_analysis/src/analyzers/unknown_known_analysis.rs @@ -26,7 +26,17 @@ enum Tag { Known, Unknown, } -type Environment = CircomEnvironment; + +// For the vars we store the information: (is_it_known, is_it_array) +// in case it is an array, if it becomes unknown it will always be unknown +// but it will not generate an error +// Example: +// a[0] = 0; // a[0] is known +// a[1] = in; // a[1] is unknown +// if (a[i] == 5){ in === 5;} // we do not know if there is an error here or not +// --> we cannot detect the error until execution + +type Environment = CircomEnvironment; pub fn unknown_known_analysis( template_name: &str, @@ -38,7 +48,8 @@ pub fn unknown_known_analysis( let file_id = template_data.get_file_id(); let mut environment = Environment::new(); for arg in template_data.get_name_of_params() { - environment.add_variable(arg, Tag::Known); + // We do not know if it is an array or not, so we use the most restrictive option + environment.add_variable(arg, (Tag::Known, true)); } let entry = EntryInformation { file_id, environment }; @@ -97,8 +108,9 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma } else if let VariableType::AnonymousComponent = xtype { environment.add_component(name, Unknown); signals_declared = true; - } else { - environment.add_variable(name, Unknown); + } else { // it is a variable + let is_array = dimensions.len() > 0; + environment.add_variable(name, (Known, is_array)); modified_variables.insert(name.clone()); } if let VariableType::AnonymousComponent = xtype { @@ -130,8 +142,12 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma } } if simplified_elem == Variable { - let value = environment.get_mut_variable_or_break(var, file!(), line!()); - *value = max(expression_tag, access_tag); + let (value, is_array) = environment.get_mut_variable_or_break(var, file!(), line!()); + if !*is_array { // if it is a single variable we always update + *value = max(expression_tag, access_tag); + } else if *value == Known{ // if not, if it was ukn it remains ukn + *value = max(expression_tag, access_tag); + } modified_variables.insert(var.clone()); } else if simplified_elem == Component { constraints_declared = true; @@ -144,10 +160,10 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma } else if simplified_elem == SignalTag { tags_modified = true; if expression_tag == Unknown { - add_report(ReportCode::UnknownTemplate, rhe.get_meta(), file_id, &mut reports); + add_report(ReportCode::NonValidTagAssignment, rhe.get_meta(), file_id, &mut reports); } if access_tag == Unknown { - add_report(ReportCode::UnknownTemplate, meta, file_id, &mut reports); + add_report(ReportCode::NonValidTagAssignment, rhe.get_meta(), file_id, &mut reports); } } else if *op == AssignOp::AssignConstraintSignal { constraints_declared = true; @@ -214,7 +230,7 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma if tag_cond == Unknown{ for var in &modified_variables{ if environment.has_variable(var){ - let value = environment.get_mut_variable_or_break(var, file!(), line!()); + let (value, _is_array) = environment.get_mut_variable_or_break(var, file!(), line!()); *value = Unknown; } } @@ -270,7 +286,7 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma if tag_out == Unknown{ for var in &exit.modified_variables{ if environment.has_variable(var){ - let value = environment.get_mut_variable_or_break(var, file!(), line!()); + let (value, _is_array) = environment.get_mut_variable_or_break(var, file!(), line!()); *value = Unknown; } } @@ -335,7 +351,12 @@ fn tag(expression: &Expression, environment: &Environment) -> Tag { Number(_, _) => Known, Variable { name, access, .. } => { let mut symbol_tag = if environment.has_variable(name) { - *environment.get_variable_or_break(name, file!(), line!()) + let (tag, is_array) = environment.get_variable_or_break(name, file!(), line!()); + if *is_array{ + Known + } else{ + *tag + } } else if environment.has_component(name) { *environment.get_component_or_break(name, file!(), line!()) } else { @@ -398,7 +419,7 @@ fn check_modified( let t_ini = initial_state.get_variable_or_break(v, file!(), line!()); let t_fin = final_state.get_mut_variable_or_break(v, file!(), line!()); if *t_ini != *t_fin{ - if *t_fin == Tag::Unknown{ // in other case we can enter in loops + if t_fin.0 == Tag::Unknown{ // in other case we can enter in loops modified = true; } *t_fin = max(*t_ini, *t_fin); @@ -534,8 +555,9 @@ fn add_report( 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 { - UnknownDimension => "The length of every array must known during the constraint generation phase".to_string(), - UnknownTemplate => "Every component instantiation must be resolved during the constraint generation phase".to_string(), + UnknownDimension => "The length of every array must be known during the constraint generation phase".to_string(), + UnknownTemplate => "Every component instantiation must be resolved during the constraint generation phase. This component declaration uses a value that can be unknown during the constraint generation phase.".to_string(), + NonValidTagAssignment => "Tags cannot be assigned to values that can be unknown during the constraint generation phase".to_string(), 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(), diff --git a/type_analysis/src/decorators/constants_handler.rs b/type_analysis/src/decorators/constants_handler.rs index bbffe78d7..a16180625 100644 --- a/type_analysis/src/decorators/constants_handler.rs +++ b/type_analysis/src/decorators/constants_handler.rs @@ -420,7 +420,7 @@ fn expand_expression(expr: Expression, environment: &ExpressionHolder) -> Expres } fn expand_number(meta: Meta, value: BigInt) -> Expression { - build_number(meta, value) + build_number_without_field(meta, value) } fn expand_array(