diff --git a/Cargo.lock b/Cargo.lock index 9bde65810..f2b28e00e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,7 +155,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "circom" -version = "2.0.9" +version = "2.1.0" dependencies = [ "ansi_term", "clap", @@ -205,7 +205,7 @@ dependencies = [ [[package]] name = "code_producers" -version = "2.0.8" +version = "2.1.0" dependencies = [ "handlebars", "lz_fnv", @@ -234,7 +234,7 @@ dependencies = [ [[package]] name = "compiler" -version = "2.0.8" +version = "2.1.0" dependencies = [ "code_producers", "constant_tracking", @@ -255,7 +255,7 @@ version = "2.0.0" [[package]] name = "constraint_generation" -version = "2.0.9" +version = "2.1.0" dependencies = [ "ansi_term", "circom_algebra", @@ -655,7 +655,7 @@ checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" [[package]] name = "parser" -version = "2.0.8" +version = "2.1.0" dependencies = [ "lalrpop", "lalrpop-util", @@ -764,7 +764,7 @@ dependencies = [ [[package]] name = "program_structure" -version = "2.0.9" +version = "2.1.0" dependencies = [ "codespan", "codespan-reporting", @@ -1188,7 +1188,7 @@ dependencies = [ [[package]] name = "type_analysis" -version = "2.0.8" +version = "2.1.0" dependencies = [ "num-bigint-dig", "num-traits", diff --git a/RELEASES.md b/RELEASES.md index 7f4c3db5b..c47bf79ce 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,17 +1,24 @@ # Release notes +## October 11, 2022 circom 2.1.0 +#### New features +- Tags: more information [here](https://github.com/iden3/circom/blob/master/mkdocs/docs/circom-language/tags.md). +- Anonymous Components: more information [here](https://github.com/iden3/circom/blob/master/mkdocs/docs/circom-language/anonymous-components-and-tuples.md). + +#### Extensions +- Improving the memory consumption during the C++ witness generation. ## September 21, 2022 circom 2.0.9 -#### Extension -- Adding a warning if the programmer is using the operator <-- when it is possible to use <== instead (if the right side is a quadratic expression and the instruction is not contained in a custom template). -- Signal ids in custom templates changed to 64 bits. -- Array sizes are expected to be usize. Now, we throw an error in other case. -- Separating optimization option -O2 in two different options: --O2 and --O2rounds. Explanation can be found [here](https://github.com/iden3/circom/blob/master/mkdocs/docs/getting-started/compilation-options.md). The default option is currently --O2. + #### Extensions + - Adding a warning if the programmer is using the operator <-- when it is possible to use <== instead (if the right side is a quadratic expression and the instruction is not contained in a custom template). + - Signal ids in custom templates changed to 64 bits. + - Array sizes are expected to be usize. Now, we throw an error in other case. + - Separating optimization option -O2 in two different options: --O2 and --O2rounds. Explanation can be found [here](https://github.com/iden3/circom/blob/master/mkdocs/docs/circom-language/include.md). The default option is currently --O2. - Writing Sections 4 and 5 of the r1cs file, only if "pragma custom_templates" is used (which is needed if custom templates are present). -- Improving --O1 optimization. -- Adding a new documentation section about the different compilation options and flags. + - Improving --O1 optimization. + - Adding a new documentation section about the different compilation options and flags. -#### Fixed bugs -- Fixing -l option to disallow several values for one option: each value must have its own -l option. + #### Fixed bugs + - Fixing -l option to disallow several values for one option: each value must have its own -l option. ## August 26, 2022 circom 2.0.8 diff --git a/circom/Cargo.toml b/circom/Cargo.toml index 3d1ab6071..5ff9cc41e 100644 --- a/circom/Cargo.toml +++ b/circom/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "circom" -version = "2.0.9" +version = "2.1.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/code_producers/Cargo.toml b/code_producers/Cargo.toml index 7d7ab0f2a..17b260546 100644 --- a/code_producers/Cargo.toml +++ b/code_producers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "code_producers" -version = "2.0.8" +version = "2.1.0" 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 3b867156a..92e4b51ee 100644 --- a/code_producers/src/c_elements/c_code_generator.rs +++ b/code_producers/src/c_elements/c_code_generator.rs @@ -729,6 +729,7 @@ pub fn generate_message_list_def(_producer: &CProducer, message_list: &MessageLi pub fn generate_function_release_memory_component() -> Vec{ let mut instructions = vec![]; instructions.push("void release_memory_component(Circom_CalcWit* ctx, uint pos) {{\n".to_string()); + instructions.push("if (pos != 0){{\n".to_string()); instructions.push("delete ctx->componentMemory[pos].subcomponents;\n".to_string()); instructions.push("delete ctx->componentMemory[pos].subcomponentsParallel;\n".to_string()); instructions.push("delete ctx->componentMemory[pos].outputIsSet;\n".to_string()); @@ -736,6 +737,7 @@ pub fn generate_function_release_memory_component() -> Vec{ instructions.push("delete ctx->componentMemory[pos].cvs;\n".to_string()); instructions.push("delete ctx->componentMemory[pos].sbct;\n".to_string()); instructions.push("}}\n\n".to_string()); + instructions.push("}}\n\n".to_string()); instructions } diff --git a/code_producers/src/wasm_elements/mod.rs b/code_producers/src/wasm_elements/mod.rs index 66f7b9563..760f4896a 100644 --- a/code_producers/src/wasm_elements/mod.rs +++ b/code_producers/src/wasm_elements/mod.rs @@ -45,6 +45,10 @@ pub struct WASMProducer { call_lvar_tag: String, expaux_tag: String, temp_tag: String, + aux_0_tag: String, + aux_1_tag: String, + aux_2_tag: String, + counter_tag: String, store_aux_1_tag: String, store_aux_2_tag: String, copy_counter_tag: String, @@ -103,6 +107,10 @@ impl Default for WASMProducer { call_lvar_tag: "$calllvar".to_string(), expaux_tag: "$expaux".to_string(), temp_tag: "$temp".to_string(), + aux_0_tag: "$aux0".to_string(), + aux_1_tag: "$aux1".to_string(), + aux_2_tag: "$aux2".to_string(), + counter_tag: "$counter".to_string(), store_aux_1_tag: "$storeaux1".to_string(), store_aux_2_tag: "$storeaux2".to_string(), copy_counter_tag: "$copycounter".to_string(), @@ -362,6 +370,18 @@ impl WASMProducer { pub fn get_temp_tag(&self) -> &str { &self.temp_tag } + pub fn get_aux_0_tag(&self) -> &str { + &self.aux_0_tag + } + pub fn get_aux_1_tag(&self) -> &str { + &self.aux_1_tag + } + pub fn get_aux_2_tag(&self) -> &str { + &self.aux_2_tag + } + pub fn get_counter_tag(&self) -> &str { + &self.counter_tag + } pub fn get_store_aux_1_tag(&self) -> &str { &self.store_aux_1_tag } diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index aeebb5c18..cbdf808fd 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "compiler" -version = "2.0.8" +version = "2.1.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/compiler/src/circuit_design/build.rs b/compiler/src/circuit_design/build.rs index abc465d89..26ad26850 100644 --- a/compiler/src/circuit_design/build.rs +++ b/compiler/src/circuit_design/build.rs @@ -97,6 +97,7 @@ fn build_template_instances( components: template.components, template_database: &c_info.template_database, string_table : string_table, + signals_to_tags: template.signals_to_tags, }; let mut template_info = TemplateCodeInfo { name, @@ -159,7 +160,8 @@ fn build_function_instances( cmp_to_type: HashMap::with_capacity(0), component_to_parallel: HashMap::with_capacity(0), template_database: &c_info.template_database, - string_table : string_table + string_table : string_table, + signals_to_tags: BTreeMap::new(), }; let mut function_info = FunctionCodeInfo { name, @@ -401,4 +403,4 @@ pub fn create_table_usize_to_string( string_table : HashMap) -> Ve table_usize_to_string[us] = string; } table_usize_to_string -} \ No newline at end of file +} diff --git a/compiler/src/circuit_design/circuit.rs b/compiler/src/circuit_design/circuit.rs index be21e09da..fecfdba28 100644 --- a/compiler/src/circuit_design/circuit.rs +++ b/compiler/src/circuit_design/circuit.rs @@ -359,8 +359,8 @@ impl WriteC for Circuit { producer.get_io_map().len() )); //code.append(&mut generate_message_list_def(producer, producer.get_message_list())); - - // Function to release the memory of a component + + // Functions to release the memory let mut release_component_code = generate_function_release_memory_component(); code.append(&mut release_component_code); @@ -376,6 +376,7 @@ impl WriteC for Circuit { code.append(&mut t_code); } + // Epilogue let run_circuit = "void run".to_string(); let run_circuit_args = vec![declare_circom_calc_wit()]; @@ -403,7 +404,6 @@ impl WriteC for Circuit { 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![start_msg, ctx_index, run_call, end_msg]; let main_run_body = vec![ctx_index, run_call]; code.push(build_callable(run_circuit, run_circuit_args, main_run_body)); (code, "".to_string()) diff --git a/compiler/src/circuit_design/function.rs b/compiler/src/circuit_design/function.rs index ecb6ae65d..305a000dd 100644 --- a/compiler/src/circuit_design/function.rs +++ b/compiler/src/circuit_design/function.rs @@ -42,6 +42,10 @@ impl WriteWasm for FunctionCodeInfo { instructions.push(format!("(local {} i32)", producer.get_lvar_tag())); instructions.push(format!("(local {} i32)", producer.get_expaux_tag())); instructions.push(format!("(local {} i32)", producer.get_temp_tag())); + instructions.push(format!("(local {} i32)", producer.get_aux_0_tag())); + instructions.push(format!("(local {} i32)", producer.get_aux_1_tag())); + instructions.push(format!("(local {} i32)", producer.get_aux_2_tag())); + instructions.push(format!("(local {} i32)", producer.get_counter_tag())); instructions.push(format!("(local {} i32)", producer.get_store_aux_1_tag())); instructions.push(format!("(local {} i32)", producer.get_store_aux_2_tag())); instructions.push(format!("(local {} i32)", producer.get_copy_counter_tag())); diff --git a/compiler/src/circuit_design/template.rs b/compiler/src/circuit_design/template.rs index 3c734913c..59b16ccd6 100644 --- a/compiler/src/circuit_design/template.rs +++ b/compiler/src/circuit_design/template.rs @@ -87,6 +87,10 @@ impl WriteWasm for TemplateCodeInfo { instructions.push(format!(" (local {} i32)", producer.get_lvar_tag())); instructions.push(format!(" (local {} i32)", producer.get_expaux_tag())); instructions.push(format!(" (local {} i32)", producer.get_temp_tag())); + instructions.push(format!(" (local {} i32)", producer.get_aux_0_tag())); + instructions.push(format!(" (local {} i32)", producer.get_aux_1_tag())); + instructions.push(format!(" (local {} i32)", producer.get_aux_2_tag())); + instructions.push(format!(" (local {} i32)", producer.get_counter_tag())); instructions.push(format!(" (local {} i32)", producer.get_store_aux_1_tag())); instructions.push(format!(" (local {} i32)", producer.get_store_aux_2_tag())); instructions.push(format!(" (local {} i32)", producer.get_copy_counter_tag())); @@ -309,8 +313,8 @@ impl TemplateCodeInfo { "release_memory_component".to_string(), vec![CIRCOM_CALC_WIT.to_string(), "index_subc".to_string()] ))); + run_body.push(format!("}}")); - let run_fun = build_callable(run_header, run_params, run_body); vec![create_fun, run_fun] } @@ -318,4 +322,4 @@ impl TemplateCodeInfo { pub fn wrap(self) -> TemplateCode { TemplateCode::new(self) } -} \ No newline at end of file +} diff --git a/compiler/src/hir/component_preprocess.rs b/compiler/src/hir/component_preprocess.rs index 071dca73f..e2512adca 100644 --- a/compiler/src/hir/component_preprocess.rs +++ b/compiler/src/hir/component_preprocess.rs @@ -66,10 +66,22 @@ fn rm_init(stmt: &mut Statement) { if let InitializationBlock { initializations, xtype, .. } = stmt { if let Signal(..) = xtype { let work = std::mem::take(initializations); - for i in work { + for mut i in work { if i.is_substitution() { initializations.push(i); } + else if i.is_block(){ + rm_block(&mut i); + initializations.push(i); + } + } + } else { + let filter = std::mem::take(initializations); + for mut s in filter { + rm_statement(&mut s); + if !should_be_removed(&s) { + initializations.push(s); + } } } } else { @@ -90,9 +102,9 @@ fn should_be_removed(stmt: &Statement) -> bool { use Statement::{InitializationBlock, Substitution}; use VariableType::*; if let InitializationBlock { xtype, .. } = stmt { - Component == *xtype + Component == *xtype || AnonymousComponent == *xtype } else if let Substitution { meta, .. } = stmt { - meta.get_type_knowledge().is_component() + meta.get_type_knowledge().is_component() || meta.get_type_knowledge().is_tag() } else { false } diff --git a/compiler/src/hir/sugar_cleaner.rs b/compiler/src/hir/sugar_cleaner.rs index 3d0bf5ded..5c0e326a8 100644 --- a/compiler/src/hir/sugar_cleaner.rs +++ b/compiler/src/hir/sugar_cleaner.rs @@ -26,6 +26,7 @@ impl State { -Inline if-then-else removal -Inline array removal -Initialization Block removal (no longer needed) + -Uniform array removal */ pub fn clean_sugar(vcp: &mut VCP) { @@ -81,6 +82,7 @@ fn extend_block(stmt: &mut Statement, state: &mut State, context: &Context) -> V map_init_blocks(stmts); map_stmts_with_sugar(stmts, state, context); map_substitutions(stmts); + map_constraint_equalities(stmts); state.fresh_id = map_returns(stmts, state.fresh_id); state.fresh_id = checkpoint_id; vec![] @@ -417,6 +419,17 @@ fn map_substitutions(stmts: &mut Vec) { } } +fn map_constraint_equalities(stmts: &mut Vec) { + let work = std::mem::take(stmts); + for w in work { + if w.is_constraint_equality() { + into_single_constraint_equality(w, stmts); + } else { + stmts.push(w); + } + } +} + fn map_returns(stmts: &mut Vec, mut fresh_id: usize) -> usize { use Statement::Return; let work = std::mem::take(stmts); @@ -590,4 +603,69 @@ fn rhe_array_case(stmt: Statement, stmts: &mut Vec) { } else { unreachable!() } +} + +fn into_single_constraint_equality(stmt: Statement, stmts: &mut Vec) { + use Statement::ConstraintEquality; + match &stmt { + ConstraintEquality { rhe, lhe, meta, .. } =>{ + if lhe.is_array() { + lhe_array_ce(meta, lhe, rhe, stmts) + } + else if rhe.is_array(){ + lhe_array_ce(meta, rhe, lhe, stmts) + } + else{ + stmts.push(stmt); + } + } + _ => stmts.push(stmt), + } +} + +fn lhe_array_ce(meta: &Meta, expr_array: &Expression, other_expr: &Expression, stmts: &mut Vec) { + use num_bigint_dig::BigInt; + use Expression::{ArrayInLine, Number, UniformArray, Variable}; + use Statement::ConstraintEquality; + if let ArrayInLine { values: values_l, .. } = expr_array { + if let ArrayInLine { values: values_r, .. } = other_expr { + for i in 0..values_l.len() { + let ce = ConstraintEquality { + lhe: values_l[i].clone(), + rhe: values_r[i].clone(), + meta: meta.clone(), + }; + stmts.push(ce); + } + } + else if let UniformArray {value, ..} = other_expr { + for i in 0..values_l.len() { + let ce = ConstraintEquality { + lhe: values_l[i].clone(), + rhe: *value.clone(), + meta: meta.clone(), + }; + stmts.push(ce); + } + } + else if let Variable { name, access, ..} = other_expr { + for i in 0..values_l.len() { + let mut index_meta = meta.clone(); + index_meta.get_mut_memory_knowledge().set_concrete_dimensions(vec![]); + let expr_index = Number(index_meta, BigInt::from(i)); + let as_access = Access::ArrayAccess(expr_index); + let mut accessed_with = access.clone(); + accessed_with.push(as_access); + let ce = ConstraintEquality { + lhe: values_l[i].clone(), + rhe: Variable {name: name.clone(), access: accessed_with, meta: meta.clone()}, + meta: meta.clone(), + }; + stmts.push(ce); + } + } + + } else { + unreachable!() + } } \ No newline at end of file diff --git a/compiler/src/hir/very_concrete_program.rs b/compiler/src/hir/very_concrete_program.rs index 4759a7e7a..f6834f9d7 100644 --- a/compiler/src/hir/very_concrete_program.rs +++ b/compiler/src/hir/very_concrete_program.rs @@ -2,7 +2,7 @@ use num_bigint_dig::BigInt; use program_structure::ast::{SignalType, Statement}; use program_structure::program_archive::ProgramArchive; use program_structure::program_library::file_definition::FileLibrary; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::ops::Range; use std::rc::Rc; @@ -10,6 +10,8 @@ pub type VCT = Vec; pub type Length = usize; pub type Code = Statement; +pub type TagInfo = BTreeMap>; + #[derive(Clone)] pub struct Argument { pub name: String, @@ -90,6 +92,7 @@ pub struct TemplateInstance { pub number_of_outputs: usize, pub number_of_intermediates: usize, pub signals: Vec, + pub signals_to_tags: BTreeMap, pub components: Vec, pub number_of_components: usize, pub triggers: Vec, @@ -109,8 +112,8 @@ pub struct TemplateConfig { pub clusters: Vec, pub components: Vec, pub arguments: Vec, + pub signals_to_tags: BTreeMap, } - impl TemplateInstance { pub fn new(config: TemplateConfig) -> TemplateInstance { TemplateInstance { @@ -131,6 +134,7 @@ impl TemplateInstance { components: config.components, triggers: config.triggers, clusters: config.clusters, + signals_to_tags: config.signals_to_tags, } } diff --git a/compiler/src/intermediate_representation/call_bucket.rs b/compiler/src/intermediate_representation/call_bucket.rs index 0a563f151..b0171e109 100644 --- a/compiler/src/intermediate_representation/call_bucket.rs +++ b/compiler/src/intermediate_representation/call_bucket.rs @@ -497,8 +497,8 @@ impl WriteC for CallBucket { AddressType::SubcmpSignal { uniform_parallel_value, input_information, .. } => { // if subcomponent input check if run needed let sub_cmp_counter_decrease = format!( - "--{}->componentMemory[{}[{}]].inputCounter", - CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, cmp_index_ref + "{}->componentMemory[{}[{}]].inputCounter -= {}", + CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, cmp_index_ref, &data.context.size ); if let InputInformation::Input{status} = input_information { if let StatusInput::NoLast = status { diff --git a/compiler/src/intermediate_representation/compute_bucket.rs b/compiler/src/intermediate_representation/compute_bucket.rs index 17bb04250..0823fad5a 100644 --- a/compiler/src/intermediate_representation/compute_bucket.rs +++ b/compiler/src/intermediate_representation/compute_bucket.rs @@ -18,7 +18,7 @@ pub enum OperatorType { GreaterEq, Lesser, Greater, - Eq, + Eq(usize), NotEq, BoolOr, BoolAnd, @@ -39,11 +39,21 @@ impl OperatorType { || *self == OperatorType::AddAddress || *self == OperatorType::MulAddress } + + pub fn is_multiple_eq(&self) -> bool { + match self { + OperatorType::Eq(n) => *n > 1, + _ => false + } + } } impl ToString for OperatorType { fn to_string(&self) -> String { use OperatorType::*; + if let Eq(n) = self { + format!("EQ({})", n) + } else { match self { Mul => "MUL", Div => "DIV", @@ -58,7 +68,6 @@ impl ToString for OperatorType { GreaterEq => "GREATER_EQ", Lesser => "LESSER", Greater => "GREATER", - Eq => "EQ", NotEq => "NOT_EQ", BoolOr => "BOOL_OR", BoolAnd => "BOOL_AND", @@ -71,8 +80,10 @@ impl ToString for OperatorType { ToAddress => "TO_ADDRESS", MulAddress => "MUL_ADDRESS", AddAddress => "ADD_ADDRESS", + _ => "", } .to_string() + } } } @@ -159,7 +170,7 @@ impl WriteWasm for ComputeBucket { instructions.push(call("$Fr_toInt")); } _ => { - match &self.op { + match self.op { OperatorType::Add => { instructions.push(call("$Fr_add")); // Result, Argument, Argument } @@ -199,8 +210,45 @@ impl WriteWasm for ComputeBucket { OperatorType::Greater => { instructions.push(call("$Fr_gt")); } - OperatorType::Eq => { - instructions.push(call("$Fr_eq")); + OperatorType::Eq(n) => { + assert!(n != 0); + if n == 1 { + instructions.push(call("$Fr_eq")); + } else { + instructions.push(set_local(producer.get_aux_2_tag())); + instructions.push(set_local(producer.get_aux_1_tag())); + instructions.push(set_local(producer.get_aux_0_tag())); + instructions.push(set_constant(&n.to_string())); + instructions.push(set_local(producer.get_counter_tag())); + instructions.push(add_block()); + instructions.push(add_loop()); + instructions.push(get_local(producer.get_aux_0_tag())); + instructions.push(get_local(producer.get_aux_1_tag())); + instructions.push(get_local(producer.get_aux_2_tag())); + instructions.push(call("$Fr_eq")); + instructions.push(get_local(producer.get_aux_0_tag())); + instructions.push(call("$Fr_isTrue")); + instructions.push(eqz32()); + instructions.push(br_if("1")); + instructions.push(get_local(producer.get_counter_tag())); + instructions.push(set_constant("1")); + instructions.push(sub32()); + instructions.push(tee_local(producer.get_counter_tag())); + instructions.push(eqz32()); + instructions.push(br_if("1")); + instructions.push(get_local(producer.get_aux_1_tag())); + let s = producer.get_size_32_bits_in_memory() * 4; + instructions.push(set_constant(&s.to_string())); + instructions.push(add32()); + instructions.push(set_local(producer.get_aux_1_tag())); + instructions.push(get_local(producer.get_aux_2_tag())); + instructions.push(set_constant(&s.to_string())); + instructions.push(add32()); + instructions.push(set_local(producer.get_aux_2_tag())); + instructions.push(br("0")); + instructions.push(add_end()); + instructions.push(add_end()); + } } OperatorType::NotEq => { instructions.push(call("$Fr_neq")); @@ -262,7 +310,7 @@ impl WriteC for ComputeBucket { OperatorType::GreaterEq => "Fr_geq".to_string(), OperatorType::Lesser => "Fr_lt".to_string(), OperatorType::Greater => "Fr_gt".to_string(), - OperatorType::Eq => "Fr_eq".to_string(), + OperatorType::Eq(_) => "Fr_eq".to_string(), OperatorType::NotEq => "Fr_neq".to_string(), OperatorType::BoolOr => "Fr_lor".to_string(), OperatorType::BoolAnd => "Fr_land".to_string(), @@ -278,7 +326,6 @@ impl WriteC for ComputeBucket { let mut compute_c = vec![]; let mut operands = vec![]; - //compute_c.push(format!("// start of compute bucket {}",self.to_string())); let result; for instr in &self.stack { @@ -296,6 +343,34 @@ impl WriteC for ComputeBucket { OperatorType::ToAddress => { result = build_call("Fr_toInt".to_string(), operands); } + + OperatorType::Eq(n) => { + let exp_aux_index = self.op_aux_no.to_string(); + let operator = get_fr_op(self.op); + let result_ref = format!("&{}", expaux(exp_aux_index.clone())); + let mut arguments = vec![result_ref.clone()]; + let operands_copy = operands.clone(); + arguments.append(&mut operands); + compute_c.push(format!("{}; // line circom {}", build_call(operator.clone(), arguments),self.line.to_string())); + if *n > 1 { + compute_c.push(format!("uint index = 1;")); + compute_c.push(format!("while(index < {} && Fr_isTrue({})) {{", n, result_ref)); + operands = vec![]; + arguments = vec![result_ref.clone()]; + for operand in &operands_copy { + operands.push(format!("{} + index", operand)); + } + arguments.append(&mut operands); + compute_c.push(format!("{}; // line circom {}", build_call(operator.clone(), arguments),self.line.to_string())); + compute_c.push(format!("index++;")); + compute_c.push(format!("}}")); + + } + result = result_ref; + + + } + _ => { let exp_aux_index = self.op_aux_no.to_string(); // build assign diff --git a/compiler/src/intermediate_representation/store_bucket.rs b/compiler/src/intermediate_representation/store_bucket.rs index 8ccb7453f..537a740ac 100644 --- a/compiler/src/intermediate_representation/store_bucket.rs +++ b/compiler/src/intermediate_representation/store_bucket.rs @@ -53,6 +53,9 @@ impl WriteWasm for StoreBucket { fn produce_wasm(&self, producer: &WASMProducer) -> Vec { use code_producers::wasm_elements::wasm_code_generator::*; let mut instructions = vec![]; + if self.context.size == 0 { + return vec![]; + } if producer.needs_comments() { instructions.push(format!(";; store bucket. Line {}", self.line)); //.to_string() } @@ -403,8 +406,8 @@ impl WriteC for StoreBucket { AddressType::SubcmpSignal{ uniform_parallel_value, input_information, .. } => { // if subcomponent input check if run needed let sub_cmp_counter_decrease = format!( - "--{}->componentMemory[{}[{}]].inputCounter", - CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, cmp_index_ref + "{}->componentMemory[{}[{}]].inputCounter -= {}", + CIRCOM_CALC_WIT, MY_SUBCOMPONENTS, cmp_index_ref, self.context.size ); if let InputInformation::Input{status} = input_information { if let StatusInput::NoLast = status { diff --git a/compiler/src/intermediate_representation/translate.rs b/compiler/src/intermediate_representation/translate.rs index 6c13643b8..c44c17a0f 100644 --- a/compiler/src/intermediate_representation/translate.rs +++ b/compiler/src/intermediate_representation/translate.rs @@ -6,7 +6,7 @@ use num_bigint_dig::BigInt; use program_structure::ast::*; use program_structure::file_definition::FileLibrary; use program_structure::utils::environment::VarEnvironment; -use std::collections::{HashMap, BTreeMap}; +use std::collections::{HashMap, BTreeMap, HashSet}; type Length = usize; pub type E = VarEnvironment; @@ -15,13 +15,21 @@ pub type FieldTracker = ConstantTracker; pub struct SymbolInfo { access_instruction: InstructionPointer, dimensions: Vec, + is_component: bool, +} + +#[derive(Clone)] +pub struct SignalInfo{ + signal_type: SignalType, + lengths: Vec, } #[derive(Clone)] pub struct TemplateDB { // one per template instance pub signal_addresses: Vec, - pub signal_info: Vec>, + // stores the type and the length of signal + pub signal_info: Vec>, // template_name to usize pub indexes: HashMap, // one per generic template, gives its signal to code correspondence @@ -65,10 +73,12 @@ impl TemplateDB { 0, ConstantTracker::new(), HashMap::with_capacity(0), + instance.signals_to_tags.clone(), ); let mut signal_info = HashMap::new(); for signal in instance.signals.clone() { - signal_info.insert(signal.name, signal.xtype); + let info = SignalInfo{ signal_type: signal.xtype, lengths: signal.lengths}; + signal_info.insert(signal.name, info); } initialize_signals(&mut state, instance.signals.clone()); db.signal_addresses.push(state.environment); @@ -80,8 +90,9 @@ struct State { field_tracker: FieldTracker, environment: E, component_to_parallel: HashMap, - component_to_instance: HashMap, + component_to_instance: HashMap>, signal_to_type: HashMap, + signal_to_tags: BTreeMap, message_id: usize, signal_stack: usize, variable_stack: usize, @@ -99,11 +110,13 @@ impl State { cmp_id_offset: usize, field_tracker: FieldTracker, component_to_parallel: HashMap, + signal_to_tags: BTreeMap ) -> State { State { field_tracker, component_to_parallel, signal_to_type: HashMap::new(), + signal_to_tags, component_to_instance: HashMap::new(), environment: E::new(), message_id: msg_id, @@ -161,7 +174,7 @@ fn initialize_parameters(state: &mut State, params: Vec) { }; let address_instruction = address_instruction.allocate(); let symbol_info = - SymbolInfo { dimensions: lengths, access_instruction: address_instruction.clone() }; + SymbolInfo { dimensions: lengths, access_instruction: address_instruction.clone(), is_component:false }; state.environment.add_variable(&p.name, symbol_info); } } @@ -180,7 +193,7 @@ fn initialize_constants(state: &mut State, constants: Vec) { } .allocate(); let symbol_info = - SymbolInfo { access_instruction: address_instruction.clone(), dimensions }; + SymbolInfo { access_instruction: address_instruction.clone(), dimensions, is_component:false }; state.environment.add_variable(&arg.name, symbol_info); let mut index = 0; for value in arg.values { @@ -237,9 +250,9 @@ fn initialize_signals(state: &mut State, signals: Vec) { op_aux_no: 0, } .allocate(); - let info = SymbolInfo { access_instruction: instruction, dimensions: signal.lengths }; + let info = SymbolInfo { access_instruction: instruction, dimensions: signal.lengths, is_component:false }; state.environment.add_variable(&signal.name, info); - state.signal_to_type.insert(signal.name, signal.xtype); + state.signal_to_type.insert(signal.name.clone(), signal.xtype); } } @@ -255,7 +268,7 @@ fn initialize_components(state: &mut State, components: Vec) { op_aux_no: 0, } .allocate(); - let info = SymbolInfo { access_instruction: instruction, dimensions: component.lengths }; + let info = SymbolInfo { access_instruction: instruction, dimensions: component.lengths, is_component: true }; state.environment.add_variable(&component.name, info); } } @@ -264,7 +277,17 @@ fn initialize_components(state: &mut State, components: Vec) { fn create_components(state: &mut State, triggers: &[Trigger], clusters: Vec) { use ClusterType::*; for trigger in triggers { - state.component_to_instance.insert(trigger.component_name.clone(), trigger.template_id); + let component_info = state.component_to_instance.get_mut(&trigger.component_name); + match component_info{ + Some(info) =>{ + info.insert(trigger.template_id); + } + None =>{ + let mut new_info = HashSet::new(); + new_info.insert(trigger.template_id); + state.component_to_instance.insert(trigger.component_name.clone(), new_info); + } + } } for cluster in clusters { match cluster.xtype.clone() { @@ -532,7 +555,7 @@ fn translate_declaration(stmt: Statement, state: &mut State, context: &Context) op_aux_no: 0, } .allocate(); - let info = SymbolInfo { access_instruction: instruction, dimensions }; + let info = SymbolInfo { access_instruction: instruction, dimensions, is_component: false }; state.environment.add_variable(&name, info); } else { unreachable!() @@ -556,8 +579,15 @@ fn translate_block(stmt: Statement, state: &mut State, context: &Context) { fn translate_constraint_equality(stmt: Statement, state: &mut State, context: &Context) { use Statement::ConstraintEquality; + use Expression::Variable; if let ConstraintEquality { meta, lhe, rhe } = stmt { let starts_at = context.files.get_line(meta.start, meta.get_file_id()).unwrap(); + + let length = if let Variable { meta, name, access} = lhe.clone() { + let def = SymbolDef { meta, symbol: name, acc: access }; + ProcessedSymbol::new(def, state, context).length + } else {1}; + let lhe_pointer = translate_expression(lhe, state, context); let rhe_pointer = translate_expression(rhe, state, context); let stack = vec![lhe_pointer, rhe_pointer]; @@ -565,7 +595,7 @@ fn translate_constraint_equality(stmt: Statement, state: &mut State, context: &C line: starts_at, message_id: state.message_id, op_aux_no: 0, - op: OperatorType::Eq, + op: OperatorType::Eq(length), stack, } .allocate(); @@ -727,15 +757,45 @@ fn translate_prefix( } } +fn check_tag_access(name_signal: &String, access: &Vec, state: &mut State) -> Option { + use Access::*; + + let symbol_info = state.environment.get_variable(name_signal).unwrap().clone(); + let mut value_tag = None; + if !symbol_info.is_component{ + for acc in access { + match acc { + ArrayAccess(..) => {}, + ComponentAccess(name) => { + let tags_signal = state.signal_to_tags.get(name_signal).unwrap(); + let value = tags_signal.get(name).unwrap(); + + value_tag = if value.is_some() { + Some(value.clone().unwrap()) + } else { + unreachable!() + }; + } + } + } + } + value_tag +} + fn translate_variable( expression: Expression, state: &mut State, context: &Context, ) -> InstructionPointer { - use Expression::Variable; + use Expression::{Variable}; if let Variable { meta, name, access, .. } = expression { - let def = SymbolDef { meta, symbol: name, acc: access }; - ProcessedSymbol::new(def, state, context).into_load(state) + let tag_access = check_tag_access(&name, &access, state); + if tag_access.is_some(){ + translate_number( Expression::Number(meta.clone(), tag_access.unwrap()), state, context) + } else{ + let def = SymbolDef { meta, symbol: name, acc: access }; + ProcessedSymbol::new(def, state, context).into_load(state) + } } else { unreachable!() } @@ -778,7 +838,7 @@ fn translate_infix_operator(op: ExpressionInfixOpcode) -> OperatorType { GreaterEq => OperatorType::GreaterEq, Lesser => OperatorType::Lesser, Greater => OperatorType::Greater, - Eq => OperatorType::Eq, + Eq => OperatorType::Eq(1), NotEq => OperatorType::NotEq, BoolOr => OperatorType::BoolOr, BoolAnd => OperatorType::BoolAnd, @@ -860,6 +920,7 @@ impl ProcessedSymbol { let mut signal_type = state.signal_to_type.get(&symbol_name).cloned(); let mut bf_index = vec![]; let mut af_index = vec![]; + let mut multiple_possible_lengths: Vec> = vec![]; for acc in definition.acc { match acc { ArrayAccess(exp) if signal.is_none() => { @@ -868,16 +929,39 @@ impl ProcessedSymbol { bf_index.push(translate_expression(exp, state, context)); } ArrayAccess(exp) => { + for possible_length in &mut multiple_possible_lengths{ + possible_length.pop(); + } af_index.push(translate_expression(exp, state, context)); } ComponentAccess(name) => { - with_length = 1; - let cmp_id = *state.component_to_instance.get(&symbol_name).unwrap(); - signal_type = context.tmp_database.signal_info[cmp_id].get(&name).cloned(); + let possible_cmp_id = state.component_to_instance.get(&symbol_name).unwrap().clone(); + for cmp_id in possible_cmp_id{ + let aux = context.tmp_database.signal_info[cmp_id].get(&name).unwrap(); + signal_type = Some(aux.signal_type); + let mut new_length = aux.lengths.clone(); + new_length.reverse(); + multiple_possible_lengths.push(new_length); + } signal = Some(name); } } } + if signal.is_some(){ + let mut is_first = true; + for possible_length in multiple_possible_lengths{ + if is_first{ + with_length = possible_length.iter().fold(1, |r, c| r * (*c)); + is_first = false; + } + else{ + if with_length != possible_length.iter().fold(1, |r, c| r * (*c)){ + unreachable!("On development: Circom compiler does not accept for now the assignment of arrays of unknown sizes during the execution of loops"); + } + } + } + } + let signal_location = signal.map(|signal_name| { build_signal_location( &signal_name, @@ -1165,7 +1249,8 @@ pub struct CodeInfo<'a> { pub functions: &'a HashMap>, pub field_tracker: FieldTracker, pub component_to_parallel: HashMap, - pub string_table: HashMap + pub string_table: HashMap, + pub signals_to_tags: BTreeMap, } pub struct CodeOutput { @@ -1185,6 +1270,7 @@ pub fn translate_code(body: Statement, code_info: CodeInfo) -> CodeOutput { code_info.fresh_cmp_id, code_info.field_tracker, code_info.component_to_parallel, + code_info.signals_to_tags, ); state.string_table = code_info.string_table; initialize_components(&mut state, code_info.components); diff --git a/constraint_generation/Cargo.toml b/constraint_generation/Cargo.toml index 3c36a72e7..aacdce9df 100644 --- a/constraint_generation/Cargo.toml +++ b/constraint_generation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "constraint_generation" -version = "2.0.9" +version = "2.1.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/constraint_generation/src/compute_constants.rs b/constraint_generation/src/compute_constants.rs index 472189c57..f19acc535 100644 --- a/constraint_generation/src/compute_constants.rs +++ b/constraint_generation/src/compute_constants.rs @@ -1,5 +1,5 @@ use crate::environment_utils::environment::ExecutionEnvironment as EE; -use crate::environment_utils::slice_types::AExpressionSlice; +use crate::environment_utils::slice_types::{TagInfo, AExpressionSlice}; use circom_algebra::algebra::ArithmeticExpression; use compiler::hir::very_concrete_program::{Argument, TemplateInstance}; use num_bigint::BigInt; @@ -61,7 +61,7 @@ fn transform_header_into_environment(header: &[Argument]) -> EE { for arg in header { let name = arg.name.clone(); let slice = argument_into_slice(arg); - execution_environment.add_variable(&name, slice); + execution_environment.add_variable(&name, (TagInfo::new(), slice)); } execution_environment } @@ -83,6 +83,8 @@ fn treat_statement(stmt: &mut Statement, context: &Context, reports: &mut Report treat_conditional(stmt, context, reports, flag_verbose, prime) } else if stmt.is_while() { treat_while(stmt, context, reports, flag_verbose, prime) + } else if stmt.is_declaration(){ + treat_declaration(stmt, context, reports, flag_verbose, prime) } else { } } @@ -134,17 +136,25 @@ fn treat_conditional(stmt: &mut Statement, context: &Context, reports: &mut Repo fn treat_declaration(stmt: &mut Statement, context: &Context, reports: &mut ReportCollection, flag_verbose: bool, prime: &String) { use Statement::Declaration; - if let Declaration { meta, dimensions, .. } = stmt { + use program_structure::ast::VariableType::AnonymousComponent; + if let Declaration { meta, dimensions, xtype, .. } = stmt { let mut concrete_dimensions = vec![]; - for d in dimensions.iter_mut() { - let execution_response = treat_dimension(d, context, reports, flag_verbose, prime); - if let Option::Some(v) = execution_response { - concrete_dimensions.push(v); - } else { - report_invalid_dimension(meta, reports); + match xtype { + AnonymousComponent => { + meta.get_mut_memory_knowledge().set_concrete_dimensions(vec![]); + }, + _ => { + for d in dimensions.iter_mut() { + let execution_response = treat_dimension(d, context, reports, flag_verbose, prime); + if let Option::Some(v) = execution_response { + concrete_dimensions.push(v); + } else { + report_invalid_dimension(meta, reports); + } + } + meta.get_mut_memory_knowledge().set_concrete_dimensions(concrete_dimensions); } } - meta.get_mut_memory_knowledge().set_concrete_dimensions(concrete_dimensions); } else { unreachable!() } diff --git a/constraint_generation/src/environment_utils/component_representation.rs b/constraint_generation/src/environment_utils/component_representation.rs index b997a0dc4..c841fb431 100644 --- a/constraint_generation/src/environment_utils/component_representation.rs +++ b/constraint_generation/src/environment_utils/component_representation.rs @@ -1,14 +1,19 @@ -use super::slice_types::{MemoryError, SignalSlice, SliceCapacity}; +use super::slice_types::{MemoryError, SignalSlice, SliceCapacity,TagInfo}; use crate::execution_data::type_definitions::NodePointer; use crate::execution_data::ExecutedProgram; -use std::collections::HashMap; +use std::collections::{BTreeMap,HashMap, HashSet}; pub struct ComponentRepresentation { pub node_pointer: Option, is_parallel: bool, unassigned_inputs: HashMap, + unassigned_tags: HashSet, + to_assign_inputs: Vec<(String, Vec, Vec)>, inputs: HashMap, + pub inputs_tags: BTreeMap, outputs: HashMap, + pub outputs_tags: BTreeMap, + pub is_initialized: bool, } impl Default for ComponentRepresentation { @@ -17,8 +22,13 @@ impl Default for ComponentRepresentation { node_pointer: Option::None, is_parallel: false, unassigned_inputs: HashMap::new(), + unassigned_tags: HashSet::new(), + to_assign_inputs: Vec::new(), inputs: HashMap::new(), + inputs_tags: BTreeMap::new(), outputs: HashMap::new(), + outputs_tags: BTreeMap::new(), + is_initialized: false, } } } @@ -28,51 +38,105 @@ impl Clone for ComponentRepresentation { node_pointer: self.node_pointer, is_parallel: self.is_parallel, unassigned_inputs: self.unassigned_inputs.clone(), + unassigned_tags: self.unassigned_tags.clone(), + to_assign_inputs: self.to_assign_inputs.clone(), inputs: self.inputs.clone(), + inputs_tags: self.inputs_tags.clone(), outputs: self.outputs.clone(), + outputs_tags: self.outputs_tags.clone(), + is_initialized: self.is_initialized, } } } impl ComponentRepresentation { - pub fn initialize_component( + pub fn preinitialize_component( component: &mut ComponentRepresentation, is_parallel: bool, - node_pointer: NodePointer, + prenode_pointer: NodePointer, scheme: &ExecutedProgram, - ) -> Result<(), MemoryError> { - if component.is_initialized() { + is_anonymous_component: bool, + ) -> Result<(), MemoryError>{ + if !is_anonymous_component && component.is_preinitialized() { return Result::Err(MemoryError::AssignmentError); } + let possible_node = ExecutedProgram::get_prenode(scheme, prenode_pointer); + assert!(possible_node.is_some()); + let node = possible_node.unwrap(); + + let mut unassigned_tags = HashSet::new(); + let mut inputs_tags = BTreeMap::new(); + let mut outputs_tags = BTreeMap::new(); + for (symbol, tags) in node.inputs() { + if !tags.is_empty() { + unassigned_tags.insert(symbol.clone()); + } + let mut new_tags = TagInfo::new(); + for t in tags{ + new_tags.insert(t.clone(), Option::None); + } + inputs_tags.insert(symbol.clone(), new_tags); + } + + for (symbol, tags) in node.outputs() { + let mut new_tags = TagInfo::new(); + for t in tags{ + new_tags.insert(t.clone(), Option::None); + } + outputs_tags.insert(symbol.clone(), new_tags); + } + + *component = ComponentRepresentation { + node_pointer: Option::Some(prenode_pointer), + unassigned_inputs: HashMap::new(), + unassigned_tags, + to_assign_inputs: Vec::new(), + inputs_tags, + outputs_tags, + inputs: HashMap::new(), + outputs: HashMap::new(), + is_initialized: false, + is_parallel, + }; + Result::Ok(()) + } + + pub fn initialize_component( + component: &mut ComponentRepresentation, + node_pointer: NodePointer, + scheme: &ExecutedProgram, + ) -> Result<(), MemoryError> { let possible_node = ExecutedProgram::get_node(scheme, node_pointer); assert!(possible_node.is_some()); let node = possible_node.unwrap(); + component.is_initialized = true; - let mut unassigned_inputs = HashMap::new(); - let mut inputs = HashMap::new(); for (symbol, route) in node.inputs() { let signal_slice = SignalSlice::new_with_route(route, &false); let signal_slice_size = SignalSlice::get_number_of_cells(&signal_slice); if signal_slice_size > 0{ - unassigned_inputs + component.unassigned_inputs .insert(symbol.clone(), signal_slice_size); } - inputs.insert(symbol.clone(), signal_slice); + component.inputs.insert(symbol.clone(), signal_slice); } - let mut outputs = HashMap::new(); for (symbol, route) in node.outputs() { - outputs.insert(symbol.clone(), SignalSlice::new_with_route(route, &true)); + component.outputs.insert(symbol.clone(), SignalSlice::new_with_route(route, &true)); + let tags_output = node.signal_to_tags.get(symbol); + if tags_output.is_some(){ + component.outputs_tags.insert(symbol.to_string(), tags_output.unwrap().clone()); + } + } + component.node_pointer = Option::Some(node_pointer); + let to_assign = component.to_assign_inputs.clone(); + + for s in to_assign{ + ComponentRepresentation::assign_value_to_signal_init(component, &s.0, &s.1, &s.2)?; } - *component = ComponentRepresentation { - node_pointer: Option::Some(node_pointer), - is_parallel, - unassigned_inputs, - inputs, - outputs, - }; Result::Ok(()) } +/* pub fn signal_has_value( component: &ComponentRepresentation, signal_name: &str, @@ -84,16 +148,27 @@ impl ComponentRepresentation { if component.outputs.contains_key(signal_name) && !component.unassigned_inputs.is_empty() { return Result::Err(MemoryError::InvalidAccess); } + if !component.is_initialized{ + return Result::Err(MemoryError::InvalidAccess); + } let slice = if component.inputs.contains_key(signal_name) { component.inputs.get(signal_name).unwrap() } else { component.outputs.get(signal_name).unwrap() }; - let enabled = *SignalSlice::get_reference_to_single_value(slice, access)?; + + let enabled_slice = SignalSlice::access_values(&slice, &access)?; + let mut enabled = false; + for i in 0..SignalSlice::get_number_of_cells(&enabled_slice) { + enabled |= SignalSlice::get_reference_to_single_value_by_index(&enabled_slice, i)?; + } Result::Ok(enabled) } - pub fn get_signal(&self, signal_name: &str) -> Result<&SignalSlice, MemoryError> { +*/ + + pub fn get_signal(&self, signal_name: &str) -> Result<(&TagInfo, &SignalSlice), MemoryError> { + if self.node_pointer.is_none() { return Result::Err(MemoryError::InvalidAccess); } @@ -101,10 +176,14 @@ impl ComponentRepresentation { return Result::Err(MemoryError::InvalidAccess); } + if !self.is_initialized { + return Result::Err(MemoryError::InvalidAccess); + } + let slice = if self.inputs.contains_key(signal_name) { - self.inputs.get(signal_name).unwrap() + (self.inputs_tags.get(signal_name).unwrap(), self.inputs.get(signal_name).unwrap()) } else { - self.outputs.get(signal_name).unwrap() + (self.outputs_tags.get(signal_name).unwrap(), self.outputs.get(signal_name).unwrap()) }; Result::Ok(slice) } @@ -113,24 +192,114 @@ impl ComponentRepresentation { component: &mut ComponentRepresentation, signal_name: &str, access: &[SliceCapacity], + slice_route: &[SliceCapacity], + tags: TagInfo, ) -> Result<(), MemoryError> { - let signal_has_value = - ComponentRepresentation::signal_has_value(component, signal_name, access)?; - if signal_has_value { - return Result::Err(MemoryError::AssignmentError); + if !component.is_initialized{ + ComponentRepresentation::assign_value_to_signal_no_init( + component, + signal_name, + access, + slice_route, + tags + ) + } else { + ComponentRepresentation::assign_value_to_signal_init( + component, + signal_name, + access, + slice_route, + ) } + } + + pub fn assign_value_to_signal_no_init( + component: &mut ComponentRepresentation, + signal_name: &str, + access: &[SliceCapacity], + slice_route: &[SliceCapacity], + tags: TagInfo, + ) -> Result<(), MemoryError> { + + // We copy tags in any case, complete or incomplete assignment + // The values of the tags must be the same than the ones stored before + let tags_input = component.inputs_tags.get_mut(signal_name).unwrap(); - let slice = component.inputs.get_mut(signal_name).unwrap(); - let value = SignalSlice::get_mut_reference_to_single_value(slice, access)?; - let left = component.unassigned_inputs.get_mut(signal_name).unwrap(); - *left -= 1; - *value = true; - if *left == 0 { - component.unassigned_inputs.remove(signal_name); + for (t, value) in tags_input{ + if !tags.contains_key(t){ + return Result::Err(MemoryError::AssignmentMissingTags); + } else{ + if component.unassigned_tags.contains(signal_name){ + *value = tags.get(t).unwrap().clone(); + component.unassigned_tags.remove(signal_name); + } + else{ + // already given a value, check that it is the same + if value != tags.get(t).unwrap(){ + return Result::Err(MemoryError::AssignmentTagTwice); + } + } + } } + component.to_assign_inputs.push((signal_name.to_string(), access.to_vec(), slice_route.to_vec())); Result::Ok(()) } - pub fn is_initialized(&self) -> bool { + + pub fn assign_value_to_signal_init( + component: &mut ComponentRepresentation, + signal_name: &str, + access: &[SliceCapacity], + slice_route: &[SliceCapacity], + ) -> Result<(), MemoryError> { + + let inputs_response = component.inputs.get_mut(signal_name).unwrap(); + let signal_previous_value = SignalSlice::access_values( + inputs_response, + &access, + )?; + + let new_value_slice = &SignalSlice::new_with_route(slice_route, &true); + + SignalSlice::check_correct_dims( + &signal_previous_value, + &Vec::new(), + &new_value_slice, + true + )?; + + for i in 0..SignalSlice::get_number_of_cells(&signal_previous_value){ + let signal_was_assigned = SignalSlice::access_value_by_index(&signal_previous_value, i)?; + if signal_was_assigned { + return Result::Err(MemoryError::AssignmentError); + } + } + + SignalSlice::insert_values( + inputs_response, + &access, + &new_value_slice, + true + )?; + let dim = SignalSlice::get_number_of_cells(new_value_slice); + match component.unassigned_inputs.get_mut(signal_name){ + Some(left) => { + *left -= dim; + if *left == 0 { + component.unassigned_inputs.remove(signal_name); + } + } + None => {} + } + + Result::Ok(()) + + } + pub fn is_preinitialized(&self) -> bool { self.node_pointer.is_some() } + + pub fn is_ready_initialize(&self) -> bool { + self.unassigned_tags.is_empty() + } + } diff --git a/constraint_generation/src/environment_utils/environment.rs b/constraint_generation/src/environment_utils/environment.rs index f9cc24f9b..0b2684480 100644 --- a/constraint_generation/src/environment_utils/environment.rs +++ b/constraint_generation/src/environment_utils/environment.rs @@ -1,10 +1,15 @@ use super::slice_types::{ - AExpressionSlice, ComponentRepresentation, ComponentSlice, SignalSlice, SliceCapacity, + AExpressionSlice, + ComponentRepresentation, + ComponentSlice, + SignalSlice, + SliceCapacity, + TagInfo }; use super::{ArithmeticExpression, CircomEnvironment, CircomEnvironmentError}; pub type ExecutionEnvironmentError = CircomEnvironmentError; -pub type ExecutionEnvironment = CircomEnvironment; +pub type ExecutionEnvironment = CircomEnvironment; pub fn environment_shortcut_add_component( environment: &mut ExecutionEnvironment, @@ -18,25 +23,28 @@ pub fn environment_shortcut_add_input( environment: &mut ExecutionEnvironment, input_name: &str, dimensions: &[SliceCapacity], + tags: &TagInfo, ) { let slice = SignalSlice::new_with_route(dimensions, &true); - environment.add_input(input_name, slice); + environment.add_input(input_name, (tags.clone(), slice)); } pub fn environment_shortcut_add_output( environment: &mut ExecutionEnvironment, output_name: &str, dimensions: &[SliceCapacity], + tags: &TagInfo, ) { let slice = SignalSlice::new_with_route(dimensions, &false); - environment.add_output(output_name, slice); + environment.add_output(output_name, (tags.clone(), slice)); } pub fn environment_shortcut_add_intermediate( environment: &mut ExecutionEnvironment, intermediate_name: &str, dimensions: &[SliceCapacity], + tags: &TagInfo, ) { let slice = SignalSlice::new_with_route(dimensions, &false); - environment.add_intermediate(intermediate_name, slice); + environment.add_intermediate(intermediate_name, (tags.clone(), slice)); } pub fn environment_shortcut_add_variable( environment: &mut ExecutionEnvironment, @@ -44,5 +52,5 @@ pub fn environment_shortcut_add_variable( dimensions: &[SliceCapacity], ) { let slice = AExpressionSlice::new_with_route(dimensions, &ArithmeticExpression::default()); - environment.add_variable(variable_name, slice); + environment.add_variable(variable_name, (TagInfo::new(), slice)); } diff --git a/constraint_generation/src/environment_utils/slice_types.rs b/constraint_generation/src/environment_utils/slice_types.rs index c9783f3c9..5d1052b5a 100644 --- a/constraint_generation/src/environment_utils/slice_types.rs +++ b/constraint_generation/src/environment_utils/slice_types.rs @@ -3,6 +3,9 @@ pub use super::memory_slice::MemorySlice; pub use super::memory_slice::{MemoryError, SliceCapacity}; pub use circom_algebra::algebra::ArithmeticExpression; pub use num_bigint::BigInt; +use std::collections::BTreeMap; + +pub type TagInfo = BTreeMap>; pub type AExpressionSlice = MemorySlice>; // The boolean is true if the signal contains a value pub type SignalSlice = MemorySlice; diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index 5c5a6bde4..3855cd572 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -6,20 +6,21 @@ use super::environment_utils::{ }, slice_types::{ AExpressionSlice, ArithmeticExpression as ArithmeticExpressionGen, ComponentRepresentation, - ComponentSlice, MemoryError, MemorySlice, SignalSlice, SliceCapacity, + ComponentSlice, MemoryError, MemorySlice, SignalSlice, SliceCapacity, TagInfo }, }; use program_structure::constants::UsefulConstants; use super::execution_data::analysis::Analysis; -use super::execution_data::{ExecutedProgram, ExecutedTemplate, NodePointer}; +use super::execution_data::{ExecutedProgram, ExecutedTemplate, PreExecutedTemplate, NodePointer}; use super::{ ast::*, ArithmeticError, FileID, ProgramArchive, Report, ReportCode, ReportCollection }; use circom_algebra::num_bigint::BigInt; -use std::collections::BTreeMap; +use std::collections::{HashMap, BTreeMap}; type AExpr = ArithmeticExpressionGen; +type AnonymousComponentsInfo = BTreeMap)>; #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] enum BlockType { @@ -37,6 +38,7 @@ struct RuntimeInformation { pub runtime_errors: ReportCollection, pub environment: ExecutionEnvironment, pub exec_program: ExecutedProgram, + pub anonymous_components: AnonymousComponentsInfo, } impl RuntimeInformation { pub fn new(current_file: FileID, id_max: usize, prime: &String) -> RuntimeInformation { @@ -50,6 +52,7 @@ impl RuntimeInformation { runtime_errors: ReportCollection::new(), environment: ExecutionEnvironment::new(), exec_program: ExecutedProgram::new(prime), + anonymous_components: AnonymousComponentsInfo::new(), } } } @@ -58,6 +61,7 @@ struct FoldedValue { pub arithmetic_slice: Option, pub node_pointer: Option, pub is_parallel: Option, + pub tags: Option, } impl FoldedValue { pub fn valid_arithmetic_slice(f_value: &FoldedValue) -> bool { @@ -70,7 +74,7 @@ impl FoldedValue { impl Default for FoldedValue { fn default() -> Self { - FoldedValue { arithmetic_slice: Option::None, node_pointer: Option::None, is_parallel: Option::None } + FoldedValue { arithmetic_slice: Option::None, node_pointer: Option::None, is_parallel: Option::None, tags: Option::None } } } @@ -87,15 +91,35 @@ enum ExecutionWarning { pub fn constraint_execution( program_archive: &ProgramArchive, flag_verbose: bool, - prime: &String,) -> Result<(ExecutedProgram, ReportCollection), ReportCollection> { let main_file_id = program_archive.get_file_id_main(); + prime: &String, +) -> Result<(ExecutedProgram, ReportCollection), ReportCollection> { + let main_file_id = program_archive.get_file_id_main(); let mut runtime_information = RuntimeInformation::new(*main_file_id, program_archive.id_max, prime); + use Expression::Call; + runtime_information.public_inputs = program_archive.get_public_inputs_main_component().clone(); - let folded_value_result = execute_expression( - program_archive.get_main_expression(), - program_archive, - &mut runtime_information, - flag_verbose - ); + + let folded_value_result = + 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); + arg_values.push(safe_unwrap_to_arithmetic_slice(f_arg.unwrap(), line!())); + // improve + } + execute_template_call_complete( + id, + arg_values, + BTreeMap::new(), + program_archive, + &mut runtime_information, + flag_verbose, + ) + } else { + unreachable!("The main expression should be a call."); + }; + + match folded_value_result { Result::Err(_) => Result::Err(runtime_information.runtime_errors), Result::Ok(folded_value) => { @@ -142,61 +166,82 @@ fn execute_statement( let id = stmt.get_meta().elem_id; Analysis::reached(&mut runtime.analysis, id); let res = match stmt { + MultSubstitution { .. } => unreachable!(), InitializationBlock { initializations, .. } => { let possible_fold = execute_sequence_of_statements( initializations, program_archive, runtime, actual_node, - flag_verbose + flag_verbose, + false )?; debug_assert!(possible_fold.is_none()); possible_fold } Declaration { meta, xtype, name, dimensions, .. } => { - let mut arithmetic_values = Vec::new(); - for dimension in dimensions.iter() { - let f_dimensions = execute_expression(dimension, program_archive, runtime, flag_verbose)?; - arithmetic_values - .push(safe_unwrap_to_single_arithmetic_expression(f_dimensions, line!())); - } - treat_result_with_memory_error_void( - valid_array_declaration(&arithmetic_values), - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - let usable_dimensions = - if let Option::Some(dimensions) = cast_indexing(&arithmetic_values) { - dimensions - } else { - let err = Result::Err(ExecutionError::ArraySizeTooBig); - treat_result_with_execution_error( - err, + match xtype { + VariableType::AnonymousComponent => { + execute_anonymous_component_declaration( + name, + meta.clone(), + &dimensions, + &mut runtime.environment, + &mut runtime.anonymous_components, + ); + } + _ => { + let mut arithmetic_values = Vec::new(); + for dimension in dimensions.iter() { + let f_dimensions = + execute_expression(dimension, program_archive, runtime, flag_verbose)?; + arithmetic_values + .push(safe_unwrap_to_single_arithmetic_expression(f_dimensions, line!())); + } + treat_result_with_memory_error_void( + valid_array_declaration(&arithmetic_values), meta, &mut runtime.runtime_errors, &runtime.call_trace, - )? - }; - match xtype { - VariableType::Component => 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, _) => execute_signal_declaration( - name, - &usable_dimensions, - *signal_type, - &mut runtime.environment, - actual_node, - ), + )?; + let usable_dimensions = + if let Option::Some(dimensions) = cast_indexing(&arithmetic_values) { + dimensions + } else { + let err = Result::Err(ExecutionError::ArraySizeTooBig); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + }; + match xtype { + VariableType::Component => 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, + ), + _ =>{ + unreachable!() + } + } + + } } Option::None } @@ -204,39 +249,60 @@ fn execute_statement( let access_information = treat_accessing(meta, access, program_archive, runtime, flag_verbose)?; let r_folded = execute_expression(rhe, program_archive, runtime, flag_verbose)?; let possible_constraint = - perform_assign(meta, var, &access_information, r_folded, actual_node, runtime)?; - if let Option::Some(node) = actual_node { - if let AssignOp::AssignConstraintSignal = op { + perform_assign(meta, var, *op, &access_information, r_folded, actual_node, runtime, program_archive, flag_verbose)?; + if let (Option::Some(node), AssignOp::AssignConstraintSignal) = (actual_node, op) { debug_assert!(possible_constraint.is_some()); let constrained = possible_constraint.unwrap(); - if constrained.right.is_nonquadratic() { - let err = Result::Err(ExecutionError::NonQuadraticConstraint); - treat_result_with_execution_error( - err, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - } else { - let p = runtime.constants.get_p().clone(); - let symbol = AExpr::Signal { symbol: constrained.left }; - let expr = AExpr::sub(&symbol, &constrained.right, &p); - let ctr = AExpr::transform_expression_to_constraint_form(expr, &p).unwrap(); - node.add_constraint(ctr); - } - } else if let AssignOp::AssignSignal = op { - debug_assert!(possible_constraint.is_some()); - let constrained = possible_constraint.unwrap(); - if !constrained.right.is_nonquadratic() && !node.is_custom_gate { - let err : Result<(),ExecutionWarning> = Result::Err(ExecutionWarning::CanBeQuadraticConstraint); - treat_result_with_execution_warning( - err, + for i in 0..AExpressionSlice::get_number_of_cells(&constrained.right){ + let value_right = treat_result_with_memory_error( + AExpressionSlice::access_value_by_index(&constrained.right, i), meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; + + if let AssignOp::AssignConstraintSignal = op { + let access_left = treat_result_with_memory_error( + AExpressionSlice::get_access_index(&constrained.right, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + let full_symbol = format!("{}{}", + constrained.left, + create_index_appendix(&access_left), + ); + + if value_right.is_nonquadratic() { + let err = Result::Err(ExecutionError::NonQuadraticConstraint); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } else { + let p = runtime.constants.get_p().clone(); + let symbol = AExpr::Signal { symbol: full_symbol }; + let expr = AExpr::sub(&symbol, &value_right, &p); + let ctr = AExpr::transform_expression_to_constraint_form(expr, &p).unwrap(); + node.add_constraint(ctr); + } + } + else if let AssignOp::AssignSignal = op { + //debug_assert!(possible_constraint.is_some()); + if !value_right.is_nonquadratic() && !node.is_custom_gate { + let err : Result<(),ExecutionWarning> = Result::Err(ExecutionWarning::CanBeQuadraticConstraint); + treat_result_with_execution_warning( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + } } - } } Option::None } @@ -244,26 +310,52 @@ fn execute_statement( 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 arith_left = safe_unwrap_to_single_arithmetic_expression(f_left, line!()); - let arith_right = safe_unwrap_to_single_arithmetic_expression(f_right, line!()); - let possible_non_quadratic = - AExpr::sub(&arith_left, &arith_right, &runtime.constants.get_p()); - if possible_non_quadratic.is_nonquadratic() { - treat_result_with_execution_error( - Result::Err(ExecutionError::NonQuadraticConstraint), + 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, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + for i in 0..AExpressionSlice::get_number_of_cells(&arith_left){ + let value_left = treat_result_with_memory_error( + AExpressionSlice::access_value_by_index(&arith_left, i), meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - } - let quadratic_expression = possible_non_quadratic; - let constraint_expression = AExpr::transform_expression_to_constraint_form( - quadratic_expression, - runtime.constants.get_p(), - ) - .unwrap(); - if let Option::Some(node) = actual_node { - node.add_constraint(constraint_expression); + let value_right = treat_result_with_memory_error( + AExpressionSlice::access_value_by_index(&arith_right, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let possible_non_quadratic = + AExpr::sub( + &value_left, + &value_right, + &runtime.constants.get_p() + ); + if possible_non_quadratic.is_nonquadratic() { + treat_result_with_execution_error( + Result::Err(ExecutionError::NonQuadraticConstraint), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + let quadratic_expression = possible_non_quadratic; + let constraint_expression = AExpr::transform_expression_to_constraint_form( + quadratic_expression, + runtime.constants.get_p(), + ) + .unwrap(); + if let Option::Some(node) = actual_node { + node.add_constraint(constraint_expression); + } } Option::None } @@ -320,7 +412,7 @@ 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)?; + execute_sequence_of_statements(stmts, program_archive, runtime, actual_node, flag_verbose, false)?; ExecutionEnvironment::remove_variable_block(&mut runtime.environment); return_value } @@ -413,6 +505,7 @@ fn execute_expression( &mut array_slice, &[row], &arithmetic_slice_array[row], + false ); treat_result_with_memory_error_void( memory_insert_result, @@ -448,6 +541,7 @@ fn execute_expression( &mut array_slice, &[row], &slice_value, + false ); treat_result_with_memory_error_void( memory_insert_result, @@ -493,33 +587,7 @@ fn execute_expression( } } Call { id, args, .. } => { - let mut arg_values = Vec::new(); - for arg_expression in args.iter() { - let f_arg = execute_expression(arg_expression, program_archive, runtime, flag_verbose)?; - arg_values.push(safe_unwrap_to_arithmetic_slice(f_arg, line!())); - } - let new_environment = prepare_environment_for_call(id, &arg_values, program_archive); - let previous_environment = std::mem::replace(&mut runtime.environment, new_environment); - let previous_block_type = std::mem::replace(&mut runtime.block_type, BlockType::Known); - - let new_file_id = if program_archive.contains_function(id) { - program_archive.get_function_data(id).get_file_id() - } else { - program_archive.get_template_data(id).get_file_id() - }; - let previous_id = std::mem::replace(&mut runtime.current_file, new_file_id); - - runtime.call_trace.push(id.clone()); - let folded_result = if program_archive.contains_function(id) { - execute_function_call(id, program_archive, runtime, flag_verbose)? - } else { - execute_template_call(id, &arg_values, program_archive, runtime, flag_verbose)? - }; - runtime.environment = previous_environment; - runtime.current_file = previous_id; - runtime.block_type = previous_block_type; - runtime.call_trace.pop(); - folded_result + execute_call(id, args, program_archive, runtime, flag_verbose)? } ParallelOp{rhe, ..} => { let folded_value = execute_expression(rhe, program_archive, runtime, flag_verbose)?; @@ -527,6 +595,7 @@ fn execute_expression( safe_unwrap_to_valid_node_pointer(folded_value, line!()); FoldedValue { node_pointer: Option::Some(node_pointer), is_parallel: Option::Some(true), ..FoldedValue::default() } } + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } }; let expr_id = expr.get_meta().elem_id; let res_p = res.arithmetic_slice.clone(); @@ -539,8 +608,76 @@ fn execute_expression( Result::Ok(res) } + //************************************************* Statement execution support ************************************************* +fn execute_call( + id: &String, + args: &Vec, + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, + flag_verbose: bool, +) -> Result { + let mut arg_values = Vec::new(); + for arg_expression in args.iter() { + let f_arg = execute_expression(arg_expression, program_archive, runtime, flag_verbose)?; + arg_values.push(safe_unwrap_to_arithmetic_slice(f_arg, line!())); + } + if program_archive.contains_function(id){ // in this case we execute + let new_environment = prepare_environment_for_call(id, &arg_values, program_archive); + let previous_environment = std::mem::replace(&mut runtime.environment, new_environment); + let previous_block_type = std::mem::replace(&mut runtime.block_type, BlockType::Known); + let previous_anonymous_components = std::mem::replace(&mut runtime.anonymous_components, AnonymousComponentsInfo::new()); + + let new_file_id = program_archive.get_function_data(id).get_file_id(); + 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)?; + + runtime.environment = previous_environment; + runtime.current_file = previous_id; + runtime.block_type = previous_block_type; + runtime.anonymous_components = previous_anonymous_components; + runtime.call_trace.pop(); + 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) + } +} + +fn execute_template_call_complete( + id: &String, + arg_values: Vec, + tags: BTreeMap, + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, + flag_verbose: bool, +) -> Result { + if program_archive.contains_template(id){ // in this case we execute + let new_environment = prepare_environment_for_call(id, &arg_values, program_archive); + let previous_environment = std::mem::replace(&mut runtime.environment, new_environment); + let previous_block_type = std::mem::replace(&mut runtime.block_type, BlockType::Known); + let previous_anonymous_components = std::mem::replace(&mut runtime.anonymous_components, AnonymousComponentsInfo::new()); + + let new_file_id = program_archive.get_template_data(id).get_file_id(); + 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)?; + + runtime.environment = previous_environment; + runtime.current_file = previous_id; + runtime.block_type = previous_block_type; + runtime.anonymous_components = previous_anonymous_components; + runtime.call_trace.pop(); + Ok(folded_result) + } else { + unreachable!(); + } +} + fn execute_component_declaration( component_name: &str, dimensions: &[SliceCapacity], @@ -555,27 +692,47 @@ fn execute_component_declaration( } } +fn execute_anonymous_component_declaration( + component_name: &str, + meta: Meta, + dimensions: &Vec, + environment: &mut ExecutionEnvironment, + anonymous_components: &mut AnonymousComponentsInfo, +) { + environment_shortcut_add_component(environment, component_name, &Vec::new()); + anonymous_components.insert(component_name.to_string(), (meta, dimensions.clone())); +} + fn execute_signal_declaration( signal_name: &str, dimensions: &[SliceCapacity], + list_tags: &Vec, signal_type: SignalType, environment: &mut ExecutionEnvironment, actual_node: &mut Option, ) { use SignalType::*; + let mut tags = TagInfo::new(); + for t in list_tags{ + tags.insert(t.clone(), None); + } if let Option::Some(node) = actual_node { node.add_ordered_signal(signal_name, dimensions); match signal_type { Input => { - environment_shortcut_add_input(environment, signal_name, dimensions); + if let Some(tags_input) = node.tag_instances().get(signal_name){ + environment_shortcut_add_input(environment, signal_name, dimensions, &tags_input); + } else{ + environment_shortcut_add_input(environment, signal_name, dimensions, &tags); + } node.add_input(signal_name, dimensions); } Output => { - environment_shortcut_add_output(environment, signal_name, dimensions); + environment_shortcut_add_output(environment, signal_name, dimensions, &tags); node.add_output(signal_name, dimensions); } Intermediate => { - environment_shortcut_add_intermediate(environment, signal_name, dimensions); + environment_shortcut_add_intermediate(environment, signal_name, dimensions, &tags); node.add_intermediate(signal_name, dimensions); } } @@ -590,51 +747,62 @@ fn execute_signal_declaration( */ struct Constrained { left: String, - right: AExpr, + right: AExpressionSlice, } fn perform_assign( meta: &Meta, symbol: &str, + op: AssignOp, accessing_information: &AccessingInformation, r_folded: FoldedValue, actual_node: &mut Option, runtime: &mut RuntimeInformation, + program_archive: &ProgramArchive, + flag_verbose: bool ) -> Result, ()> { use super::execution_data::type_definitions::SubComponentData; - let environment = &mut runtime.environment; let full_symbol = create_symbol(symbol, &accessing_information); - let possible_arithmetic_expression = if ExecutionEnvironment::has_variable(environment, symbol) + let possible_arithmetic_slice = if ExecutionEnvironment::has_variable(&runtime.environment, symbol) { debug_assert!(accessing_information.signal_access.is_none()); debug_assert!(accessing_information.after_signal.is_empty()); - let environment_result = ExecutionEnvironment::get_mut_variable_mut(environment, symbol); - let symbol_content = treat_result_with_environment_error( + let environment_result = ExecutionEnvironment::get_mut_variable_mut(&mut runtime.environment, symbol); + let (symbol_tags, symbol_content) = treat_result_with_environment_error( environment_result, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; + let mut r_tags = if r_folded.tags.is_some(){ + r_folded.tags.as_ref().unwrap().clone() + } else{ + TagInfo::new() + }; let mut r_slice = safe_unwrap_to_arithmetic_slice(r_folded, line!()); if runtime.block_type == BlockType::Unknown { r_slice = AExpressionSlice::new_with_route(r_slice.route(), &AExpr::NonQuadratic); + r_tags = TagInfo::new(); } if accessing_information.undefined { let new_value = AExpressionSlice::new_with_route(symbol_content.route(), &AExpr::NonQuadratic); let memory_result = - AExpressionSlice::insert_values(symbol_content, &vec![], &new_value); + AExpressionSlice::insert_values(symbol_content, &vec![], &new_value, false); treat_result_with_memory_error_void( memory_result, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; + *symbol_tags = TagInfo::new(); } else { + let memory_result = AExpressionSlice::insert_values( symbol_content, &accessing_information.before_signal, &r_slice, + false ); treat_result_with_memory_error_void( memory_result, @@ -642,14 +810,85 @@ fn perform_assign( &mut runtime.runtime_errors, &runtime.call_trace, )?; + // in case it is a complete assignment assign the tags, if not set the tags to empty + if AExpressionSlice::get_number_of_cells(symbol_content) == AExpressionSlice::get_number_of_cells(&r_slice){ + *symbol_tags = r_tags; + } else { + *symbol_tags = TagInfo::new(); + } + } + Option::None + } else if ExecutionEnvironment::has_signal(&runtime.environment, symbol) && + accessing_information.signal_access.is_some() { + if ExecutionEnvironment::has_input(&runtime.environment, symbol) { + treat_result_with_memory_error( + Result::Err(MemoryError::AssignmentTagInput), + 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_signal_content) = treat_result_with_environment_error( + environment_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let memory_response_for_signal_previous_value = SignalSlice::access_values( + reference_to_signal_content, + &accessing_information.before_signal, + ); + let signal_previous_value = treat_result_with_memory_error( + memory_response_for_signal_previous_value, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + if signal_is_initialized(&signal_previous_value, meta, &mut runtime.runtime_errors, &runtime.call_trace) { + treat_result_with_memory_error( + Result::Err(MemoryError::AssignmentTagAfterInit), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + } + else if let Some(a_slice) = r_folded.arithmetic_slice { + let value = AExpressionSlice::unwrap_to_single(a_slice); + match value { + ArithmeticExpressionGen::Number { value } => { + let possible_tag = reference_to_tags.get(&tag.clone()); + if let Some(val) = possible_tag { + if let Some(_) = val { + treat_result_with_memory_error( + Result::Err(MemoryError::AssignmentTagTwice), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + } else { + reference_to_tags.insert(tag.clone(), Option::Some(value.clone())); + if let Option::Some(node) = actual_node{ + node.add_tag_signal(symbol, &tag, Some(value)); + } else{ + unreachable!(); + } + + } + } else {unreachable!()} + }, + _ => unreachable!(), + } } + else { unreachable!() } Option::None - } else if ExecutionEnvironment::has_signal(environment, symbol) { + } else if ExecutionEnvironment::has_signal(&runtime.environment, symbol) { debug_assert!(accessing_information.signal_access.is_none()); debug_assert!(accessing_information.after_signal.is_empty()); - let environment_response = ExecutionEnvironment::get_mut_signal_res(environment, symbol); - let reference_to_signal_content = treat_result_with_environment_error( + let environment_response = ExecutionEnvironment::get_mut_signal_res(&mut runtime.environment, symbol); + let (reference_to_tags, reference_to_signal_content) = treat_result_with_environment_error( environment_response, meta, &mut runtime.runtime_errors, @@ -665,36 +904,136 @@ fn perform_assign( &mut runtime.runtime_errors, &runtime.call_trace, )?; - debug_assert!(signal_previous_value.is_single()); - let signal_was_assigned = SignalSlice::unwrap_to_single(signal_previous_value); - let access_response = if signal_was_assigned { - Result::Err(MemoryError::AssignmentError) - } else { - SignalSlice::insert_values( - reference_to_signal_content, - &accessing_information.before_signal, - &SignalSlice::new(&true), - ) - }; + + // Study the tags: add the new ones and copy their content. + // The case of inputs is studied in other place + if r_folded.tags.is_some() && op == AssignOp::AssignConstraintSignal{ + let rfolded_tags = r_folded.tags.as_ref().unwrap(); + if signal_is_initialized(&signal_previous_value, meta, &mut runtime.runtime_errors, &runtime.call_trace) { + // check that all the tags have the same values + for (tag, value) in reference_to_tags{ + match rfolded_tags.get(tag){ + Some(value_rfolded) => { + if value != value_rfolded { + treat_result_with_memory_error( + Result::Err(MemoryError::AssignmentTagTwice), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + } + }, + None => {}, + } + } + } else { + for (tag, value) in r_folded.tags.as_ref().unwrap() { + let possible_tag = reference_to_tags.get(tag); + if let Some(val) = possible_tag { + if let Some(_) = val { + treat_result_with_memory_error( + Result::Err(MemoryError::AssignmentTagTwice), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + } else { + reference_to_tags.insert(tag.clone(), value.clone()); + if let Option::Some(node) = actual_node{ + node.add_tag_signal(symbol, &tag, value.clone()); + } else{ + unreachable!(); + } + } + } + /* else { // it is a tag that does not belong to the value + + reference_to_tags.insert(tag.clone(), value.clone()); + if let Option::Some(node) = actual_node{ + node.add_tag_signal(symbol, &tag, value.clone()); + } else{ + unreachable!(); + } + } + */ + } + } + } + + let r_slice = safe_unwrap_to_arithmetic_slice(r_folded, line!()); + let new_value_slice = &SignalSlice::new_with_route(r_slice.route(), &true); + + let correct_dims_result = SignalSlice::check_correct_dims( + &signal_previous_value, + &Vec::new(), + &new_value_slice, + true + ); + treat_result_with_memory_error_void( + correct_dims_result, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + for i in 0..SignalSlice::get_number_of_cells(&signal_previous_value){ + let signal_was_assigned = treat_result_with_memory_error( + SignalSlice::access_value_by_index(&signal_previous_value, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + if signal_was_assigned { + let access_response = Result::Err(MemoryError::AssignmentError); + treat_result_with_memory_error( + access_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + } + + let access_response = SignalSlice::insert_values( + reference_to_signal_content, + &accessing_information.before_signal, + &new_value_slice, + true + ); + + treat_result_with_memory_error_void( access_response, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - Option::Some(safe_unwrap_to_single_arithmetic_expression(r_folded, line!())) - } else if ExecutionEnvironment::has_component(environment, symbol) { - let environment_response = ExecutionEnvironment::get_mut_component_res(environment, symbol); + + Option::Some(r_slice) + } else if ExecutionEnvironment::has_component(&runtime.environment, symbol) { + if accessing_information.tag_access.is_some() { + unreachable!() + } + let environment_response = ExecutionEnvironment::get_mut_component_res(&mut runtime.environment, symbol); let component_slice = treat_result_with_environment_error( environment_response, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - let memory_response = ComponentSlice::get_mut_reference_to_single_value( - component_slice, - &accessing_information.before_signal, - ); + + let is_anonymous_component = runtime.anonymous_components.contains_key(symbol); + let memory_response = if is_anonymous_component{ + ComponentSlice::get_mut_reference_to_single_value( + component_slice, + &Vec::new(), + ) + } else{ + ComponentSlice::get_mut_reference_to_single_value( + component_slice, + &accessing_information.before_signal, + ) + }; let component = treat_result_with_memory_error( memory_response, meta, @@ -702,24 +1041,13 @@ fn perform_assign( &runtime.call_trace, )?; if accessing_information.signal_access.is_none() { - debug_assert!(accessing_information.after_signal.is_empty()); - let (node_pointer, is_parallel) = safe_unwrap_to_valid_node_pointer(r_folded, line!()); - if let Option::Some(actual_node) = actual_node { - let data = SubComponentData { - name: symbol.to_string(), - goes_to: node_pointer, - is_parallel, - indexed_with: accessing_information.before_signal.clone(), - }; - actual_node.add_arrow(full_symbol.clone(), data); - } else { - unreachable!(); - } - let memory_result = ComponentRepresentation::initialize_component( + let (prenode_pointer, is_parallel) = safe_unwrap_to_valid_node_pointer(r_folded, line!()); + let memory_result = ComponentRepresentation::preinitialize_component( component, is_parallel, - node_pointer, + prenode_pointer, &runtime.exec_program, + is_anonymous_component, ); treat_result_with_memory_error_void( memory_result, @@ -727,16 +1055,86 @@ fn perform_assign( &mut runtime.runtime_errors, &runtime.call_trace, )?; + if component.is_ready_initialize() { + // calls to execute and initialize the component + let pretemplate_info = runtime.exec_program.get_prenode_value(prenode_pointer).unwrap(); + let inputs_tags = component.inputs_tags.clone(); + let result = execute_template_call_complete( + pretemplate_info.template_name(), + pretemplate_info.parameter_instances().clone(), + inputs_tags, + program_archive, + runtime, + flag_verbose, + )?; + + let (node_pointer, is_parallel) = safe_unwrap_to_valid_node_pointer(result, line!()); + + let environment_response = ExecutionEnvironment::get_mut_component_res(&mut runtime.environment, symbol); + let component_slice = treat_result_with_environment_error( + environment_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let memory_response = if is_anonymous_component { + ComponentSlice::get_mut_reference_to_single_value( + component_slice, + &Vec::new(), + ) + } else{ + ComponentSlice::get_mut_reference_to_single_value( + component_slice, + &accessing_information.before_signal, + ) + }; + let component = treat_result_with_memory_error( + memory_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + let init_result = ComponentRepresentation::initialize_component( + component, + node_pointer, + &mut runtime.exec_program, + ); + treat_result_with_memory_error( + init_result, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + if let Option::Some(actual_node) = actual_node { + let data = SubComponentData { + name: symbol.to_string(), + is_parallel, + goes_to: node_pointer, + indexed_with: accessing_information.before_signal.clone(), + }; + actual_node.add_arrow(full_symbol.clone(), data); + } else { + unreachable!(); + } + } Option::None } else { let signal_accessed = accessing_information.signal_access.clone().unwrap(); debug_assert!(FoldedValue::valid_arithmetic_slice(&r_folded)); let arithmetic_slice = r_folded.arithmetic_slice.unwrap(); - debug_assert!(arithmetic_slice.is_single()); + let tags = if r_folded.tags.is_some() { + r_folded.tags.unwrap() + } else { + TagInfo::new() + }; + let memory_response = ComponentRepresentation::assign_value_to_signal( component, &signal_accessed, &accessing_information.after_signal, + &arithmetic_slice.route(), + tags, ); treat_result_with_memory_error_void( memory_response, @@ -744,19 +1142,102 @@ fn perform_assign( &mut runtime.runtime_errors, &runtime.call_trace, )?; - Option::Some(AExpressionSlice::unwrap_to_single(arithmetic_slice)) + if !component.is_initialized && component.is_ready_initialize() { + // calls to execute and initialize the component + let pretemplate_info = runtime.exec_program.get_prenode_value( + component.node_pointer.unwrap() + ).unwrap(); + let inputs_tags = component.inputs_tags.clone(); + + let folded_result = execute_template_call_complete( + pretemplate_info.template_name(), + pretemplate_info.parameter_instances().clone(), + inputs_tags, + program_archive, + runtime, + flag_verbose, + )?; + + let (node_pointer, is_parallel) = safe_unwrap_to_valid_node_pointer(folded_result, line!()); + + let environment_response = ExecutionEnvironment::get_mut_component_res(&mut runtime.environment, symbol); + let component_slice = treat_result_with_environment_error( + environment_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let memory_response = if is_anonymous_component { + ComponentSlice::get_mut_reference_to_single_value( + component_slice, + &Vec::new(), + ) + } else{ + ComponentSlice::get_mut_reference_to_single_value( + component_slice, + &accessing_information.before_signal, + ) + }; + let component = treat_result_with_memory_error( + memory_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + let init_result = ComponentRepresentation::initialize_component( + component, + node_pointer, + &mut runtime.exec_program, + ); + treat_result_with_memory_error_void( + init_result, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + if let Option::Some(actual_node) = actual_node { + let data = SubComponentData { + name: symbol.to_string(), + goes_to: node_pointer, + is_parallel, + indexed_with: accessing_information.before_signal.clone(), + }; + let component_symbol = create_component_symbol(symbol, &accessing_information); + actual_node.add_arrow(component_symbol, data); + } else { + unreachable!(); + } + } + Option::Some(arithmetic_slice) } } else { unreachable!(); }; - if let Option::Some(arithmetic_expression) = possible_arithmetic_expression { - let ret = Constrained { left: full_symbol, right: arithmetic_expression }; + if let Option::Some(arithmetic_slice) = possible_arithmetic_slice { + let ret = Constrained { left: full_symbol, right: arithmetic_slice }; Result::Ok(Some(ret)) } else { Result::Ok(None) } } +fn signal_is_initialized(signal_previous_value: &MemorySlice, meta: &Meta, mut runtime_errors: &mut ReportCollection, + call_trace: &[String],) -> bool { + for i in 0..SignalSlice::get_number_of_cells(&signal_previous_value){ + let signal_was_assigned = treat_result_with_memory_error( + SignalSlice::access_value_by_index(&signal_previous_value, i), + meta, + &mut runtime_errors, + &call_trace, + ); + if let Ok(signal_was_assigned) = signal_was_assigned { + if signal_was_assigned { return true; } + } + } + return false; +} + // Evaluates the given condition and executes the corresponding statement. Returns a tuple (a,b) where a is the possible value returned and b is the value of the condition (in case the evaluation was successful) fn execute_conditional_statement( condition: &Expression, @@ -800,7 +1281,8 @@ fn execute_sequence_of_statements( program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, actual_node: &mut Option, - flag_verbose: bool + flag_verbose: bool, + is_complete_template: bool ) -> Result, ()> { for stmt in stmts.iter() { let f_value = execute_statement(stmt, program_archive, runtime, actual_node, flag_verbose)?; @@ -808,11 +1290,59 @@ fn execute_sequence_of_statements( return Result::Ok(f_value); } } + if is_complete_template{ + execute_delayed_declarations(program_archive, runtime, actual_node, flag_verbose)?; + } Result::Ok(Option::None) } +fn execute_delayed_declarations( + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, + actual_node: &mut Option, + flag_verbose: bool, +)-> 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)?; + arithmetic_values + .push(safe_unwrap_to_single_arithmetic_expression(f_dimensions, line!())); + } + treat_result_with_memory_error_void( + valid_array_declaration(&arithmetic_values), + &meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let usable_dimensions = + if let Option::Some(dimensions) = cast_indexing(&arithmetic_values) { + dimensions + } else { + let err = Result::Err(ExecutionError::ArraySizeTooBig); + treat_result_with_execution_error( + err, + &meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + }; + if let Option::Some(node) = actual_node { + node.add_component(&component_name, &usable_dimensions); + } + } + Result::Ok(()) +} + //************************************************* Expression execution support ************************************************* +fn create_component_symbol(symbol: &str, access_information: &AccessingInformation) -> String { + let mut appendix = "".to_string(); + let bf_signal = create_index_appendix(&access_information.before_signal); + appendix.push_str(&bf_signal); + format!("{}{}", symbol, appendix) +} + fn create_symbol(symbol: &str, access_information: &AccessingInformation) -> String { let mut appendix = "".to_string(); let bf_signal = create_index_appendix(&access_information.before_signal); @@ -852,7 +1382,7 @@ fn execute_variable( debug_assert!(access_information.after_signal.is_empty()); let indexing = access_information.before_signal; let environment_response = ExecutionEnvironment::get_variable_res(&runtime.environment, symbol); - let ae_slice = treat_result_with_environment_error( + let (var_tag, ae_slice) = treat_result_with_environment_error( environment_response, meta, &mut runtime.runtime_errors, @@ -865,7 +1395,7 @@ fn execute_variable( &mut runtime.runtime_errors, &runtime.call_trace, )?; - Result::Ok(FoldedValue { arithmetic_slice: Option::Some(ae_slice), ..FoldedValue::default() }) + Result::Ok(FoldedValue { arithmetic_slice: Option::Some(ae_slice), tags: Option::Some(var_tag.clone()), ..FoldedValue::default() }) } fn execute_signal( @@ -881,7 +1411,6 @@ fn execute_signal( let arithmetic_slice = Option::Some(AExpressionSlice::new(&AExpr::NonQuadratic)); return Result::Ok(FoldedValue { arithmetic_slice, ..FoldedValue::default() }); } - debug_assert!(access_information.signal_access.is_none()); debug_assert!(access_information.after_signal.is_empty()); let indexing = &access_information.before_signal; let environment_response = if ExecutionEnvironment::has_input(&runtime.environment, symbol) { @@ -893,31 +1422,53 @@ fn execute_signal( } else { unreachable!(); }; - let signal_slice = treat_result_with_environment_error( + let (tags, signal_slice) = treat_result_with_environment_error( environment_response, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - let memory_response = SignalSlice::access_values(signal_slice, indexing); - let signal_slice = treat_result_with_memory_error( - memory_response, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - let full_symbol = create_symbol(symbol, &access_information); - let signal_access = signal_to_arith(full_symbol, signal_slice); - let arith_slice = treat_result_with_memory_error( - signal_access, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - Result::Ok(FoldedValue { - arithmetic_slice: Option::Some(arith_slice), - ..FoldedValue::default() - }) + if let Some(acc) = access_information.signal_access { + if tags.contains_key(&acc) { + let value_tag = tags.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, + )? + } + } + else { unreachable!() } + } else { + let memory_response = SignalSlice::access_values(signal_slice, indexing); + let signal_slice = treat_result_with_memory_error( + memory_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let full_symbol = create_symbol(symbol, &access_information); + let signal_access = signal_to_arith(full_symbol, signal_slice); + let arith_slice = treat_result_with_memory_error( + signal_access, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + Result::Ok(FoldedValue { + arithmetic_slice: Option::Some(arith_slice), + tags: Option::Some(tags.clone()), + ..FoldedValue::default() + }) + } } fn signal_to_arith(symbol: String, slice: SignalSlice) -> Result { @@ -960,7 +1511,6 @@ fn execute_component( let arithmetic_slice = Option::Some(AExpressionSlice::new(&AExpr::NonQuadratic)); return Result::Ok(FoldedValue { arithmetic_slice, ..FoldedValue::default() }); } - let indexing = &access_information.before_signal; let environment_response = ExecutionEnvironment::get_component_res(&runtime.environment, symbol); let component_slice = treat_result_with_environment_error( @@ -969,7 +1519,11 @@ fn execute_component( &mut runtime.runtime_errors, &runtime.call_trace, )?; - let memory_response = ComponentSlice::access_values(component_slice, indexing); + let memory_response = if runtime.anonymous_components.contains_key(symbol) { + ComponentSlice::access_values(component_slice, &Vec::new()) + } else{ + ComponentSlice::access_values(component_slice, &access_information.before_signal) + }; let slice_result = treat_result_with_memory_error( memory_response, meta, @@ -977,7 +1531,7 @@ fn execute_component( &runtime.call_trace, )?; let resulting_component = safe_unwrap_to_single(slice_result, line!()); - let read_result = if resulting_component.is_initialized() { + let read_result = if resulting_component.is_ready_initialize() { Result::Ok(resulting_component) } else { Result::Err(MemoryError::InvalidAccess) @@ -988,9 +1542,32 @@ fn execute_component( &mut runtime.runtime_errors, &runtime.call_trace, )?; - if let Option::Some(signal_name) = &access_information.signal_access { + 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, + )? + } + } + else { unreachable!() } + } else { unreachable!()} + } + else if let Option::Some(signal_name) = &access_information.signal_access { let access_after_signal = &access_information.after_signal; - let signal = treat_result_with_memory_error( + let (tags_signal, signal) = treat_result_with_memory_error( checked_component.get_signal(signal_name), meta, &mut runtime.runtime_errors, @@ -1005,7 +1582,11 @@ fn execute_component( )?; let symbol = create_symbol(symbol, &access_information); let result = signal_to_arith(symbol, slice) - .map(|s| FoldedValue { arithmetic_slice: Option::Some(s), ..FoldedValue::default() }); + .map(|s| FoldedValue { + arithmetic_slice: Option::Some(s), + tags: Option::Some(tags_signal.clone()), + ..FoldedValue::default() + }); treat_result_with_memory_error( result, meta, @@ -1036,7 +1617,7 @@ fn prepare_environment_for_call( let mut environment = ExecutionEnvironment::new(); debug_assert_eq!(arg_names.len(), arg_values.len()); for (arg_name, arg_value) in arg_names.iter().zip(arg_values) { - ExecutionEnvironment::add_variable(&mut environment, arg_name, arg_value.clone()); + ExecutionEnvironment::add_variable(&mut environment, arg_name, (TagInfo::new(), arg_value.clone())); } environment } @@ -1051,7 +1632,7 @@ fn execute_function_call( 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)?; + execute_sequence_of_statements(function_body, program_archive, runtime, &mut Option::None, flag_verbose, true)?; runtime.block_type = previous_block; let return_value = function_result.unwrap(); debug_assert!(FoldedValue::valid_arithmetic_slice(&return_value)); @@ -1060,7 +1641,8 @@ fn execute_function_call( fn execute_template_call( id: &str, - parameter_values: &[AExpressionSlice], + parameter_values: Vec, + tag_values: BTreeMap, program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, flag_verbose: bool @@ -1074,15 +1656,30 @@ fn execute_template_call( let mut args_to_values = BTreeMap::new(); debug_assert_eq!(args_names.len(), parameter_values.len()); let mut instantiation_name = format!("{}(", id); + let mut not_empty_name = false; for (name, value) in args_names.iter().zip(parameter_values) { instantiation_name.push_str(&format!("{},", value.to_string())); + not_empty_name = true; args_to_values.insert(name.clone(), value.clone()); } - if !parameter_values.is_empty() { + for (_input, input_tags) in &tag_values{ + for (_tag, value) in input_tags { + if value.is_none(){ + instantiation_name.push_str("null,"); + } + else{ + let value = value.clone().unwrap(); + instantiation_name.push_str(&format!("{},", value.to_string())); + } + not_empty_name = true; + } + } + + if not_empty_name { instantiation_name.pop(); } instantiation_name.push(')'); - let existent_node = runtime.exec_program.identify_node(id, &args_to_values); + let existent_node = runtime.exec_program.identify_node(id, &args_to_values, &tag_values); let node_pointer = if let Option::Some(pointer) = existent_node { pointer } else { @@ -1094,6 +1691,7 @@ fn execute_template_call( id.to_string(), instantiation_name, args_to_values, + tag_values, code, is_parallel, is_custom_gate @@ -1103,7 +1701,8 @@ fn execute_template_call( program_archive, runtime, &mut node_wrap, - flag_verbose + flag_verbose, + true )?; debug_assert!(ret.is_none()); let new_node = node_wrap.unwrap(); @@ -1114,6 +1713,40 @@ fn execute_template_call( Result::Ok(FoldedValue { node_pointer: Option::Some(node_pointer), is_parallel: Option::Some(false), ..FoldedValue::default() }) } +fn preexecute_template_call( + id: &str, + parameter_values: &[AExpressionSlice], + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, +) -> Result { + debug_assert!(runtime.block_type == BlockType::Known); + let inputs = program_archive.get_template_data(id).get_inputs(); + let outputs = program_archive.get_template_data(id).get_outputs(); + + let mut inputs_to_tags = HashMap::new(); + let mut outputs_to_tags = HashMap::new(); + + + for (name, info_input) in inputs { + inputs_to_tags.insert(name.clone(), info_input.1.clone()); + } + + for (name, info_output) in outputs { + outputs_to_tags.insert(name.clone(), info_output.1.clone()); + } + + let node_wrap = Option::Some(PreExecutedTemplate::new( + id.to_string(), + parameter_values.to_vec(), + inputs_to_tags, + outputs_to_tags, + )); + + let new_node = node_wrap.unwrap(); + let node_pointer = runtime.exec_program.add_prenode_to_scheme(new_node); + Result::Ok(FoldedValue { node_pointer: Option::Some(node_pointer), is_parallel: Option::Some(false), ..FoldedValue::default() }) +} + fn execute_infix_op( meta: &Meta, infix: ExpressionInfixOpcode, @@ -1266,6 +1899,7 @@ fn cast_index(ae_index: &AExpr) -> Option { pub before_signal: Vec, pub signal_access: Option ==> may not appear, pub after_signal: Vec + pub tag_access: Option ==> may not appear, } */ struct AccessingInformation { @@ -1273,6 +1907,7 @@ struct AccessingInformation { pub before_signal: Vec, pub signal_access: Option, pub after_signal: Vec, + pub tag_access: Option } fn treat_accessing( meta: &Meta, @@ -1283,7 +1918,7 @@ fn treat_accessing( ) -> Result { let (ae_before_signal, signal_name, signal_index) = treat_indexing(0, access, program_archive, runtime, flag_verbose)?; - let (ae_after_signal, _, _) = + let (ae_after_signal, tag_name , _tag_index) = treat_indexing(signal_index + 1, access, program_archive, runtime, flag_verbose)?; treat_result_with_memory_error( valid_indexing(&ae_before_signal), @@ -1303,12 +1938,13 @@ fn treat_accessing( let undefined = possible_before_indexing.is_none() || possible_after_indexing.is_none(); let signal_access = signal_name; + let tag_access = tag_name; let (before_signal, after_signal) = if !undefined { (possible_before_indexing.unwrap(), possible_after_indexing.unwrap()) } else { (Vec::new(), Vec::new()) }; - Result::Ok(AccessingInformation { undefined, before_signal, after_signal, signal_access }) + Result::Ok(AccessingInformation { undefined, before_signal, after_signal, signal_access, tag_access}) } //************************************************* Safe transformations ************************************************* @@ -1383,13 +2019,34 @@ fn treat_result_with_memory_error_void( Report::error("Out of bounds exception".to_string(), RuntimeError) }, MemoryError::MismatchedDimensions => { - Report::error(" Typing error found: mismatched dimensions, assigning to an array an expression of greater length".to_string(), RuntimeError) + Report::error("Typing error found: mismatched dimensions".to_string(), RuntimeError) }, MemoryError::UnknownSizeDimension => { Report::error("Array dimension with unknown size".to_string(), RuntimeError) - } - _ => unreachable!(), + }, + MemoryError::AssignmentMissingTags => Report::error( + "Invalid assignment: missing tags required by input signal".to_string(), + RuntimeError, + ), + MemoryError::AssignmentTagAfterInit => Report::error( + "Invalid assignment: tags cannot be assigned to a signal already initialized".to_string(), + RuntimeError, + ), + MemoryError::AssignmentTagTwice => Report::error( + "Invalid assignment: this tag already got a value".to_string(), + RuntimeError, + ), + MemoryError::AssignmentTagInput => Report::error( + "Invalid assignment: this tag belongs to an input which already got a value".to_string(), + RuntimeError, + ), + MemoryError::MismatchedDimensionsWeak => unreachable!() + , + MemoryError::TagValueNotInitializedAccess => Report::error( + "Tag value has not been previously initialized".to_string(), + RuntimeError) + , }; add_report_to_runtime(report, meta, runtime_errors, call_trace); Result::Err(()) @@ -1415,6 +2072,22 @@ fn treat_result_with_memory_error( "Exception caused by invalid assignment".to_string(), RuntimeError, ), + MemoryError::AssignmentMissingTags => Report::error( + "Invalid assignment: missing tags required by input signal".to_string(), + RuntimeError, + ), + MemoryError::AssignmentTagAfterInit => Report::error( + "Invalid assignment: tags cannot be assigned to a signal already initialized".to_string(), + RuntimeError, + ), + MemoryError::AssignmentTagTwice => Report::error( + "Invalid assignment: this tag already got a value".to_string(), + RuntimeError, + ), + MemoryError::AssignmentTagInput => Report::error( + "Invalid assignment: this tag belongs to an input which already got a value".to_string(), + RuntimeError, + ), MemoryError::OutOfBoundsError => { Report::error("Out of bounds exception".to_string(), RuntimeError) }, @@ -1424,7 +2097,14 @@ fn treat_result_with_memory_error( MemoryError::UnknownSizeDimension => { Report::error("Array dimension with unknown size".to_string(), RuntimeError) } - _ => unreachable!(), + MemoryError::TagValueNotInitializedAccess => { + Report::error("Tag value has not been previously initialized".to_string(), RuntimeError) + + } + MemoryError::MismatchedDimensionsWeak => { + Report::error("Entra aqui".to_string(), RuntimeError) + }, + }; add_report_to_runtime(report, meta, runtime_errors, call_trace); Result::Err(()) @@ -1522,4 +2202,4 @@ fn add_report_to_runtime( } report.add_note(trace); runtime_errors.push(report); -} +} \ No newline at end of file diff --git a/constraint_generation/src/execution_data/executed_program.rs b/constraint_generation/src/execution_data/executed_program.rs index 17c2190d2..5b6dda57a 100644 --- a/constraint_generation/src/execution_data/executed_program.rs +++ b/constraint_generation/src/execution_data/executed_program.rs @@ -1,5 +1,5 @@ use super::analysis::Analysis; -use super::executed_template::ExecutedTemplate; +use super::executed_template::{ExecutedTemplate, PreExecutedTemplate}; use super::type_definitions::*; use compiler::hir::very_concrete_program::{Stats, VCPConfig, VCP}; use dag::DAG; @@ -12,6 +12,7 @@ pub type ExportResult = Result<(DAG, VCP, ReportCollection), ReportCollection>; #[derive(Default)] pub struct ExecutedProgram { pub model: Vec, + pub model_pretemplates: Vec, pub template_to_nodes: HashMap>, pub prime: String, } @@ -22,16 +23,18 @@ impl ExecutedProgram { model: Vec::new(), template_to_nodes: HashMap::new(), prime: prime.clone(), + model_pretemplates: Vec::new(), } } - pub fn identify_node(&self, name: &str, context: &ParameterContext) -> Option { + + pub fn identify_node(&self, name: &str, context: &ParameterContext, tag_context: &TagContext) -> Option { if !self.template_to_nodes.contains_key(name) { return Option::None; } let related_nodes = self.template_to_nodes.get(name).unwrap(); for index in related_nodes { let existing_node = &self.model[*index]; - if ExecutedTemplate::is_equal(existing_node, name, context) { + if ExecutedTemplate::is_equal(existing_node, name, context, tag_context) { return Option::Some(*index); } } @@ -47,6 +50,30 @@ impl ExecutedProgram { Option::Some(&self.model[node_pointer]) } + pub fn get_prenode(&self, node_pointer: NodePointer) -> Option<&PreExecutedTemplate> { + if node_pointer >= self.model_pretemplates.len() { + return Option::None; + } + Option::Some(&self.model_pretemplates[node_pointer]) + } + + pub fn get_prenode_value(&self, node_pointer: NodePointer) -> Option { + if node_pointer >= self.model_pretemplates.len() { + return Option::None; + } + Option::Some(self.model_pretemplates[node_pointer].clone()) + } + + pub fn add_prenode_to_scheme( + &mut self, + node: PreExecutedTemplate, + ) -> NodePointer { + // Insert pretemplate + let node_index = self.model_pretemplates.len(); + self.model_pretemplates.push(node); + node_index + } + pub fn add_node_to_scheme( &mut self, mut node: ExecutedTemplate, @@ -57,7 +84,11 @@ impl ExecutedProgram { apply_unused(&mut node.code, &analysis, &self.prime); apply_computed(&mut node.code, &analysis); // Insert template - let possible_index = self.identify_node(node.template_name(), node.parameter_instances()); + let possible_index = self.identify_node( + node.template_name(), + node.parameter_instances(), + node.tag_instances(), + ); if let Option::Some(index) = possible_index { return index; } diff --git a/constraint_generation/src/execution_data/executed_template.rs b/constraint_generation/src/execution_data/executed_template.rs index 4147cc510..f0651408b 100644 --- a/constraint_generation/src/execution_data/executed_template.rs +++ b/constraint_generation/src/execution_data/executed_template.rs @@ -5,6 +5,9 @@ use dag::DAG; use num_bigint::BigInt; use program_structure::ast::{SignalType, Statement}; use std::collections::{HashMap, HashSet}; +use crate::execution_data::AExpressionSlice; +use crate::execution_data::TagInfo; + struct Connexion { full_name: String, @@ -15,6 +18,48 @@ struct Connexion { dag_component_jump: usize, } +#[derive(Clone)] +pub struct PreExecutedTemplate { + pub template_name: String, + pub parameter_instances: Vec, + pub inputs: HashMap>, + pub outputs: HashMap>, +} + +impl PreExecutedTemplate { + pub fn new( + name: String, + instance: Vec, + inputs: HashMap>, + outputs: HashMap>, + ) -> PreExecutedTemplate { + PreExecutedTemplate { + template_name: name, + parameter_instances: instance, + inputs, + outputs, + } + } + + pub fn template_name(&self) -> &String { + &self.template_name + } + + pub fn parameter_instances(&self) -> &Vec{ + &self.parameter_instances + } + + pub fn inputs(&self) -> &HashMap> { + &self.inputs + } + + pub fn outputs(&self) -> &HashMap> { + &self.outputs + } +} + + + pub struct ExecutedTemplate { pub code: Statement, pub template_name: String, @@ -28,6 +73,8 @@ pub struct ExecutedTemplate { pub number_of_components: usize, pub public_inputs: HashSet, pub parameter_instances: ParameterContext, + pub tag_instances: TagContext, + pub signal_to_tags: TagContext, pub is_parallel: bool, pub has_parallel_sub_cmp: bool, pub is_custom_gate: bool, @@ -40,6 +87,7 @@ impl ExecutedTemplate { name: String, report_name: String, instance: ParameterContext, + tag_instances: TagContext, code: Statement, is_parallel: bool, is_custom_gate: bool @@ -54,6 +102,8 @@ impl ExecutedTemplate { code: code.clone(), template_name: name, parameter_instances: instance, + signal_to_tags: tag_instances.clone(), + tag_instances, inputs: SignalCollector::new(), outputs: SignalCollector::new(), intermediates: SignalCollector::new(), @@ -65,14 +115,16 @@ impl ExecutedTemplate { } } - pub fn is_equal(&self, name: &str, context: &ParameterContext) -> bool { - self.template_name == name && self.parameter_instances == *context + pub fn is_equal(&self, name: &str, context: &ParameterContext, tag_context: &TagContext) -> bool { + self.template_name == name + && self.parameter_instances == *context + && self.tag_instances == *tag_context } pub fn add_arrow(&mut self, component_name: String, data: SubComponentData) { let cnn = Connexion { full_name: component_name, inspect: data, dag_offset: 0, dag_component_offset: 0, dag_jump: 0, dag_component_jump: 0}; - self.connexions.push(cnn); + self.connexions.push(cnn); } pub fn add_input(&mut self, input_name: &str, dimensions: &[usize]) { @@ -108,6 +160,17 @@ impl ExecutedTemplate { } } + pub fn add_tag_signal(&mut self, signal_name: &str, tag_name: &str, value: Option){ + let tags_signal = self.signal_to_tags.get_mut(signal_name); + if tags_signal.is_none(){ + let mut new_tags_signal = TagInfo::new(); + new_tags_signal.insert(tag_name.to_string(), value); + self.signal_to_tags.insert(signal_name.to_string(), new_tags_signal); + } else { + tags_signal.unwrap().insert(tag_name.to_string(), value); + } + } + pub fn add_component(&mut self, component_name: &str, dimensions: &[usize]) { self.components.push((component_name.to_string(), dimensions.to_vec())); self.number_of_components += dimensions.iter().fold(1, |p, c| p * (*c)); @@ -125,6 +188,10 @@ impl ExecutedTemplate { &self.parameter_instances } + pub fn tag_instances(&self) -> &TagContext { + &self.tag_instances + } + pub fn inputs(&self) -> &SignalCollector { &self.inputs } @@ -280,6 +347,7 @@ impl ExecutedTemplate { code: self.code, name: self.template_name, number_of_components : self.number_of_components, + signals_to_tags: self.signal_to_tags, }; let mut instance = TemplateInstance::new(config); @@ -296,29 +364,30 @@ impl ExecutedTemplate { let mut local_id = 0; let mut dag_local_id = 1; for (name, lengths) in self.outputs { - let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Output }; + let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Output}; local_id += signal.size(); dag_local_id += signal.size(); instance.add_signal(signal); } for (name, lengths) in public { - let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Input }; + let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Input}; local_id += signal.size(); dag_local_id += signal.size(); instance.add_signal(signal); } for (name, lengths) in not_public { - let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Input }; + let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Input}; local_id += signal.size(); dag_local_id += signal.size(); instance.add_signal(signal); } for (name, lengths) in self.intermediates { - let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Intermediate }; + let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Intermediate}; local_id += signal.size(); dag_local_id += signal.size(); instance.add_signal(signal); } + instance } } @@ -453,6 +522,7 @@ fn build_clusters(tmp: &ExecutedTemplate, instances: &[TemplateInstance]) -> Vec end += 1; } } + let cluster = TriggerCluster { slice: start..end, length: end - start, diff --git a/constraint_generation/src/execution_data/filters.rs b/constraint_generation/src/execution_data/filters.rs index 53aef6268..06c14d230 100644 --- a/constraint_generation/src/execution_data/filters.rs +++ b/constraint_generation/src/execution_data/filters.rs @@ -10,6 +10,7 @@ fn clean_dead_code(stmt: &mut Statement, analysis: &Analysis, prime: &String) -> use Statement::*; match stmt { While { stmt, .. } => clean_dead_code(stmt, analysis, prime), + MultSubstitution { .. } => unreachable!(), IfThenElse { if_case, else_case, cond, meta } => { let field = program_structure::constants::UsefulConstants::new(prime).get_p().clone(); let empty_block = Box::new(Block { meta: meta.clone(), stmts: vec![] }); @@ -49,6 +50,7 @@ fn clean_dead_code(stmt: &mut Statement, analysis: &Analysis, prime: &String) -> pub fn apply_computed(stmt: &mut Statement, analysis: &Analysis) { use Statement::*; match stmt { + MultSubstitution { .. } => unreachable!(), IfThenElse { cond, if_case, else_case, .. } => { *cond = computed_or_original(analysis, cond); apply_computed_expr(cond, analysis); @@ -183,5 +185,6 @@ fn apply_computed_expr(expr: &mut Expression, analysis: &Analysis) { *rhe = Box::new(computed_or_original(analysis, rhe)); apply_computed_expr(rhe, analysis); } + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } diff --git a/constraint_generation/src/execution_data/mod.rs b/constraint_generation/src/execution_data/mod.rs index df6a057dc..6dcc6c012 100644 --- a/constraint_generation/src/execution_data/mod.rs +++ b/constraint_generation/src/execution_data/mod.rs @@ -1,7 +1,7 @@ -use super::environment_utils::slice_types::AExpressionSlice; +use super::environment_utils::slice_types::{AExpressionSlice, TagInfo}; use circom_algebra::algebra::Constraint; pub use executed_program::ExecutedProgram; -pub use executed_template::ExecutedTemplate; +pub use executed_template::{PreExecutedTemplate, ExecutedTemplate}; pub use type_definitions::NodePointer; pub mod analysis; diff --git a/constraint_generation/src/execution_data/type_definitions.rs b/constraint_generation/src/execution_data/type_definitions.rs index 46defc7b9..03ef731c3 100644 --- a/constraint_generation/src/execution_data/type_definitions.rs +++ b/constraint_generation/src/execution_data/type_definitions.rs @@ -1,10 +1,15 @@ use super::AExpressionSlice; use super::Constraint as ConstraintGen; use std::collections::BTreeMap; +use num_bigint_dig::BigInt; + pub type NodePointer = usize; pub type Constraint = ConstraintGen; pub type ParameterContext = BTreeMap; +pub type TagContext = BTreeMap; +pub type TagInfo = BTreeMap>; +// From name to dimensions pub type SignalCollector = Vec<(String, Vec)>; pub type ComponentCollector = Vec<(String, Vec)>; pub struct SubComponentData { diff --git a/mkdocs/docs/circom-language/anonymous-components-and-tuples.md b/mkdocs/docs/circom-language/anonymous-components-and-tuples.md new file mode 100644 index 000000000..517b1cf39 --- /dev/null +++ b/mkdocs/docs/circom-language/anonymous-components-and-tuples.md @@ -0,0 +1,196 @@ +# Tuples and Anonymous Components + +## Anonymous Components + +circom 2.1.0 introduces a new feature called __anonymous component__. An anonymous component allows in a single instruction 1) the implicit declaration of a new component, 2) the assignment of every input signal and, finally, 3) the assignment of every output signal. +This section is divided in the next subsections: +1. Syntax and semantics of __anonymous components__. +2. What if the anonymous component is an instance of a template with more than an output signal? + __We introduce the tuples in circom.__ +3. What if the anonymous component is an instance of a template which input/output signals are arrays? + __We introduce the element-wise assignment for signal arrays.__ +4. What if we are not interested in collecting one of the outputs? +__We introduce the use of "_" to indicate that a signal is not relevant.__ + + +### Syntax and semantics of anonymous components +Let us see a typical circom program. +```text +template A(n){ + signal input a, b; + signal output c; + c <== a*b; +} +template B(n){ + signal input in[n]; + signal out; + component temp_a = A(n); + temp_a.a <== in[0]; + temp_a.b <== in[1]; + out <== temp_a.c; +} +component main = B(2); +``` +Thanks to the use of anonymous components, we can easily write the next program, which is, equivalent to the previous one, but its code is much cleaner. + +```text +template A(n){ + signal input a, b; + signal output c; + c <== a*b; +} +template B(n){ + signal input in[n]; + signal out <== A(n)(in[0],in[1]); +} +component main = B(2); +``` + +It is important to highlight that both circuits are equivalent: they have the same witnesses and the same constraint, that is, `out === in[0]*in[1]`. + +The anonymous components are a new kind of circom expression whose syntax is as follows: ```temp_name(arg1,...,argN)(input1,...,inputM)``` +assuming that we have a template temp_name with N arguments and M input signals. + +Let us clarify two points: +1. `arg1`, ..., `argN` are template arguments. We can use them as usual. They can be arithmetic operations expressions or constants. The important thing is its value must be known at compilation time. +2. `input1`, ..., `inputM` are input signals. We can pass another signals, (just like in the example) constants or other anonymous components (in a compositional way), if and only if, __such components only have 1 output signal__. + +The value returned by the anonymous components depends on the number of template's output signals. + +1. __If the template does not define any output signal__ (for instance, if it only defines constraints based on the input signals), we can use the anonymous component like if it was an statement + `temp_name(arg1,...,argN)(inp1,...,inpM);` + +2. __If the template defines a single output signal__, we can use any of the well-known operators to collect the output signal. It is important to highlight that we can use with the anonymous components any of the operators `<--`, `<==`, `=`, `-->`and `==>` with the usual semantics. For instance, +`signal out <== temp_name(a1,...,aN)(i1,...,iM);` + +1. __If the template defines more than an output signal__, we need to use a new kind of expression to collect all the outputs: __the tuples__, whose syntax is the usual in other programming languages. + +``` +signal output o1, ..., oK; +(o1,...,oK) <== temp_name(a1,...,aN)(i1,...,iM); +``` + +``` +var v1, ..., vK; +(v1,...,vK) = temp_name(a1,...,aN)(i1,...,iM); +``` + +##The use of tuples +Tuples are a typical expression in other programming languages, which allows us to do multiple assignments like in the previous example. + +We have introduced tuples because of the previous feature. When using templates with several outputs is necessary being able to collect all the outputs at the same time, since the component is anonymous and later cannot be accessed. + +Apart from the main use of the tuples, we can use this new kind of expressions with every kind of assignment `<==`,`=` and `<--`. However, we discourage the use of the latter. Tuples can only be used in combination of any of these operators whenever there are tuples in both sides of the statement. In this case, the semantics of this multiple assignment is the element-wise assignment. + +Let us see a non-trivial example to illustrate the importance of the order of the tuple elements. + +``` +var a = 0, b = 0; component c; +(a, b, c) = (1,a+1, A(2)); +``` + +This is internally translated by the compiler to +``` +a = 1; +b = a + 1; +c = A(2); +``` +Then, the final value of a and b is 1 and 2, respectively. Notice that c is an instance of template A and we could use now statements to access its inputs and outputs. + +### The use of <== for signal arrays +One more extension must be added to circom in order to enable the use of anonymous components. + +How could we use a template as anonymous component if it makes use of input/output signal arrays? So far, this could not be handled by circom. + +In circom 2.1.0, we have overloaded the operator `<==` for signal arrays with the same dimension to express the element-wise assignment. For instance, let us consider the next code. + +``` +template Ex(n,m){ + signal input in[n]; + signal output out[m]; + out <== in; +} +``` + +If `n != m`, then the compiler reports an error, since both arrays have not the same size. Otherwise, the code is equivalent to: + +``` +template Ex(n,m){ + signal input in[n]; + signal output out[m]; + while(i < 4){ + out[i] <== in[i]; + i += 1; + } +} +``` + +Let us use this template to illustrate how this new feature is combined with the use of an anonymous component. + +``` +template A{ + signal input i[4]; + signal output o[4]; + o <== Ex(4,4)(i); +} +``` +Here, we can see that we pass as first signal parameter a 4-size array. Notice that previously we can only write a program similar to: + +``` +template A{ + signal input i[4]; + signal output o[4]; + component anon = Ex(4,4); + var i = 0; + while(i < 4){ + anon.in[i] <== i[i]; + i += 1; + } + i = 0; + while(i < 4){ + o[i] <== anon.out[i]; + i += 1; + } +} +``` + +### The use of _ + +The underscore __"_"__ allows to ignore any amount of output signals of the anonymous components. + +```text +template A(n){ + signal input a, b, c; + signal output d; + d <== a*b+c; + a * b === c*c; +} +template B(n){ + signal input in[n]; + _ <== A(n)(in[0],in[1],in[2]); +} +component main = B(3); +``` + +In the previous example, we are interesting in adding to the R1CS the constraint `a * b = c * c`, but we can ignore the output signal `d`. + +In case the anonymous component has one more than one output, we can ignore the ones we are not interested. + +```text +template A(n){ + signal input a; + signal output b, c, d; + b <== a * a; + c <== a + 2; + d <== a * a + 2; +} +template B(n){ + signal input in; + signal output out1; + (_,out1,_) <== A(n)(in); +} +component main = B(3); +``` + +In this example, we are only interested in `out1 = in + 2`. + diff --git a/mkdocs/docs/circom-language/tags.md b/mkdocs/docs/circom-language/tags.md new file mode 100644 index 000000000..8d49211d3 --- /dev/null +++ b/mkdocs/docs/circom-language/tags.md @@ -0,0 +1,88 @@ +#Signal Tags +circom 2.1.0 introduces a new feature called __signal tags__. Tags can be defined during the declaration of any signal in a template. The tag list is indicated between brackets right before the signal name. + +``` +signal (input/output) {tag_1,...,tag_n} signalname; +``` + +Let us see a first well-known example in the circomlib, where the tag is declared in an input signal: + +``` +template Bits2Num(n) { + signal input {binary} in[n]; + signal output out; + var lc1=0; + + var e2 = 1; + for (var i = 0; i out; +} + +template A(){ + ... + component b = Bits2Num(10); + b.in <== a; + ... +} +``` +The input array `in` is declared with the tag `binary`. This tag means that each signal in the array is always expected to be `0`or `1`, in order to compute the corresponding number correctly. + +Then, whenever the previous template is instantiated, the compiler checks if the array `a` assigned to the input array has the tag binary, since `in` has the tag `binary` in its declaration. If it does not, an error is reported. Notice that the compiler also checks if both arrays have the same size. + +It is important to highlight that the compiler does never make any check about the validity of the tags. It is the programmer's responsability to include the constraints and executable code to guarantee that the inteded meaning of each signal is always true. + +Let us consider another well-known template that the programmer can use to guarantee that the output signal is always binary. + +``` +template IsZero() { + signal input in; + signal output {binary} out; + signal inv; + inv <-- in!=0 ? 1/in : 0; + out <== -in*inv +1; + in*out === 0; +} +``` + +To the light of this example, when using tags in intermediate or output signals, the programmer must use components like the previous one or explicitly include the constraints to guarantee the validity of the tags. + +### Tags with value +Notice that in the previous template `Bits2Num`, we can add more information about the output signal `out`: the maximum number of bits needed to represent it is `n`. To express this fact is necessary that tags can have a value. + +The value of the tag can be accessed using the notation `.` at any moment as a part of an arithmetic expression. However, if the tag has not been previously initialized, then the compiler reports an error. + +The value of the tag can be also modified using the notation `.`, as long as the corresponding signal has not received any value. Valued tags behave like parameters which means that they can only be assigned to values known at compilation time. + +Let us modify the previous example to include this tag in the template. + +``` +template Bits2Num(n) { + signal input {binary} in[n]; + signal output {maxbit} out; + var lc1=0; + + var e2 = 1; + for (var i = 0; i out; +} +``` + +On the other hand, the next code is erroneous since the tag value is modified after the output receives its value. +``` +template Bits2Num(n) { + ... + lc1 ==> out; + out.maxbit = n; +} +``` + +###Tags in signal arrays +Every signal in an array has exactly the same tag value. Then, the tag is accessed directly from the array name instead of accessing from a particular signal in the array. Similarly to the previous erroneous example: if a particular position of the array is modified, then the tag value of the whole array cannot be modified at all. \ No newline at end of file diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 9f2dd3fc7..e428d23ea 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "2.0.8" +version = "2.1.0" authors = ["Costa Group UCM","iden3"] edition = "2018" build = "build.rs" diff --git a/parser/src/errors.rs b/parser/src/errors.rs index e589c28f8..5af6dbd9b 100644 --- a/parser/src/errors.rs +++ b/parser/src/errors.rs @@ -1,3 +1,4 @@ +use program_structure::ast::Meta; use program_structure::error_code::ReportCode; use program_structure::error_definition::Report; use program_structure::file_definition::{FileID, FileLocation}; @@ -73,6 +74,8 @@ impl CompilerVersionError { } } + + pub struct NoCompilerVersionWarning{ pub path: String, pub version: Version, @@ -84,4 +87,68 @@ impl NoCompilerVersionWarning { ReportCode::NoCompilerVersionWarning, ) } +} + +pub struct AnonymousCompError{ + pub location: FileLocation, + pub msg : String +} + +impl AnonymousCompError { + pub fn produce_report( error : Self) -> Report { + Report::error( + format!("{}", error.msg), + ReportCode::AnonymousCompError, + ) + } + + pub fn anonymous_inside_condition_error(meta : Meta) -> Report { + let error = AnonymousCompError {msg: "An anonymous component cannot be used inside a condition ".to_string(), location : meta.location.clone()}; + let mut report = AnonymousCompError::produce_report(error); + let file_id = meta.get_file_id().clone(); + report.add_primary( + meta.location, + file_id, + "This is an anonymous component used inside a condition".to_string(), + ); + report + } + + pub fn anonymous_general_error(meta : Meta, msg : String) -> Report { + let error = AnonymousCompError {msg, location : meta.location.clone()}; + let mut report = AnonymousCompError::produce_report(error); + let file_id = meta.get_file_id().clone(); + report.add_primary( + meta.location, + file_id, + "This is the anonymous component whose use is not allowed".to_string(), + ); + report + } +} + +pub struct TupleError{ + pub location: FileLocation, + pub msg : String +} + +impl TupleError { + pub fn produce_report( error : Self) -> Report { + Report::error( + format!("{}", error.msg), + ReportCode::TupleError, + ) + } + + pub fn tuple_general_error(meta : Meta, msg : String) -> Report { + let error = TupleError {msg, location : meta.location.clone()}; + let mut report = TupleError::produce_report(error); + let file_id = meta.get_file_id().clone(); + report.add_primary( + meta.location, + file_id, + "This is the tuple whose use is not allowed".to_string(), + ); + report + } } \ No newline at end of file diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 425cdf64e..3db1c989f 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -2,9 +2,10 @@ use num_bigint::BigInt; use program_structure::statement_builders::*; use program_structure::expression_builders::*; use program_structure::ast::*; -use program_structure::ast_shortcuts::{self,Symbol}; +use program_structure::ast_shortcuts::{self,Symbol,TupleInit}; use std::str::FromStr; + grammar; // ==================================================================== @@ -93,9 +94,9 @@ pub ParseDefinition : Definition = { // VariableDefinitions // ==================================================================== -ParseElementType : SignalElementType = { - "FieldElement" => SignalElementType::FieldElement, - "Binary" => SignalElementType::Binary, +// To generate the list of tags associated to a signal +ParseTagsList : Vec = { + "{" "}" => id, }; ParseSignalType: SignalType = { @@ -104,17 +105,17 @@ ParseSignalType: SignalType = { }; SignalHeader : VariableType = { - "signal" )?> + "signal" => { - let e = match element_type { - None => SignalElementType::FieldElement, - Some(t) => t, - }; let s = match signal_type { None => SignalType::Intermediate, Some(st) => st, }; - VariableType::Signal(s,e) + let t = match tags_list { + None => Vec::new(), + Some(tl) => tl, + }; + VariableType::Signal(s, t) } }; @@ -162,6 +163,18 @@ SignalSimpleSymbol : Symbol = { }; +TupleInitialization : TupleInit = { + "<==" => TupleInit { + tuple_init : (AssignOp::AssignConstraintSignal,rhe) + }, + "<--" => TupleInit { + tuple_init : (AssignOp::AssignSignal,rhe) + }, + "=" => TupleInit { + tuple_init : (AssignOp::AssignVar,rhe) + }, +} + SomeSymbol : Symbol = { ComplexSymbol, SimpleSymbol, @@ -175,6 +188,29 @@ SignalSymbol : Symbol = { // A declaration is the definition of a type followed by the initialization ParseDeclaration : Statement = { + "var" "(" ",")*> ")" => { + let mut symbols = symbols; + let meta = Meta::new(s,e); + let xtype = VariableType::Var; + symbols.push(symbol); + ast_shortcuts::split_declaration_into_single_nodes_and_multisubstitution(meta,xtype,symbols,init) + }, + + "(" ",")*> ")" => { + let mut symbols = symbols; + let meta = Meta::new(s,e); + symbols.push(symbol); + ast_shortcuts::split_declaration_into_single_nodes_and_multisubstitution(meta,xtype,symbols, init) + }, + "component" "(" ",")*> ")" => { + let mut symbols = symbols; + let meta = Meta::new(s,e); + let xtype = VariableType::Component; + symbols.push(symbol); + ast_shortcuts::split_declaration_into_single_nodes_and_multisubstitution(meta,xtype,symbols, init) + }, + + "var" ",")*> => { let mut symbols = symbols; let meta = Meta::new(s,e); @@ -182,6 +218,7 @@ ParseDeclaration : Statement = { symbols.push(symbol); ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols,AssignOp::AssignVar) }, + "component" ",")*> => { let mut symbols = symbols; let meta = Meta::new(s,e); @@ -189,6 +226,7 @@ ParseDeclaration : Statement = { symbols.push(symbol); ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols,AssignOp::AssignVar) }, + ",")*> => { let mut symbols = symbols; @@ -205,17 +243,26 @@ ParseDeclaration : Statement = { }, }; ParseSubstitution : Statement = { - - => {let (name,access) = variable; - build_substitution(Meta::new(s,e),name,access,op,rhe) + + => {if let Expression::Variable {meta, name, access} = variable { + build_substitution(Meta::new(s,e),name,access,ops,rhe) + } else{ + build_mult_substitution(Meta::new(s,e),variable,ops,rhe) + } }, - "-->" - => {let (name,access) = variable; - build_substitution(Meta::new(s,e),name,access,AssignOp::AssignSignal,lhe) + "-->" + => {if let Expression::Variable {meta, name, access} = variable { + build_substitution(Meta::new(s,e),name,access,AssignOp::AssignSignal,lhe) + } else { + build_mult_substitution(Meta::new(s,e),variable,AssignOp::AssignSignal,lhe) + } }, - "==>" - => {let (name,access) = variable; - build_substitution(Meta::new(s,e),name,access,AssignOp::AssignConstraintSignal,lhe) + "==>" + => {if let Expression::Variable {meta, name, access} = variable { + build_substitution(Meta::new(s,e),name,access,AssignOp::AssignConstraintSignal,lhe) + } else{ + build_mult_substitution(Meta::new(s,e),variable,AssignOp::AssignConstraintSignal,lhe) + } }, "\\=" => ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::IntDiv,Meta::new(s,e),variable,rhe), @@ -318,6 +365,9 @@ ParseStatement2 : Statement = { "assert" "(" ")" ";" => build_assert(Meta::new(s,e),arg), + ";" + => build_anonymous_component_statement(Meta::new(s,e), lhe), + ParseBlock }; @@ -398,6 +448,16 @@ LogListable: Vec = { }, }; +TwoElemsListable: Vec = { + "," )*> + => { + let mut rest = rest; + let mut new_v = vec![head, head1]; + new_v.append(&mut rest); + new_v + }, +}; + InfixOpTier : Expression = { > => build_infix(Meta::new(s,e),lhe,infix_op,rhe), @@ -470,8 +530,20 @@ Expression3 = InfixOpTier; // ops: Unary - ! ~ Expression2 = PrefixOpTier; -// function call, array inline +// function call, array inline, anonymous component call Expression1: Expression = { + "(" ")" "(" ")" + => {let params = match args { + None => Vec::new(), + Some(a) => a + }; + let signals = match args2 { + None => Vec::new(), + Some(a) => a + }; + build_anonymous_component(Meta::new(s,e),id,params,signals, false)} + , + "(" ")" => match args { None => build_call(Meta::new(s,e),id,Vec::new()), @@ -479,7 +551,12 @@ Expression1: Expression = { }, "[" "]" - => build_array_in_line(Meta::new(s,e),values), + => build_array_in_line(Meta::new(s,e), values), + + "(" ")" + => { + build_tuple(Meta::new(s,e), values) + }, Expression0, }; @@ -492,6 +569,9 @@ Expression0: Expression = { build_variable(Meta::new(s,e),name,access) }, + "_" + => build_variable(Meta::new(s,e),"_".to_string(),Vec::new()), + => build_number(Meta::new(s,e),value), diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 920c82267..b77e7bb20 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -10,12 +10,16 @@ lalrpop_mod!(pub lang); mod errors; mod include_logic; mod parser_logic; +mod syntax_sugar_remover; + use include_logic::{FileStack, IncludesGraph}; use program_structure::error_code::ReportCode; use program_structure::error_definition::{Report, ReportCollection}; use program_structure::file_definition::{FileLibrary}; use program_structure::program_archive::ProgramArchive; use std::path::{PathBuf, Path}; +use syntax_sugar_remover::{apply_syntactic_sugar}; + use std::str::FromStr; pub type Version = (usize, usize, usize); @@ -123,8 +127,13 @@ pub fn run_parser( Err((lib, rep)) => { Err((lib, rep)) } - Ok(program_archive) => { - Ok((program_archive, warnings)) + Ok(mut program_archive) => { + let lib = program_archive.get_file_library().clone(); + let program_archive_result = apply_syntactic_sugar( &mut program_archive); + match program_archive_result { + Result::Err(v) => {Result::Err((lib,vec![v]))} + Result::Ok(()) => {Ok((program_archive, warnings))} + } } } } @@ -216,4 +225,4 @@ fn check_custom_gates_version( } } Ok(()) -} +} \ No newline at end of file diff --git a/parser/src/syntax_sugar_remover.rs b/parser/src/syntax_sugar_remover.rs new file mode 100644 index 000000000..8876dacf0 --- /dev/null +++ b/parser/src/syntax_sugar_remover.rs @@ -0,0 +1,659 @@ +use program_structure::ast::*; +use program_structure::statement_builders::{build_block, build_substitution}; +use program_structure::error_definition::{Report}; +use program_structure::expression_builders::{build_call, build_tuple, build_parallel_op}; +use program_structure::file_definition::FileLibrary; +use program_structure::program_archive::ProgramArchive; +use program_structure::statement_builders::{build_declaration, build_log_call, build_assert, build_return, build_constraint_equality, build_initialization_block}; +use program_structure::template_data::{TemplateData}; + +use std::collections::HashMap; +use num_bigint::BigInt; + + + +use crate::errors::{AnonymousCompError,TupleError}; + + + +fn remove_anonymous_from_statement( + templates : &mut HashMap, + file_lib : &FileLibrary, + stm : Statement, + var_access: &Option +) -> Result<(Statement,Vec),Report>{ + match stm.clone() { + Statement::MultSubstitution { meta, lhe, op, rhe } => { + if lhe.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(lhe.get_meta().clone(),"An anonymous component cannot be used in the left side of an assignment".to_string())); + } else{ + let (mut stmts, declarations, new_rhe) = remove_anonymous_from_expression(templates, file_lib, rhe, var_access)?; + let subs = Statement::MultSubstitution { meta: meta.clone(), lhe: lhe, op: op, rhe: new_rhe }; + let mut substs = Vec::new(); + if stmts.is_empty(){ + Result::Ok((subs, declarations)) + }else{ + substs.append(&mut stmts); + substs.push(subs); + Result::Ok((Statement::Block { meta : meta, stmts : substs}, declarations)) + } + } + }, + Statement::IfThenElse { meta, cond, if_case, else_case } + => { + if cond.contains_anonymous_comp() { + Result::Err(AnonymousCompError::anonymous_inside_condition_error(cond.get_meta().clone())) + } else{ + let (if_ok,mut declarations) = remove_anonymous_from_statement(templates, file_lib, *if_case, var_access)?; + let b_if = Box::new(if_ok); + if else_case.is_none(){ + Result::Ok((Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::None},declarations)) + }else { + let else_c = *(else_case.unwrap()); + let (else_ok, mut declarations2) = remove_anonymous_from_statement(templates, file_lib, else_c, var_access)?; + let b_else = Box::new(else_ok); + declarations.append(&mut declarations2); + Result::Ok((Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::Some(b_else)},declarations)) + } + } + } + Statement::While { meta, cond, stmt } => { + if cond.contains_anonymous_comp() { + Result::Err(AnonymousCompError::anonymous_inside_condition_error(cond.get_meta().clone())) + } else{ + + let id_var_while = "anon_var_".to_string() + &file_lib.get_line(meta.start, meta.get_file_id()).unwrap().to_string() + "_" + &meta.start.to_string(); + let var_access = Expression::Variable{meta: meta.clone(), name: id_var_while.clone(), access: Vec::new()}; + let mut declarations = vec![]; + let (while_ok, mut declarations2) = remove_anonymous_from_statement(templates, file_lib, *stmt, &Some(var_access.clone()))?; + let b_while = if !declarations2.is_empty(){ + declarations.push( + build_declaration( + meta.clone(), + VariableType::Var, + id_var_while.clone(), + Vec::new(), + ) + ); + declarations.push( + build_substitution( + meta.clone(), + id_var_while.clone(), + vec![], + AssignOp::AssignVar, + Expression::Number(meta.clone(), BigInt::from(0)) + ) + ); + declarations.append(&mut declarations2); + let next_access = Expression::InfixOp{ + meta: meta.clone(), + infix_op: ExpressionInfixOpcode::Add, + lhe: Box::new(var_access), + rhe: Box::new(Expression::Number(meta.clone(), BigInt::from(1))), + }; + let subs_access = Statement::Substitution{ + meta: meta.clone(), + var: id_var_while, + access: Vec::new(), + op: AssignOp::AssignVar, + rhe: next_access, + }; + + let new_block = Statement::Block{ + meta: meta.clone(), + stmts: vec![while_ok, subs_access], + }; + Box::new(new_block) + } else{ + Box::new(while_ok) + }; + + Result::Ok((Statement::While { meta: meta, cond: cond, stmt: b_while}, declarations)) + } + } + Statement::LogCall {meta, args } => { + for arg in &args { + if let program_structure::ast::LogArgument::LogExp( exp ) = arg { + if exp.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta,"An anonymous component cannot be used inside a log".to_string())) + } + } + } + Result::Ok((build_log_call(meta, args),Vec::new())) + } + Statement::Assert { meta, arg} => { Result::Ok((build_assert(meta, arg),Vec::new()))} + Statement::Return { meta, value: arg}=> { + if arg.contains_anonymous_comp(){ + Result::Err(AnonymousCompError::anonymous_general_error(meta,"An anonymous component cannot be used inside a function ".to_string())) + } + else{ Result::Ok((build_return(meta, arg),Vec::new()))} + } + Statement::ConstraintEquality {meta, lhe, rhe } => { + if lhe.contains_anonymous_comp() || rhe.contains_anonymous_comp() { + Result::Err(AnonymousCompError::anonymous_general_error(meta,"An anonymous component cannot be used with operator === ".to_string())) + } + else{ Result::Ok((build_constraint_equality(meta, lhe, rhe),Vec::new())) } + } + Statement::Declaration { meta , xtype , name , + dimensions, .. } => { + for exp in dimensions.clone(){ + if exp.contains_anonymous_comp(){ + return Result::Err(AnonymousCompError::anonymous_general_error(exp.get_meta().clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + } + } + Result::Ok((build_declaration(meta, xtype, name, dimensions),Vec::new())) + } + Statement::InitializationBlock { meta, xtype, initializations } => + { + let mut new_inits = Vec::new(); + let mut declarations = Vec::new(); + for stmt in initializations { + let (stmt_ok, mut declaration) = remove_anonymous_from_statement(templates, file_lib, stmt, var_access)?; + new_inits.push(stmt_ok); + declarations.append(&mut declaration) + } + Result::Ok((Statement::InitializationBlock { meta: meta, xtype: xtype, initializations: new_inits }, declarations)) + } + Statement::Block { meta, stmts } => { + let mut new_stmts = Vec::new(); + let mut declarations = Vec::new(); + for stmt in stmts { + let (stmt_ok, mut declaration) = remove_anonymous_from_statement(templates, file_lib, stmt, var_access)?; + new_stmts.push(stmt_ok); + declarations.append(&mut declaration); + } + Result::Ok((Statement::Block { meta : meta, stmts: new_stmts},declarations)) + } + Statement::Substitution { meta, var, op, rhe, access} => { + let (mut stmts, declarations, new_rhe) = remove_anonymous_from_expression(templates, file_lib, rhe, var_access)?; + let subs = Statement::Substitution { meta: meta.clone(), var: var, access: access, op: op, rhe: new_rhe }; + let mut substs = Vec::new(); + if stmts.is_empty(){ + Result::Ok((subs, declarations)) + }else{ + substs.append(&mut stmts); + substs.push(subs); + Result::Ok((Statement::Block { meta : meta, stmts : substs}, declarations)) + } + } + } +} + +// returns a block with the substitutions, the declarations and finally the output expression +pub fn remove_anonymous_from_expression( + templates : &mut HashMap, + file_lib : & FileLibrary, + exp : Expression, + var_access: &Option, // in case the call is inside a loop, variable used to control the access +) -> Result<(Vec, Vec, Expression),Report>{ + use Expression::*; + match exp.clone() { + ArrayInLine { values, .. } => { + for value in values{ + if value.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(value.get_meta().clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + } + } + Result::Ok((Vec::new(),Vec::new(),exp)) + }, + UniformArray { meta, value, dimension } => { + if value.contains_anonymous_comp() || dimension.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to define a dimension of an array".to_string())); + } + Result::Ok((Vec::new(),Vec::new(),exp)) + }, + Number(_, _) => { Result::Ok((Vec::new(),Vec::new(),exp)) }, + Variable { meta, .. } => { + if exp.contains_anonymous_comp(){ + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used to access an array".to_string())); + } + Result::Ok((Vec::new(),Vec::new(),exp)) + }, + InfixOp { meta, lhe, rhe, .. } => { + if lhe.contains_anonymous_comp() || rhe.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used in the middle of an operation ".to_string())); + } + Result::Ok((Vec::new(),Vec::new(),exp)) + }, + PrefixOp { meta, rhe, .. } => { + if rhe.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used in the middle of an operation ".to_string())); + } + Result::Ok((Vec::new(),Vec::new(),exp)) + }, + InlineSwitchOp { meta, cond, if_true, if_false } => { + if cond.contains_anonymous_comp() || if_true.contains_anonymous_comp() || if_false.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used inside an inline switch ".to_string())); + } + Result::Ok((Vec::new(),Vec::new(),exp)) + /* This code is useful if we want to allow anonymous components inside InLineSwitch. + let result_if = remove_anonymous_from_expression( templates, file_lib, *if_true); + let result_else = remove_anonymous_from_expression( templates, file_lib, *if_false); + if result_if.is_err() { Result::Err(result_if.err().unwrap())} + else if result_else.is_err() { Result::Err(result_else.err().unwrap())} + else { + let (result_if2,exp_if) = result_if.ok().unwrap(); + let (result_else2, exp_else) = result_else.ok().unwrap(); + let block_if = if result_if2.is_none() { build_block(meta.clone(), Vec::new())} else { result_if2.unwrap()}; + + Result::Ok((Option::Some(build_conditional_block(meta.clone(), *cond.clone(), block_if, result_else2)), + build_inline_switch_op(meta.clone(), *cond.clone(), exp_if, exp_else)))*/ + + }, + Call { meta, args, .. } => { + for value in args{ + if value.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); + } + } + Result::Ok((Vec::new(),Vec::new(),exp)) + }, + AnonymousComp { meta, id, params, signals, is_parallel } => { + let template = templates.get(&id); + let mut declarations = Vec::new(); + if template.is_none(){ + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"The template does not exist ".to_string())); + } + let inputs = template.unwrap().get_declaration_inputs(); + if inputs.len() != signals.len(){ + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"The number of template input signals must coincide with the number of input parameters ".to_string())); + } + let mut i = 0; + let mut seq_substs = Vec::new(); + let id_anon_temp = id.to_string() + "_" + &file_lib.get_line(meta.start, meta.get_file_id()).unwrap().to_string() + "_" + &meta.start.to_string(); + if var_access.is_none(){ + declarations.push(build_declaration( + meta.clone(), + VariableType::Component, + id_anon_temp.clone(), + Vec::new(), + )); + } else{ + declarations.push(build_declaration( + meta.clone(), + VariableType::AnonymousComponent, + id_anon_temp.clone(), + vec![var_access.as_ref().unwrap().clone()], + )); + } + let call = build_call(meta.clone(), id, params); + if call.contains_anonymous_comp(){ + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); + } + + let exp_with_call = if is_parallel { + build_parallel_op(meta.clone(), call) + } else { + call + }; + let access = if var_access.is_none(){ + Vec::new() + } else{ + vec![build_array_access(var_access.as_ref().unwrap().clone())] + }; + let sub = build_substitution(meta.clone(), id_anon_temp.clone(), + access.clone(), AssignOp::AssignVar, exp_with_call); + seq_substs.push(sub); + for inp in inputs{ + let mut acc = if var_access.is_none(){ + Vec::new() + } else{ + vec![build_array_access(var_access.as_ref().unwrap().clone())] + }; + + acc.push(Access::ComponentAccess(inp.0.clone())); + let (mut stmts, mut declarations2, new_exp) = remove_anonymous_from_expression( + &mut templates.clone(), + file_lib, + signals.get(i).unwrap().clone(), + var_access + )?; + if new_exp.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(new_exp.get_meta().clone(),"Inputs of an anonymous component cannot contain anonymous calls".to_string())); + } + seq_substs.append(&mut stmts); + declarations.append(&mut declarations2); + let subs = Statement::Substitution { meta: meta.clone(), var: id_anon_temp.clone(), access: acc, op: AssignOp::AssignConstraintSignal, rhe: new_exp }; + i+=1; + seq_substs.push(subs); + } + let outputs = template.unwrap().get_declaration_outputs(); + if outputs.len() == 1 { + let output = outputs.get(0).unwrap().0.clone(); + let mut acc = if var_access.is_none(){ + Vec::new() + } else{ + vec![build_array_access(var_access.as_ref().unwrap().clone())] + }; + + acc.push(Access::ComponentAccess(output.clone())); + let out_exp = Expression::Variable { meta: meta.clone(), name: id_anon_temp, access: acc }; + Result::Ok((vec![Statement::Block { meta: meta, stmts: seq_substs }],declarations,out_exp)) + + } else{ + let mut new_values = Vec::new(); + for output in outputs { + let mut acc = if var_access.is_none(){ + Vec::new() + } else{ + vec![build_array_access(var_access.as_ref().unwrap().clone())] + }; + acc.push(Access::ComponentAccess(output.0.clone())); + let out_exp = Expression::Variable { meta: meta.clone(), name: id_anon_temp.clone(), access: acc }; + new_values.push(out_exp); + } + let out_exp = Tuple {meta : meta.clone(), values : new_values}; + Result::Ok((vec![Statement::Block { meta: meta, stmts: seq_substs }], declarations, out_exp)) + + } + }, + Tuple { meta, values } => { + let mut new_values = Vec::new(); + let mut new_stmts : Vec = Vec::new(); + let mut declarations : Vec = Vec::new(); + for val in values{ + let result = remove_anonymous_from_expression(templates, file_lib, val, var_access); + match result { + Ok((mut stm, mut declaration, val2)) => { + new_stmts.append(&mut stm); + new_values.push(val2); + declarations.append(&mut declaration); + }, + Err(er) => {return Result::Err(er);}, + } + } + Result::Ok((new_stmts, declarations, build_tuple(meta, new_values))) + }, + ParallelOp { meta, rhe } => { + if !rhe.is_call() && !rhe.is_anonymous_comp() && rhe.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"Bad use of parallel operator in combination with anonymous components ".to_string())); + } + else if rhe.is_call() && rhe.contains_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a template call ".to_string())); + } + else if rhe.is_anonymous_comp(){ + let rhe2 = rhe.make_anonymous_parallel(); + return remove_anonymous_from_expression(templates, file_lib, rhe2, var_access); + } + Result::Ok((Vec::new(),Vec::new(),exp)) + }, + } +} + +pub fn separate_declarations_in_comp_var_subs(declarations: Vec) -> (Vec, Vec, Vec){ + let mut components_dec = Vec::new(); + let mut variables_dec = Vec::new(); + let mut substitutions = Vec::new(); + for dec in declarations { + if let Statement::Declaration { ref xtype, .. } = dec { + if VariableType::Component.eq(&xtype) || VariableType::AnonymousComponent.eq(&xtype){ + components_dec.push(dec); + } + else if VariableType::Var.eq(&xtype) { + variables_dec.push(dec); + } + else { + unreachable!(); + } + } + else if let Statement::Substitution {.. } = dec { + substitutions.push(dec); + } else{ + unreachable!(); + } + } + (components_dec, variables_dec, substitutions) +} +pub fn apply_syntactic_sugar(program_archive : &mut ProgramArchive) -> Result<(),Report> { + let mut new_templates : HashMap = HashMap::new(); + if program_archive.get_main_expression().is_anonymous_comp() { + return Result::Err(AnonymousCompError::anonymous_general_error(program_archive.get_main_expression().get_meta().clone(),"The main component cannot contain an anonymous call ".to_string())); + + } + for temp in program_archive.templates.clone() { + let t = temp.1.clone(); + let body = t.get_body().clone(); + let (new_body, initializations) = remove_anonymous_from_statement(&mut program_archive.templates, &program_archive.file_library, body, &None)?; + if let Statement::Block { meta, mut stmts } = new_body { + let (component_decs, variable_decs, mut substitutions) = separate_declarations_in_comp_var_subs(initializations); + let mut init_block = vec![ + build_initialization_block(meta.clone(), VariableType::Var, variable_decs), + build_initialization_block(meta.clone(), VariableType::Component, component_decs)]; + init_block.append(&mut substitutions); + init_block.append(&mut stmts); + let new_body_with_inits = build_block(meta, init_block); + let new_body = remove_tuples_from_statement(&mut program_archive.templates, &program_archive.file_library, new_body_with_inits)?; + let t2 = TemplateData::copy(t.get_name().to_string(), t.get_file_id(), new_body, t.get_num_of_params(), t.get_name_of_params().clone(), + t.get_param_location(), t.get_inputs().clone(), t.get_outputs().clone(), t.is_parallel(), t.is_custom_gate(), t.get_declaration_inputs().clone(), t.get_declaration_outputs().clone()); + new_templates.insert(temp.0.clone(), t2); + } else{ + unreachable!() + } + } + program_archive.templates = new_templates; + Result::Ok(()) +} + +fn remove_tuples_from_statement(templates: &mut HashMap, file_lib: &FileLibrary, stm: Statement) -> Result { + match stm.clone() { + Statement::MultSubstitution { meta, lhe, op, rhe } => { + let ok = remove_tuple_from_expression(templates, file_lib, lhe)?; + let ok2 = remove_tuple_from_expression(templates, file_lib, rhe)?; + match (ok, ok2) { + (Expression::Tuple { values: mut values1, .. }, + Expression::Tuple { values: mut values2, .. }) => { + if values1.len() == values2.len() { + let mut substs = Vec::new(); + while values1.len() > 0 { + let lhe = values1.remove(0); + if let Expression::Variable { meta, name, access } = lhe { + let rhe = values2.remove(0); + if name != "_" { + substs.push(build_substitution(meta.clone(), name.clone(), access.to_vec(), op, rhe)); + } + } else{ + return Result::Err(TupleError::tuple_general_error(meta.clone(),"The elements of the receiving tuple must be signals or variables.".to_string())); + } + } + return Result::Ok(build_block(meta.clone(),substs)); + } else if values1.len() > 0 { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"The number of elements in both tuples does not coincide".to_string())); + } else { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"This expression must be in the right side of an assignment".to_string())); + } + }, + (lhe, rhe) => { + if lhe.is_tuple() || lhe.is_variable(){ + return Result::Err(TupleError::tuple_general_error(rhe.get_meta().clone(),"This expression must be a tuple or an anonymous component".to_string())); + } else { + return Result::Err(TupleError::tuple_general_error(lhe.get_meta().clone(),"This expression must be a tuple, a component, a signal or a variable ".to_string())); + } + } + } + }, + Statement::IfThenElse { meta, cond, if_case, else_case } + => { + if cond.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a condition ".to_string())); + } else{ + let if_ok = remove_tuples_from_statement(templates, file_lib, *if_case)?; + let b_if = Box::new(if_ok); + if else_case.is_none(){ + Result::Ok(Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::None}) + }else { + let else_c = *(else_case.unwrap()); + let else_ok = remove_tuples_from_statement(templates, file_lib, else_c)?; + let b_else = Box::new(else_ok); + Result::Ok(Statement::IfThenElse { meta : meta, cond : cond, if_case: b_if, else_case: Option::Some(b_else)}) + } + } + } + + Statement::While { meta, cond, stmt } => { + if cond.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a condition ".to_string())); + } else{ + let while_ok = remove_tuples_from_statement(templates, file_lib, *stmt)?; + let b_while = Box::new(while_ok); + Result::Ok(Statement::While { meta : meta, cond : cond, stmt : b_while}) + } + } + Statement::LogCall {meta, args } => { + let mut newargs = Vec::new(); + for arg in args { + match arg { + LogArgument::LogStr(str) => {newargs.push(LogArgument::LogStr(str));}, + LogArgument::LogExp(exp) => { + let mut args2 = separate_tuple_for_logcall(vec![exp]); + newargs.append(&mut args2); + }, + } + } + Result::Ok(build_log_call(meta, newargs)) + } + Statement::Assert { meta, arg} => { Result::Ok(build_assert(meta, arg))} + Statement::Return { meta, value: arg}=> { + if arg.contains_tuple(){ + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside a function ".to_string())); + } + else{ Result::Ok(build_return(meta, arg))} + } + Statement::ConstraintEquality {meta, lhe, rhe } => { + if lhe.contains_tuple() || rhe.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used with the operator === ".to_string())); + } + else{ Result::Ok(build_constraint_equality(meta, lhe, rhe)) } + } + Statement::Declaration { meta , xtype , name , + dimensions, .. } => + { + for exp in dimensions.clone(){ + if exp.contains_tuple(){ + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); + } + } + Result::Ok(build_declaration(meta, xtype, name, dimensions)) + } + Statement::InitializationBlock { meta, xtype, initializations } => + { + let mut new_inits = Vec::new(); + for stmt in initializations { + let stmt_ok = remove_tuples_from_statement(templates, file_lib, stmt)?; + new_inits.push(stmt_ok); + } + Result::Ok(Statement::InitializationBlock { meta: meta, xtype: xtype, initializations: new_inits }) + } + Statement::Block { meta, stmts } => { + let mut new_stmts = Vec::new(); + for stmt in stmts { + let stmt_ok = remove_tuples_from_statement(templates, file_lib, stmt)?; + new_stmts.push(stmt_ok); + } + Result::Ok(Statement::Block { meta : meta, stmts: new_stmts}) + } + Statement::Substitution { meta, var, op, rhe, access} => { + let new_rhe = remove_tuple_from_expression(templates, file_lib, rhe)?; + if new_rhe.is_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"Left-side of the statement is not a tuple".to_string())); + } + if var != "_" { + Result::Ok(Statement::Substitution { meta: meta.clone(), var: var, access: access, op: op, rhe: new_rhe }) + } + else {//If this + Result::Ok(build_block(meta, Vec::new())) + } + } + } +} + +fn separate_tuple_for_logcall(values: Vec) -> Vec { + let mut new_values = Vec::new(); + for val in values { + if let Expression::Tuple { values : values2, .. } = val { + new_values.push(LogArgument::LogStr("(".to_string())); + let mut new_values2 = separate_tuple_for_logcall(values2); + new_values.append(&mut new_values2); + new_values.push(LogArgument::LogStr(")".to_string())); + } + else { + new_values.push(LogArgument::LogExp(val)); + } + } + new_values +} + + +pub fn remove_tuple_from_expression(templates : &mut HashMap, file_lib : & FileLibrary, exp : Expression) -> Result{ + use Expression::*; + match exp.clone() { + ArrayInLine { meta, values } => { + for value in values{ + if value.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); + } + } + Result::Ok(exp) + }, + UniformArray { meta, value, dimension } => { + if value.contains_tuple() || dimension.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to define a dimension of an array ".to_string())); + } + Result::Ok(exp) + }, + Number(_, _) => { Result::Ok(exp) }, + Variable { meta, .. } => { + if exp.contains_tuple(){ + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used to access an array ".to_string())); + + } + Result::Ok(exp) + }, + InfixOp { meta, lhe, rhe, .. } => { + if lhe.contains_tuple() || rhe.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in the middle of an operation".to_string())); + } + Result::Ok(exp) + }, + PrefixOp { meta, rhe, .. } => { + if rhe.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in the middle of an operation".to_string())); + } + Result::Ok(exp) + }, + InlineSwitchOp { meta, cond, if_true, if_false } => { + if cond.contains_tuple() || if_true.contains_tuple() || if_false.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used inside an inline switch".to_string())); + } + Result::Ok(exp) + }, + Call { meta, args, .. } => { + for value in args{ + if value.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used as a parameter of a function call".to_string())); + } + } + Result::Ok(exp) + }, + AnonymousComp { .. } => { + unreachable!(); + } + Tuple { meta, values } => { + let mut unfolded_values = Vec::new(); + for val in values { + let exp = remove_tuple_from_expression(templates, file_lib, val)?; + if let Tuple { values: mut values2, ..} = exp { + unfolded_values.append(&mut values2); + } else { + unfolded_values.push(exp); + } + } + Result::Ok(build_tuple(meta, unfolded_values)) + }, + ParallelOp { meta, rhe} => { + if rhe.contains_tuple() { + return Result::Err(TupleError::tuple_general_error(meta.clone(),"A tuple cannot be used in a parallel operator ".to_string())); + } + Result::Ok(exp) + }, + } +} diff --git a/program_structure/Cargo.toml b/program_structure/Cargo.toml index f70fb2f0b..8d02631a4 100644 --- a/program_structure/Cargo.toml +++ b/program_structure/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "program_structure" -version = "2.0.9" +version = "2.1.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index 8c6fe2d77..626b79d9a 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -195,6 +195,12 @@ pub enum Statement { op: AssignOp, rhe: Expression, }, + MultSubstitution { + meta: Meta, + lhe: Expression, + op: AssignOp, + rhe: Expression, + }, ConstraintEquality { meta: Meta, lhe: Expression, @@ -214,13 +220,6 @@ pub enum Statement { }, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub enum SignalElementType { - Empty, - Binary, - FieldElement, -} - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub enum SignalType { Output, @@ -228,11 +227,14 @@ pub enum SignalType { Intermediate, } -#[derive(Copy, Clone, PartialEq, Ord, PartialOrd, Eq)] +pub type TagList = Vec; + +#[derive(Clone, PartialEq, Ord, PartialOrd, Eq)] pub enum VariableType { Var, - Signal(SignalType, SignalElementType), + Signal(SignalType, TagList), Component, + AnonymousComponent, } #[derive(Clone)] @@ -269,10 +271,21 @@ pub enum Expression { id: String, args: Vec, }, + AnonymousComp{ + meta: Meta, + id: String, + is_parallel: bool, + params: Vec, + signals: Vec, + }, ArrayInLine { meta: Meta, values: Vec, }, + Tuple { + meta: Meta, + values: Vec, + }, UniformArray { meta: Meta, value: Box, @@ -337,6 +350,7 @@ pub enum TypeReduction { Variable, Component, Signal, + Tag, } #[derive(Clone)] @@ -379,6 +393,9 @@ impl TypeKnowledge { pub fn is_signal(&self) -> bool { self.get_reduces_to() == TypeReduction::Signal } + pub fn is_tag(&self) -> bool { + self.get_reduces_to() == TypeReduction::Tag + } } #[derive(Default, Clone)] diff --git a/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs b/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs index cb199fa07..2041797d0 100644 --- a/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs +++ b/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs @@ -11,6 +11,10 @@ pub struct Symbol { pub init: Option, } +pub struct TupleInit{ + pub tuple_init : (AssignOp,Expression) +} + pub fn assign_with_op_shortcut( op: ExpressionInfixOpcode, meta: Meta, @@ -57,7 +61,7 @@ pub fn split_declaration_into_single_nodes( for symbol in symbols { let with_meta = meta.clone(); - let has_type = xtype; + let has_type = xtype.clone(); let name = symbol.name.clone(); let dimensions = symbol.is_array; let possible_init = symbol.init; @@ -80,4 +84,42 @@ pub fn split_declaration_into_single_nodes( } } build_initialization_block(meta, xtype, initializations) +} + +pub fn split_declaration_into_single_nodes_and_multisubstitution( + meta: Meta, + xtype: VariableType, + symbols: Vec, + init: Option, +) -> Statement { + use crate::ast_shortcuts::VariableType::Var; + + let mut initializations = Vec::new(); + let mut values = Vec::new(); + for symbol in symbols { + let with_meta = meta.clone(); + let has_type = xtype.clone(); + let name = symbol.name.clone(); + let dimensions = symbol.is_array; + debug_assert!(symbol.init.is_none()); + let single_declaration = build_declaration(with_meta.clone(), has_type, name.clone(), dimensions.clone()); + initializations.push(single_declaration); + if xtype == Var && init.is_none() { + 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![], AssignOp::AssignVar, value); + initializations.push(substitution); + } + values.push(Expression::Variable { meta: with_meta.clone(), name: name, access: Vec::new() }) + } + if let Some( tuple) = init { + let (op,expression) = tuple.tuple_init; + let multi_sub = build_mult_substitution(meta.clone(), build_tuple(meta.clone(), values), op, expression); + initializations.push(multi_sub); + } + build_initialization_block(meta, xtype, initializations) } \ No newline at end of file diff --git a/program_structure/src/abstract_syntax_tree/expression_builders.rs b/program_structure/src/abstract_syntax_tree/expression_builders.rs index da4c6a933..cde3c035a 100644 --- a/program_structure/src/abstract_syntax_tree/expression_builders.rs +++ b/program_structure/src/abstract_syntax_tree/expression_builders.rs @@ -51,10 +51,18 @@ pub fn build_call(meta: Meta, id: String, args: Vec) -> Expression { Call { meta, id, args } } +pub fn build_anonymous_component(meta: Meta, id: String, params: Vec, signals: Vec, is_parallel: bool) -> Expression { + AnonymousComp { meta, id, params, signals, is_parallel } +} + pub fn build_array_in_line(meta: Meta, values: Vec) -> Expression { ArrayInLine { meta, values } } +pub fn build_tuple(meta: Meta, values: Vec) -> Expression { + Tuple { meta, values } +} + pub fn build_uniform_array(meta: Meta, value: Expression, dimension: Expression) -> Expression { UniformArray { meta, value: Box::new(value), dimension: Box::new(dimension) } } diff --git a/program_structure/src/abstract_syntax_tree/expression_impl.rs b/program_structure/src/abstract_syntax_tree/expression_impl.rs index d3bdbd3e2..c5fa0e322 100644 --- a/program_structure/src/abstract_syntax_tree/expression_impl.rs +++ b/program_structure/src/abstract_syntax_tree/expression_impl.rs @@ -1,3 +1,5 @@ +use crate::expression_builders::build_anonymous_component; + use super::ast::*; impl Expression { @@ -11,8 +13,10 @@ impl Expression { | Variable { meta, .. } | Number(meta, ..) | Call { meta, .. } + | AnonymousComp { meta, ..} | ArrayInLine { meta, .. } => meta, | UniformArray { meta, .. } => meta, + | Tuple {meta, ..} => meta, } } pub fn get_mut_meta(&mut self) -> &mut Meta { @@ -25,8 +29,10 @@ impl Expression { | Variable { meta, .. } | Number(meta, ..) | Call { meta, .. } + | AnonymousComp {meta, ..} | ArrayInLine { meta, .. } => meta, | UniformArray { meta, .. } => meta, + | Tuple {meta, ..} => meta, } } @@ -58,7 +64,15 @@ impl Expression { false } } - + + pub fn is_tuple(&self) -> bool { + use Expression::*; + if let Tuple { .. } = self { + true + } else { + false + } + } pub fn is_switch(&self) -> bool { use Expression::*; if let InlineSwitchOp { .. } = self { @@ -103,6 +117,100 @@ impl Expression { false } } + + pub fn is_anonymous_comp(&self) -> bool { + use Expression::*; + if let AnonymousComp { .. } = self { + true + } else { + false + } + } + + pub fn make_anonymous_parallel(self) -> Expression { + use Expression::*; + match self { + AnonymousComp { meta, id, params, signals, .. } => { + build_anonymous_component(meta, id, params, signals, true) + } + _ => self, + } + } + + pub fn contains_anonymous_comp(&self) -> bool { + use Expression::*; + match &self { + InfixOp { lhe, rhe , ..} | UniformArray { value : lhe, dimension : rhe, .. } => { + lhe.contains_anonymous_comp() || rhe.contains_anonymous_comp() + }, + PrefixOp { rhe, .. } => { + rhe.contains_anonymous_comp() + }, + InlineSwitchOp { cond, if_true, if_false, .. } => { + cond.contains_anonymous_comp() || if_true.contains_anonymous_comp() || if_false.contains_anonymous_comp() + }, + Call { args, .. } | Tuple {values: args, ..} | ArrayInLine { values : args, .. } => { + for arg in args{ + if arg.contains_anonymous_comp() { return true;} + } + false + }, + AnonymousComp { .. } => { true }, + Variable { access, .. } => { + for ac in access{ + match ac { + Access::ComponentAccess(_) => {}, + Access::ArrayAccess( exp ) => if exp.contains_anonymous_comp() {return true;}, + } + } + false + }, + Number(_, _) => {false } + ParallelOp { rhe , .. } => { rhe.contains_anonymous_comp() }, + } + } + + pub fn contains_tuple(&self) -> bool { + use Expression::*; + match &self { + InfixOp { lhe, rhe , ..} | UniformArray { value : lhe, dimension : rhe, .. } => { + lhe.contains_tuple() || rhe.contains_tuple() + }, + PrefixOp { rhe, .. } => { + rhe.contains_tuple() + }, + InlineSwitchOp { cond, if_true, if_false, .. } => { + cond.contains_tuple() || if_true.contains_tuple() || if_false.contains_tuple() + }, + Call { args, .. } | ArrayInLine { values : args, .. } => { + for arg in args{ + if arg.contains_tuple() { return true;} + } + false + }, + AnonymousComp { params, signals, .. } => { + for ac in params{ + if ac.contains_tuple() {return true;} + } + for ac in signals{ + if ac.contains_tuple() {return true;} + } + false + }, + Variable { access, .. } => { + for ac in access{ + match ac { + Access::ComponentAccess(_) => {}, + Access::ArrayAccess( exp ) => if exp.contains_tuple() {return true;}, + } + } + false + }, + Number(_, _) => {false }, + Tuple { .. } => {true}, + ParallelOp { rhe, .. } => {rhe.contains_tuple()}, + } + } } impl FillMeta for Expression { @@ -126,6 +234,10 @@ impl FillMeta for Expression { UniformArray { meta, value, dimension, .. } => { fill_uniform_array(meta, value, dimension, file_id, element_id) } + AnonymousComp { meta, params, signals, .. } => { + fill_anonymous_comp(meta, params, signals, file_id, element_id) + }, + Tuple { meta, values} => {fill_tuple(meta,values,file_id,element_id)} } } } @@ -186,6 +298,15 @@ fn fill_call(meta: &mut Meta, args: &mut [Expression], file_id: usize, element_i } } +fn fill_anonymous_comp(meta: &mut Meta, params: &mut [Expression],signals: &mut [Expression], file_id: usize, element_id: &mut usize) { + meta.set_file_id(file_id); + for a in params { + a.fill(file_id, element_id); + } + for a in signals { + a.fill(file_id, element_id); + } +} fn fill_array_inline( meta: &mut Meta, values: &mut [Expression], @@ -198,6 +319,18 @@ fn fill_array_inline( } } +fn fill_tuple( + meta: &mut Meta, + values: &mut [Expression], + file_id: usize, + element_id: &mut usize, +) { + meta.set_file_id(file_id); + for v in values { + v.fill(file_id, element_id); + } +} + fn fill_uniform_array( meta: &mut Meta, value: &mut Expression, diff --git a/program_structure/src/abstract_syntax_tree/statement_builders.rs b/program_structure/src/abstract_syntax_tree/statement_builders.rs index 3caee213e..aebcbc04c 100644 --- a/program_structure/src/abstract_syntax_tree/statement_builders.rs +++ b/program_structure/src/abstract_syntax_tree/statement_builders.rs @@ -79,3 +79,11 @@ fn split_string(str: String) -> Vec { pub fn build_assert(meta: Meta, arg: Expression) -> Statement { Assert { meta, arg } } + +pub fn build_mult_substitution(meta: Meta, lhe: Expression, op : AssignOp, rhe: Expression) -> Statement { + MultSubstitution { meta: meta.clone(), lhe, op, rhe } +} + +pub fn build_anonymous_component_statement(meta: Meta, arg: Expression) -> Statement { + MultSubstitution { meta: meta.clone(), lhe: crate::expression_builders::build_tuple(meta, Vec::new()), op: AssignOp::AssignConstraintSignal, rhe: arg } +} \ No newline at end of file diff --git a/program_structure/src/abstract_syntax_tree/statement_impl.rs b/program_structure/src/abstract_syntax_tree/statement_impl.rs index c78912ad8..496eac1dd 100644 --- a/program_structure/src/abstract_syntax_tree/statement_impl.rs +++ b/program_structure/src/abstract_syntax_tree/statement_impl.rs @@ -14,6 +14,7 @@ impl Statement { | Assert { meta, .. } | ConstraintEquality { meta, .. } | InitializationBlock { meta, .. } => meta, + | MultSubstitution { meta, ..} => meta, } } pub fn get_mut_meta(&mut self) -> &mut Meta { @@ -29,6 +30,7 @@ impl Statement { | Assert { meta, .. } | ConstraintEquality { meta, .. } | InitializationBlock { meta, .. } => meta, + | MultSubstitution { meta, ..} => meta, } } @@ -134,12 +136,17 @@ impl FillMeta for Statement { Substitution { meta, access, rhe, .. } => { fill_substitution(meta, access, rhe, file_id, element_id) } + MultSubstitution { meta, lhe, rhe, .. } + => { + fill_mult_substitution(meta, lhe, rhe, file_id, element_id); + } ConstraintEquality { meta, lhe, rhe } => { fill_constraint_equality(meta, lhe, rhe, file_id, element_id) } LogCall { meta, args, .. } => fill_log_call(meta, args, file_id, element_id), Block { meta, stmts, .. } => fill_block(meta, stmts, file_id, element_id), Assert { meta, arg, .. } => fill_assert(meta, arg, file_id, element_id), + } } } @@ -217,6 +224,18 @@ fn fill_substitution( } } +fn fill_mult_substitution( + meta: &mut Meta, + lhe: &mut Expression, + rhe: &mut Expression, + file_id: usize, + element_id: &mut usize, +) { + meta.set_file_id(file_id); + rhe.fill(file_id, element_id); + lhe.fill(file_id,element_id); +} + fn fill_constraint_equality( meta: &mut Meta, lhe: &mut Expression, diff --git a/program_structure/src/program_library/error_code.rs b/program_structure/src/program_library/error_code.rs index 55a21a351..ccb2accc7 100644 --- a/program_structure/src/program_library/error_code.rs +++ b/program_structure/src/program_library/error_code.rs @@ -13,6 +13,7 @@ pub enum ReportCode { UninitializedSymbolInExpression, UnableToTypeFunction, UnreachableConstraints, + UnreachableTags, UnknownIndex, UnknownDimension, SameFunctionDeclaredTwice, @@ -34,6 +35,7 @@ pub enum ReportCode { NoMainFoundInProject, NoCompilerVersionWarning, MultipleMainInComponent, + MainComponentWithTags, TemplateCallAsArgument, TemplateWrongNumberOfArguments, TemplateWithReturnStatement, @@ -47,6 +49,8 @@ pub enum ReportCode { InconsistentStaticInformation, InvalidArrayAccess, InvalidSignalAccess, + InvalidTagAccess, + InvalidTagAccessAfterArray, InvalidArraySize, InvalidArrayType, ForStatementIllConstructed, @@ -58,6 +62,9 @@ pub enum ReportCode { WrongSignalTags, InvalidPartialArray, MustBeSingleArithmetic, + MustBeArithmetic, + OutputTagCannotBeModifiedOutside, + MustBeSameDimension, ExpectedDimDiffGotDim(usize, usize), RuntimeError, RuntimeWarning, @@ -75,6 +82,9 @@ pub enum ReportCode { CustomGateSubComponentError, CustomGatesPragmaError, CustomGatesVersionError, + AnonymousCompError, + TupleError, + InvalidSignalTagAccess, } impl fmt::Display for ReportCode { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -91,6 +101,8 @@ impl fmt::Display for ReportCode { UninitializedSymbolInExpression => "T2003", UnableToTypeFunction => "T2004", UnreachableConstraints => "T2005", + UnreachableTags => "T2015", + MainComponentWithTags => "T2016", SameFunctionDeclaredTwice => "T2006", SameTemplateDeclaredTwice => "T2007", SameSymbolDeclaredTwice => "T2008", @@ -120,6 +132,9 @@ impl fmt::Display for ReportCode { InconsistentStaticInformation => "T2031", InvalidArrayAccess => "T2032", InvalidSignalAccess => "T2046", + InvalidSignalTagAccess => "T2047", + InvalidTagAccess => "T2048", + InvalidTagAccessAfterArray => "T2049", InvalidArraySize => "T2033", InvalidArrayType => "T2034", ForStatementIllConstructed => "T2035", @@ -134,6 +149,9 @@ impl fmt::Display for ReportCode { InvalidPartialArray => "T2043", MustBeSingleArithmetic => "T2044", ExpectedDimDiffGotDim(..) => "T2045", + MustBeSameDimension => "T2046", + MustBeArithmetic => "T2047", + OutputTagCannotBeModifiedOutside => "T2048", RuntimeError => "T3001", RuntimeWarning => "T3002", UnknownDimension => "T20460", @@ -152,6 +170,8 @@ impl fmt::Display for ReportCode { CustomGateSubComponentError => "CG03", CustomGatesPragmaError => "CG04", CustomGatesVersionError => "CG05", + AnonymousCompError => "TAC01", + TupleError => "TAC02" }; f.write_str(string_format) } diff --git a/program_structure/src/program_library/template_data.rs b/program_structure/src/program_library/template_data.rs index 5ffd52c56..134f7e405 100644 --- a/program_structure/src/program_library/template_data.rs +++ b/program_structure/src/program_library/template_data.rs @@ -1,11 +1,13 @@ use super::ast; -use super::ast::{FillMeta, SignalElementType, Statement}; +use super::ast::{FillMeta, Statement}; use super::file_definition::FileID; use crate::file_definition::FileLocation; -use std::collections::hash_map::HashMap; +use std::collections::{HashMap, HashSet, BTreeMap}; pub type TemplateInfo = HashMap; -type SignalInfo = HashMap; +pub type TagInfo = HashSet; +type SignalInfo = BTreeMap; +type SignalDeclarationOrder = Vec<(String, usize)>; #[derive(Clone)] pub struct TemplateData { @@ -19,6 +21,9 @@ pub struct TemplateData { output_signals: SignalInfo, is_parallel: bool, is_custom_gate: bool, + /* Only used to know the order in which signals are declared.*/ + input_declarations: SignalDeclarationOrder, + output_declarations: SignalDeclarationOrder, } impl TemplateData { @@ -36,7 +41,9 @@ impl TemplateData { body.fill(file_id, elem_id); let mut input_signals = SignalInfo::new(); let mut output_signals = SignalInfo::new(); - fill_inputs_and_outputs(&body, &mut input_signals, &mut output_signals); + let mut input_declarations = SignalDeclarationOrder::new(); + let mut output_declarations = SignalDeclarationOrder::new(); + fill_inputs_and_outputs(&body, &mut input_signals, &mut output_signals, &mut input_declarations, &mut output_declarations); TemplateData { name, file_id, @@ -48,6 +55,38 @@ impl TemplateData { output_signals, is_parallel, is_custom_gate, + input_declarations, + output_declarations + } + } + + pub fn copy( + name: String, + file_id: FileID, + body: Statement, + num_of_params: usize, + name_of_params: Vec, + param_location: FileLocation, + input_signals: SignalInfo, + output_signals: SignalInfo, + is_parallel: bool, + is_custom_gate: bool, + input_declarations :SignalDeclarationOrder, + output_declarations : SignalDeclarationOrder + ) -> TemplateData { + TemplateData { + name, + file_id, + body, + num_of_params, + name_of_params, + param_location, + input_signals, + output_signals, + is_parallel, + is_custom_gate, + input_declarations, + output_declarations } } pub fn get_file_id(&self) -> FileID { @@ -80,10 +119,10 @@ impl TemplateData { pub fn get_name_of_params(&self) -> &Vec { &self.name_of_params } - pub fn get_input_info(&self, name: &str) -> Option<&(usize, SignalElementType)> { + pub fn get_input_info(&self, name: &str) -> Option<&(usize, TagInfo)> { self.input_signals.get(name) } - pub fn get_output_info(&self, name: &str) -> Option<&(usize, SignalElementType)> { + pub fn get_output_info(&self, name: &str) -> Option<&(usize, TagInfo)> { self.output_signals.get(name) } pub fn get_inputs(&self) -> &SignalInfo { @@ -92,6 +131,12 @@ impl TemplateData { pub fn get_outputs(&self) -> &SignalInfo { &self.output_signals } + pub fn get_declaration_inputs(&self) -> &SignalDeclarationOrder { + &&self.input_declarations + } + pub fn get_declaration_outputs(&self) -> &SignalDeclarationOrder { + &self.output_declarations + } pub fn get_name(&self) -> &str { &self.name } @@ -107,37 +152,46 @@ fn fill_inputs_and_outputs( template_statement: &Statement, input_signals: &mut SignalInfo, output_signals: &mut SignalInfo, + input_declarations : &mut SignalDeclarationOrder, + output_declarations : &mut SignalDeclarationOrder ) { match template_statement { Statement::IfThenElse { if_case, else_case, .. } => { - fill_inputs_and_outputs(if_case, input_signals, output_signals); + fill_inputs_and_outputs(if_case, input_signals, output_signals, input_declarations, output_declarations); if let Option::Some(else_value) = else_case { - fill_inputs_and_outputs(else_value, input_signals, output_signals); + fill_inputs_and_outputs(else_value, input_signals, output_signals, input_declarations, output_declarations); } } Statement::Block { stmts, .. } => { for stmt in stmts.iter() { - fill_inputs_and_outputs(stmt, input_signals, output_signals); + fill_inputs_and_outputs(stmt, input_signals, output_signals, input_declarations, output_declarations); } } Statement::While { stmt, .. } => { - fill_inputs_and_outputs(stmt, input_signals, output_signals); + fill_inputs_and_outputs(stmt, input_signals, output_signals, input_declarations, output_declarations); } Statement::InitializationBlock { initializations, .. } => { for initialization in initializations.iter() { - fill_inputs_and_outputs(initialization, input_signals, output_signals); + fill_inputs_and_outputs(initialization, input_signals, output_signals, input_declarations, output_declarations); } } Statement::Declaration { xtype, name, dimensions, .. } => { - if let ast::VariableType::Signal(stype, tag) = xtype { + if let ast::VariableType::Signal(stype, tag_list) = xtype { let signal_name = name.clone(); let dim = dimensions.len(); + let mut tag_info = HashSet::new(); + for tag in tag_list{ + tag_info.insert(tag.clone()); + } + match stype { ast::SignalType::Input => { - input_signals.insert(signal_name, (dim, *tag)); + input_signals.insert(signal_name.clone(), (dim, tag_info)); + input_declarations.push((signal_name,dim)); } ast::SignalType::Output => { - output_signals.insert(signal_name, (dim, *tag)); + output_signals.insert(signal_name.clone(), (dim, tag_info)); + output_declarations.push((signal_name,dim)); } _ => {} //no need to deal with intermediate signals } diff --git a/program_structure/src/utils/memory_slice.rs b/program_structure/src/utils/memory_slice.rs index 5ccfd80c3..e0a81660f 100644 --- a/program_structure/src/utils/memory_slice.rs +++ b/program_structure/src/utils/memory_slice.rs @@ -6,7 +6,12 @@ pub enum MemoryError { InvalidAccess, UnknownSizeDimension, MismatchedDimensions, - MismatchedDimensionsWeak + MismatchedDimensionsWeak, + AssignmentMissingTags, + AssignmentTagAfterInit, + AssignmentTagTwice, + AssignmentTagInput, + TagValueNotInitializedAccess, } pub type SliceCapacity = usize; pub type SimpleSlice = MemorySlice; @@ -72,6 +77,7 @@ impl MemorySlice { memory_slice: &MemorySlice, access: &[SliceCapacity], new_values: &MemorySlice, + is_strict: bool, ) -> Result<(), MemoryError> { if access.len() + new_values.route.len() > memory_slice.route.len() { @@ -92,7 +98,11 @@ impl MemorySlice { while i < new_values.route.len() { if new_values.route[i] < memory_slice.route[initial_index_new + i] { - return Result::Err(MemoryError::MismatchedDimensionsWeak); + 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 new_values.route[i] > memory_slice.route[initial_index_new + i] { return Result::Err(MemoryError::MismatchedDimensions); @@ -167,15 +177,17 @@ impl MemorySlice { memory_slice: &mut MemorySlice, access: &[SliceCapacity], new_values: &MemorySlice, + is_strict:bool, ) -> Result<(), MemoryError> { - match MemorySlice::check_correct_dims(memory_slice, access, new_values){ + match MemorySlice::check_correct_dims(memory_slice, access, new_values, is_strict){ Result::Ok(_) => { 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) // { // return Result::Err(MemoryError::OutOfBoundsError); - // } + for value in new_values.values.iter() { memory_slice.values[cell] = value.clone(); cell += 1; @@ -199,12 +211,54 @@ impl MemorySlice { } } + pub fn insert_value_by_index( + memory_slice: &mut MemorySlice, + index: usize, + new_value: C, + )-> Result<(), MemoryError> { + if index > MemorySlice::get_number_of_cells(memory_slice) { + return Result::Err(MemoryError::OutOfBoundsError); + } + memory_slice.values[index] = new_value; + return Result::Ok(()); + } + + pub fn get_access_index( + memory_slice: &MemorySlice, + index: usize, + ) -> Result, MemoryError>{ + let mut number_cells = MemorySlice::get_number_of_cells(memory_slice); + if index > number_cells { + return Result::Err(MemoryError::OutOfBoundsError); + } + else{ + let mut access = vec![]; + let mut index_aux = index; + for pos in &memory_slice.route{ + number_cells = number_cells/pos; + access.push(index_aux / number_cells); + index_aux = index_aux % number_cells; + } + return Result::Ok(access); + } + } + pub fn access_values( memory_slice: &MemorySlice, access: &[SliceCapacity], ) -> Result, MemoryError> { MemorySlice::generate_slice_from_access(memory_slice, access) } + pub fn access_value_by_index( + memory_slice: &MemorySlice, + index: usize, + )-> Result { + if index > MemorySlice::get_number_of_cells(memory_slice) { + return Result::Err(MemoryError::OutOfBoundsError); + } + return Result::Ok(memory_slice.values[index].clone()); + } + pub fn get_reference_to_single_value<'a>( memory_slice: &'a MemorySlice, access: &[SliceCapacity], @@ -213,6 +267,15 @@ impl MemorySlice { let cell = MemorySlice::get_initial_cell(memory_slice, access)?; Result::Ok(&memory_slice.values[cell]) } + pub fn get_reference_to_single_value_by_index<'a>( + memory_slice: &'a MemorySlice, + index: usize, + ) -> Result<&'a C, MemoryError> { + if index > MemorySlice::get_number_of_cells(memory_slice) { + return Result::Err(MemoryError::OutOfBoundsError); + } + Result::Ok(&memory_slice.values[index]) + } pub fn get_mut_reference_to_single_value<'a>( memory_slice: &'a mut MemorySlice, access: &[SliceCapacity], diff --git a/type_analysis/Cargo.toml b/type_analysis/Cargo.toml index 8c2fbdde7..722ae8d35 100644 --- a/type_analysis/Cargo.toml +++ b/type_analysis/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "type_analysis" -version = "2.0.8" +version = "2.1.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/type_analysis/src/analyzers/custom_gate_analysis.rs b/type_analysis/src/analyzers/custom_gate_analysis.rs index 582ddf3c8..6e4e879de 100644 --- a/type_analysis/src/analyzers/custom_gate_analysis.rs +++ b/type_analysis/src/analyzers/custom_gate_analysis.rs @@ -47,7 +47,7 @@ pub fn custom_gate_analysis( ); warnings.push(warning); } - Component => { + Component | AnonymousComponent => { let mut error = Report::error( String::from("Component inside custom template"), ReportCode::CustomGateSubComponentError diff --git a/type_analysis/src/analyzers/functions_free_of_template_elements.rs b/type_analysis/src/analyzers/functions_free_of_template_elements.rs index f4bd36785..b508f05c6 100644 --- a/type_analysis/src/analyzers/functions_free_of_template_elements.rs +++ b/type_analysis/src/analyzers/functions_free_of_template_elements.rs @@ -27,6 +27,7 @@ fn analyse_statement( use Statement::*; let file_id = stmt.get_meta().get_file_id(); match stmt { + MultSubstitution { .. } => unreachable!(), IfThenElse { cond, if_case, else_case, .. } => { analyse_expression(cond, function_names, reports); analyse_statement(if_case, function_names, reports); @@ -191,5 +192,6 @@ fn analyse_expression( } + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } diff --git a/type_analysis/src/analyzers/mod.rs b/type_analysis/src/analyzers/mod.rs index 6c373dcb9..bcfc47dee 100644 --- a/type_analysis/src/analyzers/mod.rs +++ b/type_analysis/src/analyzers/mod.rs @@ -4,7 +4,6 @@ pub use functions_free_of_template_elements::free_of_template_elements; pub use no_returns_in_template::free_of_returns; pub use signal_declaration_analysis::check_signal_correctness; pub use symbol_analysis::check_naming_correctness; -pub use tag_analysis::tag_analysis; pub use type_check::type_check; pub use unknown_known_analysis::unknown_known_analysis; @@ -14,7 +13,6 @@ pub mod functions_free_of_template_elements; pub mod no_returns_in_template; pub mod signal_declaration_analysis; pub mod symbol_analysis; -pub mod tag_analysis; pub mod type_check; pub mod type_given_function; pub mod type_register; diff --git a/type_analysis/src/analyzers/signal_declaration_analysis.rs b/type_analysis/src/analyzers/signal_declaration_analysis.rs index ba8ac01dc..05845804e 100644 --- a/type_analysis/src/analyzers/signal_declaration_analysis.rs +++ b/type_analysis/src/analyzers/signal_declaration_analysis.rs @@ -41,7 +41,7 @@ fn treat_statement( } } InitializationBlock { meta, xtype, .. } => match xtype { - VariableType::Signal(_, _) | VariableType::Component => { + VariableType::Signal(_, _) | VariableType::Component | VariableType::AnonymousComponent => { if !signal_declaration_allowed { let mut report = Report::error( "Signal or component declaration outside initial scope".to_string(), diff --git a/type_analysis/src/analyzers/symbol_analysis.rs b/type_analysis/src/analyzers/symbol_analysis.rs index 065f240bf..0ecc67167 100644 --- a/type_analysis/src/analyzers/symbol_analysis.rs +++ b/type_analysis/src/analyzers/symbol_analysis.rs @@ -164,6 +164,7 @@ fn analyze_statement( environment: &mut Environment, ) { match stmt { + Statement::MultSubstitution { .. } => unreachable!(), Statement::Return { value, .. } => { analyze_expression(value, file_id, function_info, template_info, reports, environment) } diff --git a/type_analysis/src/analyzers/tag_analysis.rs b/type_analysis/src/analyzers/tag_analysis.rs index dafd87d91..d5b0ebe40 100644 --- a/type_analysis/src/analyzers/tag_analysis.rs +++ b/type_analysis/src/analyzers/tag_analysis.rs @@ -1,245 +1,238 @@ -use program_structure::ast::{ - Access, AssignOp, Expression, ExpressionPrefixOpcode, Meta, SignalElementType, SignalType, - Statement, VariableType, -}; -use program_structure::environment::CircomEnvironment; -use program_structure::error_code::ReportCode; -use program_structure::error_definition::{Report, ReportCollection}; -use program_structure::file_definition::{generate_file_location, FileID}; -use program_structure::program_archive::ProgramArchive; -use program_structure::template_data::TemplateData; -use std::collections::HashMap; +// use program_structure::ast::{ +// Access, AssignOp, Expression, ExpressionPrefixOpcode, Meta, SignalElementType, SignalType, +// Statement, VariableType, +// }; +// use program_structure::environment::CircomEnvironment; +// use program_structure::error_code::ReportCode; +// use program_structure::error_definition::{Report, ReportCollection}; +// use program_structure::file_definition::{generate_file_location, FileID}; +// use program_structure::program_archive::ProgramArchive; +// use program_structure::template_data::TemplateData; +// use std::collections::HashMap; -type TemplateInfo = HashMap; -type Environment = CircomEnvironment, SignalElementType, SignalElementType>; -enum ExpressionResult { - ArithmeticExpression(SignalElementType), - Template(Option), -} +// type TemplateInfo = HashMap; +// type Environment = CircomEnvironment, SignalElementType, SignalElementType>; +// enum ExpressionResult { +// ArithmeticExpression(SignalElementType), +// Template(Option), +// } -pub fn tag_analysis( - template_name: &str, - program_archive: &ProgramArchive, -) -> Result<(), ReportCollection> { - let template_body = program_archive.get_template_data(template_name).get_body_as_vec(); - let file_id = program_archive.get_template_data(template_name).get_file_id(); - let template_info = program_archive.get_templates(); +// pub fn tag_analysis( +// template_name: &str, +// program_archive: &ProgramArchive, +// ) -> Result<(), ReportCollection> { +// let template_body = program_archive.get_template_data(template_name).get_body_as_vec(); +// let file_id = program_archive.get_template_data(template_name).get_file_id(); +// let template_info = program_archive.get_templates(); - let mut environment = Environment::new(); - let args = program_archive.get_template_data(template_name).get_name_of_params().clone(); - for arg in args.iter() { - environment.add_variable(arg, SignalElementType::FieldElement); - } +// let mut environment = Environment::new(); +// let args = program_archive.get_template_data(template_name).get_name_of_params().clone(); +// for arg in args.iter() { +// environment.add_variable(arg, SignalElementType::FieldElement); +// } - let mut reports = ReportCollection::new(); - treat_sequence_of_statements( - template_body, - file_id, - template_info, - &mut reports, - &mut environment, - ); - if reports.is_empty() { - Result::Ok(()) - } else { - Result::Err(reports) - } -} +// let mut reports = ReportCollection::new(); +// treat_sequence_of_statements( +// template_body, +// file_id, +// template_info, +// &mut reports, +// &mut environment, +// ); +// if reports.is_empty() { +// Result::Ok(()) +// } else { +// Result::Err(reports) +// } +// } -fn statement_inspection( - stmt: &Statement, - file_id: FileID, - template_info: &TemplateInfo, - reports: &mut ReportCollection, - environment: &mut Environment, -) { - use Statement::*; - match stmt { - IfThenElse { if_case, else_case, .. } => { - statement_inspection(if_case, file_id, template_info, reports, environment); - if let Option::Some(else_stmt) = else_case { - statement_inspection(else_stmt, file_id, template_info, reports, environment); - } - } - While { stmt, .. } => { - statement_inspection(stmt, file_id, template_info, reports, environment); - } - Block { stmts, .. } => { - environment.add_variable_block(); - treat_sequence_of_statements(stmts, file_id, template_info, reports, environment); - environment.remove_variable_block(); - } - InitializationBlock { initializations, .. } => { - treat_sequence_of_statements( - initializations, - file_id, - template_info, - reports, - environment, - ); - } - Declaration { xtype, name, meta, .. } => { - use SignalType::*; - use VariableType::*; - match xtype { - Var => environment.add_variable(name, SignalElementType::FieldElement), - Component => environment.add_component(name, meta.component_inference.clone()), - Signal(signal_type, signal_element) => match signal_type { - Output => environment.add_output(name, *signal_element), - Intermediate => environment.add_intermediate(name, *signal_element), - Input => environment.add_input(name, *signal_element), - }, - } - } - Substitution { meta, var, access, rhe, op, .. } => { - use ExpressionResult::*; - let var_info = variable_inspection(var, access, environment, template_info); - let rhe_info = expression_inspection(rhe, template_info, reports, environment); - match (var_info, rhe_info) { - (Template(_), Template(assign)) => { - *environment.get_mut_component_or_break(var, file!(), line!()) = assign; - } - (ArithmeticExpression(tag_0), ArithmeticExpression(tag_1)) - if tag_0 == SignalElementType::Binary - && tag_1 == SignalElementType::FieldElement - && *op == AssignOp::AssignConstraintSignal => - { - add_report(ReportCode::WrongSignalTags, meta, file_id, reports) - } - _ => {} - } - } - _ => {} - } -} +// fn statement_inspection( +// stmt: &Statement, +// file_id: FileID, +// template_info: &TemplateInfo, +// reports: &mut ReportCollection, +// environment: &mut Environment, +// ) { +// use Statement::*; +// match stmt { +// IfThenElse { if_case, else_case, .. } => { +// statement_inspection(if_case, file_id, template_info, reports, environment); +// if let Option::Some(else_stmt) = else_case { +// statement_inspection(else_stmt, file_id, template_info, reports, environment); +// } +// } +// While { stmt, .. } => { +// statement_inspection(stmt, file_id, template_info, reports, environment); +// } +// Block { stmts, .. } => { +// environment.add_variable_block(); +// treat_sequence_of_statements(stmts, file_id, template_info, reports, environment); +// environment.remove_variable_block(); +// } +// InitializationBlock { initializations, .. } => { +// treat_sequence_of_statements( +// initializations, +// file_id, +// template_info, +// reports, +// environment, +// ); +// } +// Declaration { xtype, name, meta, .. } => { +// use SignalType::*; +// use VariableType::*; +// match xtype { +// Var => environment.add_variable(name, SignalElementType::FieldElement), +// Component => environment.add_component(name, meta.component_inference.clone()), +// Signal(signal_type, signal_element) => match signal_type { +// Output => environment.add_output(name, *signal_element), +// Intermediate => environment.add_intermediate(name, *signal_element), +// Input => environment.add_input(name, *signal_element), +// }, +// } +// } +// Substitution { meta, var, access, rhe, op, .. } => { +// use ExpressionResult::*; +// let var_info = variable_inspection(var, access, environment, template_info); +// let rhe_info = expression_inspection(rhe, template_info, reports, environment); +// match (var_info, rhe_info) { +// (Template(_), Template(assign)) => { +// *environment.get_mut_component_or_break(var, file!(), line!()) = assign; +// } +// (ArithmeticExpression(tag_0), ArithmeticExpression(tag_1)) +// if tag_0 == SignalElementType::Binary +// && tag_1 == SignalElementType::FieldElement +// && *op == AssignOp::AssignConstraintSignal => +// { +// add_report(ReportCode::WrongSignalTags, meta, file_id, reports) +// } +// _ => {} +// } +// } +// _ => {} +// } +// } -fn expression_inspection( - expr: &Expression, - template_info: &TemplateInfo, - reports: &mut ReportCollection, - environment: &Environment, -) -> ExpressionResult { - use Expression::*; - match expr { - InfixOp { .. } => ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement), - PrefixOp { rhe, prefix_op, .. } => { - let rhe_info = expression_inspection(rhe, template_info, reports, environment); - match prefix_op { - ExpressionPrefixOpcode::BoolNot => rhe_info, - _ => ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement), - } - } - ParallelOp{rhe, .. } => { - let rhe_info = expression_inspection(rhe, template_info, reports, environment); - match &rhe_info{ - ExpressionResult::Template(tag) => { - ExpressionResult::Template(tag.clone()) - }, - _ => ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement) - } - } - InlineSwitchOp { if_true, if_false, .. } => { - let if_true_info = expression_inspection(if_true, template_info, reports, environment); - let if_false_info = - expression_inspection(if_false, template_info, reports, environment); - match (&if_true_info, &if_false_info) { - ( - ExpressionResult::ArithmeticExpression(tag_0), - ExpressionResult::ArithmeticExpression(tag_1), - ) => { - if let SignalElementType::FieldElement = tag_0 { - if_true_info - } else if let SignalElementType::FieldElement = tag_1 { - if_false_info - } else { - if_true_info - } - } - _ => if_true_info, - } - } - Variable { name, access, .. } => { - variable_inspection(name, access, environment, template_info) - } - Number(..) => ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement), - Call { id, .. } => { - if template_info.contains_key(id) { - ExpressionResult::Template(Option::Some(id.clone())) - } else { - ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement) - } - } - ArrayInLine { .. } => { - ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement) - } - UniformArray { .. } => { - ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement) - } - } -} + +// fn expression_inspection( +// expr: &Expression, +// template_info: &TemplateInfo, +// reports: &mut ReportCollection, +// environment: &Environment, +// ) -> ExpressionResult { +// use Expression::*; +// match expr { +// InfixOp { .. } => ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement), +// PrefixOp { rhe, prefix_op, .. } => { +// let rhe_info = expression_inspection(rhe, template_info, reports, environment); +// match prefix_op { +// ExpressionPrefixOpcode::BoolNot => rhe_info, +// _ => ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement), +// } +// } +// InlineSwitchOp { if_true, if_false, .. } => { +// let if_true_info = expression_inspection(if_true, template_info, reports, environment); +// let if_false_info = +// expression_inspection(if_false, template_info, reports, environment); +// match (&if_true_info, &if_false_info) { +// ( +// ExpressionResult::ArithmeticExpression(tag_0), +// ExpressionResult::ArithmeticExpression(tag_1), +// ) => { +// if let SignalElementType::FieldElement = tag_0 { +// if_true_info +// } else if let SignalElementType::FieldElement = tag_1 { +// if_false_info +// } else { +// if_true_info +// } +// } +// _ => if_true_info, +// } +// } +// Variable { name, access, .. } => { +// variable_inspection(name, access, environment, template_info) +// } +// Number(..) => ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement), +// Call { id, .. } => { +// if template_info.contains_key(id) { +// ExpressionResult::Template(Option::Some(id.clone())) +// } else { +// ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement) +// } +// } +// ArrayInLine { .. } => { +// ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement) +// } +// UniformArray { .. } => { +// ExpressionResult::ArithmeticExpression(SignalElementType::FieldElement) +// } +// _ => {unreachable!("Anonymous calls should not be reachable at this point."); } +// } +// } -//************************************************* Statement support ************************************************* -fn treat_sequence_of_statements( - stmts: &[Statement], - file_id: FileID, - template_info: &TemplateInfo, - reports: &mut ReportCollection, - environment: &mut Environment, -) { - for stmt in stmts { - statement_inspection(stmt, file_id, template_info, reports, environment); - } -} -//************************************************* Expression support ************************************************* +// //************************************************* Statement support ************************************************* +// fn treat_sequence_of_statements( +// stmts: &[Statement], +// file_id: FileID, +// template_info: &TemplateInfo, +// reports: &mut ReportCollection, +// environment: &mut Environment, +// ) { +// for stmt in stmts { +// statement_inspection(stmt, file_id, template_info, reports, environment); +// } +// } +// //************************************************* Expression support ************************************************* -fn variable_inspection( - symbol: &str, - accesses: &[Access], - environment: &Environment, - template_info: &TemplateInfo, -) -> ExpressionResult { - use ExpressionResult::*; - let mut result = if environment.has_component(symbol) { - Template(environment.get_component_or_break(symbol, file!(), line!()).clone()) - } else if environment.has_signal(symbol) { - ArithmeticExpression(*environment.get_signal_or_break(symbol, file!(), line!())) - } else { - ArithmeticExpression(SignalElementType::FieldElement) - }; +// fn variable_inspection( +// symbol: &str, +// accesses: &[Access], +// environment: &Environment, +// template_info: &TemplateInfo, +// ) -> ExpressionResult { +// use ExpressionResult::*; +// let mut result = if environment.has_component(symbol) { +// Template(environment.get_component_or_break(symbol, file!(), line!()).clone()) +// } else if environment.has_signal(symbol) { +// ArithmeticExpression(*environment.get_signal_or_break(symbol, file!(), line!())) +// } else { +// ArithmeticExpression(SignalElementType::FieldElement) +// }; - for access in accesses { - if let Access::ComponentAccess(signal) = access { - let template = - environment.get_component_or_break(symbol, file!(), line!()).clone().unwrap(); - let input = template_info.get(&template).unwrap().get_input_info(signal); - let output = template_info.get(&template).unwrap().get_output_info(signal); - match (input, output) { - (Some((_, tag)), _) | (_, Some((_, tag))) => { - result = ArithmeticExpression(*tag); - } - _ => { - unreachable!() - } - } - } - } - result -} +// for access in accesses { +// if let Access::ComponentAccess(signal) = access { +// let template = +// environment.get_component_or_break(symbol, file!(), line!()).clone().unwrap(); +// let input = template_info.get(&template).unwrap().get_input_info(signal); +// let output = template_info.get(&template).unwrap().get_output_info(signal); +// match (input, output) { +// (Some((_, tag)), _) | (_, Some((_, tag))) => { +// result = ArithmeticExpression(*tag); +// } +// _ => { +// unreachable!() +// } +// } +// } +// } +// result +// } -//************************************************* Report support ************************************************* -fn add_report( - error_code: ReportCode, - meta: &Meta, - file_id: FileID, - reports: &mut ReportCollection, -) { - use ReportCode::*; - 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 { - WrongSignalTags => "Can not assign Field values to signals tagged as binary".to_string(), - _ => panic!("Unimplemented error code"), - }; - report.add_primary(location, file_id, message); - reports.push(report); -} +// //************************************************* Report support ************************************************* +// fn add_report( +// error_code: ReportCode, +// meta: &Meta, +// file_id: FileID, +// reports: &mut ReportCollection, +// ) { +// use ReportCode::*; +// 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 { +// WrongSignalTags => "Can not assign Field values to signals tagged as binary".to_string(), +// _ => panic!("Unimplemented error code"), +// }; +// report.add_primary(location, file_id, message); +// reports.push(report); +// } diff --git a/type_analysis/src/analyzers/type_check.rs b/type_analysis/src/analyzers/type_check.rs index a4e725450..aa5f5cc95 100644 --- a/type_analysis/src/analyzers/type_check.rs +++ b/type_analysis/src/analyzers/type_check.rs @@ -1,3 +1,4 @@ +use program_structure::ast::Expression::Call; use super::type_given_function::type_given_function; use super::type_register::TypeRegister; use program_structure::ast::*; @@ -10,7 +11,7 @@ use std::collections::HashSet; type ArithmeticType = usize; type ComponentInfo = (Option, ArithmeticType); -type TypingEnvironment = CircomEnvironment; +type TypingEnvironment = CircomEnvironment), ArithmeticType>; type CallRegister = TypeRegister; struct AnalysisInformation { @@ -85,6 +86,16 @@ pub fn type_check(program_archive: &ProgramArchive) -> Result Result bool { + 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; + } + } + tag_in_inputs + } + else { unreachable!()} +} + fn type_statement( statement: &Statement, program_archive: &ProgramArchive, @@ -116,13 +142,13 @@ fn type_statement( } } match xtype { - VariableType::Signal(s_type, _) => { + VariableType::Signal(s_type, tags) => { if let SignalType::Input = s_type { - analysis_information.environment.add_input(name, dimensions.len()); + analysis_information.environment.add_input(name, (dimensions.len(),tags.clone())); } else if let SignalType::Output = s_type { - analysis_information.environment.add_output(name, dimensions.len()); + analysis_information.environment.add_output(name, (dimensions.len(),tags.clone())); } else { - analysis_information.environment.add_intermediate(name, dimensions.len()); + analysis_information.environment.add_intermediate(name, (dimensions.len(),tags.clone())); } } VariableType::Var => { @@ -131,6 +157,9 @@ fn type_statement( VariableType::Component => analysis_information .environment .add_component(name, (meta.component_inference.clone(), dimensions.len())), + VariableType::AnonymousComponent => analysis_information + .environment + .add_component(name, (meta.component_inference.clone(), dimensions.len())), } } Substitution { var, access, op, rhe, meta, .. } => { @@ -142,12 +171,22 @@ fn type_statement( }; let access_information_result = - treat_access(access, meta, program_archive, analysis_information); + treat_access(var, access, meta, program_archive, analysis_information); + let access_information = if let Result::Ok(info) = access_information_result { info } else { return; }; + + if analysis_information.environment.has_component(var) && access_information.2.is_some(){ + return add_report( + ReportCode::OutputTagCannotBeModifiedOutside, + meta, + &mut analysis_information.reports, + ); + } + let symbol_type_result = apply_access_to_symbol( var, meta, @@ -167,6 +206,7 @@ fn type_statement( | (SymbolInformation::Signal(_), AssignOp::AssignSignal) | (SymbolInformation::Var(_), AssignOp::AssignVar) | (SymbolInformation::Component(_), AssignOp::AssignVar) => {} + | (SymbolInformation::Tag, AssignOp::AssignVar) => {} _ => { return add_report( ReportCode::WrongTypesInAssignOperation, @@ -197,16 +237,11 @@ fn type_statement( ) } } - SymbolInformation::Signal(dim) if dim > 0 => add_report( - ReportCode::MustBeSingleArithmetic, - rhe.get_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::Var(dim) + if dim == rhe_type.dim() && !rhe_type.is_template() => {} + SymbolInformation::Tag if !rhe_type.is_template() => {} _ => add_report( ReportCode::WrongTypesInAssignOperation, meta, @@ -227,16 +262,23 @@ fn type_statement( } else { return; }; - if lhe_type.is_template() || lhe_type.dim() > 0 { + if lhe_type.is_template() { add_report( - ReportCode::MustBeSingleArithmetic, + ReportCode::MustBeArithmetic, lhe.get_meta(), &mut analysis_information.reports, ); } - if rhe_type.is_template() || rhe_type.dim() > 0 { + if rhe_type.is_template() { add_report( - ReportCode::MustBeSingleArithmetic, + ReportCode::MustBeArithmetic, + rhe.get_meta(), + &mut analysis_information.reports, + ); + } + if rhe_type.dim() != lhe_type.dim() { + add_report( + ReportCode::MustBeSameDimension, rhe.get_meta(), &mut analysis_information.reports, ); @@ -336,6 +378,7 @@ fn type_statement( } analysis_information.environment.remove_variable_block(); } + MultSubstitution { .. } => unreachable!(), } } fn type_expression( @@ -484,7 +527,7 @@ fn type_expression( Variable { name, access, meta, .. } => { debug_assert!(analysis_information.environment.has_symbol(name)); let access_information = - treat_access(access, meta, program_archive, analysis_information)?; + treat_access(name, access, meta, program_archive, analysis_information)?; let environment = &analysis_information.environment; let reports = &mut analysis_information.reports; let symbol_information = apply_access_to_symbol( @@ -502,6 +545,9 @@ fn type_expression( SymbolInformation::Var(dim) | SymbolInformation::Signal(dim) => { Result::Ok(FoldedType::arithmetic_type(dim)) } + SymbolInformation::Tag => { + Result::Ok(FoldedType::arithmetic_type(0)) + } SymbolInformation::Component(possible_template) if possible_template.is_none() => { add_report_and_end(ReportCode::UninitializedSymbolInExpression, meta, reports) } @@ -564,6 +610,7 @@ fn type_expression( let folded_value = returned_type?; Result::Ok(folded_value) } + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } //************************************************* Statement support ************************************************* @@ -580,15 +627,16 @@ fn treat_sequence_of_statements( //************************************************* Expression support ************************************************* // 0: symbol dimensions accessed // 1: Signal accessed and dimensions accessed in that signal (optional) -type AccessInfo = (ArithmeticType, Option<(String, ArithmeticType)>); +type AccessInfo = (ArithmeticType, Option<(String, ArithmeticType)>, Option); fn treat_access( + var: &String, accesses: &[Access], meta: &Meta, program_archive: &ProgramArchive, analysis_information: &mut AnalysisInformation, ) -> Result { use Access::*; - let mut access_info: AccessInfo = (0, Option::None); + let mut access_info: AccessInfo = (0, Option::None, Option::None); let mut successful = Result::Ok(()); for access in accesses { match access { @@ -609,15 +657,46 @@ fn treat_access( } } } - ComponentAccess(signal_name) => { - if access_info.1.is_some() { - successful = add_report_and_end( - ReportCode::InvalidSignalAccess, - 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 { + add_report( + ReportCode::InvalidArraySize, + 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((signal_name.clone(), 0)); + access_info.1 = Option::Some((name.clone(), 0)); } } } @@ -630,6 +709,7 @@ enum SymbolInformation { Component(Option), Var(ArithmeticType), Signal(ArithmeticType), + Tag, } fn apply_access_to_symbol( symbol: &str, @@ -639,12 +719,15 @@ fn apply_access_to_symbol( reports: &mut ReportCollection, program_archive: &ProgramArchive, ) -> Result { - let (current_template, mut current_dim) = if environment.has_component(symbol) { - environment.get_component_or_break(symbol, file!(), line!()).clone() + let (current_template, mut current_dim, possible_tags) = if environment.has_component(symbol) { + let (temp, dim) = environment.get_component_or_break(symbol, file!(), line!()).clone(); + (temp,dim, Vec::new()) } else if environment.has_signal(symbol) { - (Option::None, *environment.get_signal_or_break(symbol, file!(), line!())) + let(dim, tags) = environment.get_signal_or_break(symbol, file!(), line!()); + (Option::None, *dim, tags.clone()) } else { - (Option::None, *environment.get_variable_or_break(symbol, file!(), line!())) + let dim = environment.get_variable_or_break(symbol, file!(), line!()); + (Option::None, *dim, Vec::new()) }; if access_information.0 > current_dim { @@ -653,8 +736,25 @@ fn apply_access_to_symbol( current_dim -= access_information.0 } - if access_information.1.is_some() && (current_dim > 0 || current_template.is_none()) { - add_report_and_end(ReportCode::InvalidSignalAccess, meta, reports) + 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) + } + else { + add_report_and_end(ReportCode::InvalidTagAccessAfterArray, 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); @@ -682,6 +782,13 @@ 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], program_archive: &ProgramArchive, @@ -801,6 +908,9 @@ fn add_report(error_code: ReportCode, meta: &Meta, reports: &mut ReportCollectio "Array access does not match the dimensions of the 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(), InvalidArrayType => "Components can not be declared inside inline arrays".to_string(), InfixOperatorWithWrongTypes | PrefixOperatorWithWrongTypes => { "Type not allowed by the operator".to_string() @@ -814,6 +924,10 @@ fn add_report(error_code: ReportCode, meta: &Meta, reports: &mut ReportCollectio 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(), + OutputTagCannotBeModifiedOutside => "Output tag from a subcomponent cannot be modified".to_string(), + MustBeSameDimension => "Must be two arrays of the same dimensions".to_string(), + MainComponentWithTags => "Main component cannot have inputs with tags".to_string(), ExpectedDimDiffGotDim(expected, got) => { format!("Function should return {} but returns {}", expected, got) } diff --git a/type_analysis/src/analyzers/type_given_function.rs b/type_analysis/src/analyzers/type_given_function.rs index 1cda4c5c7..427d11bcd 100644 --- a/type_analysis/src/analyzers/type_given_function.rs +++ b/type_analysis/src/analyzers/type_given_function.rs @@ -317,5 +317,6 @@ fn look_for_type_in_expression( let has_type = start(id, explored_functions, function_info, ¶ms_types); has_type } + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } diff --git a/type_analysis/src/analyzers/unknown_known_analysis.rs b/type_analysis/src/analyzers/unknown_known_analysis.rs index 56a0e48b0..7806c5174 100644 --- a/type_analysis/src/analyzers/unknown_known_analysis.rs +++ b/type_analysis/src/analyzers/unknown_known_analysis.rs @@ -16,6 +16,7 @@ struct ExitInformation { reports: ReportCollection, environment: Environment, constraints_declared: bool, + tags_modified: bool, modified_variables: HashSet, } @@ -58,47 +59,56 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma mut reports: ReportCollection, mut environment: Environment, file_id: FileID, - ) -> (bool, ReportCollection, Environment, HashSet) { + ) -> (bool, bool, ReportCollection, Environment, HashSet) { let mut constraints_declared = false; + let mut tags_modified = 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; modified_variables.extend(exit.modified_variables); for report in exit.reports { reports.push(report); } environment = exit.environment; } - (constraints_declared, reports, environment, modified_variables) + (constraints_declared, tags_modified, reports, environment, modified_variables) } let file_id = entry_information.file_id; let mut reports = ReportCollection::new(); let mut environment = entry_information.environment; let mut modified_variables = HashSet::new(); let mut constraints_declared = false; + let mut tags_modified = false; match stmt { Declaration { xtype, name, dimensions, .. } => { if let VariableType::Signal(..) = xtype { environment.add_intermediate(name, Unknown); } else if let VariableType::Component = xtype { environment.add_component(name, Unknown); + } else if let VariableType::AnonymousComponent = xtype { + environment.add_component(name, Unknown); } else { environment.add_variable(name, Unknown); modified_variables.insert(name.clone()); } - - for dimension in dimensions { - if tag(dimension, &environment) == Unknown { - add_report( - ReportCode::UnknownDimension, - dimension.get_meta(), - file_id, - &mut reports, - ); + if let VariableType::AnonymousComponent = xtype { + // in this case the dimension is ukn + } else{ + for dimension in dimensions { + if tag(dimension, &environment) == Unknown { + add_report( + ReportCode::UnknownDimension, + dimension.get_meta(), + file_id, + &mut reports, + ); + } } } + } Substitution { meta, var, access, op, rhe, .. } => { let simplified_elem = simplify_symbol(&environment, var, access); @@ -124,6 +134,14 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma if access_tag == Unknown { add_report(ReportCode::UnknownTemplate, meta, file_id, &mut reports); } + } else if simplified_elem == SignalTag { + tags_modified = true; + if expression_tag == Unknown { + add_report(ReportCode::UnknownTemplate, rhe.get_meta(), file_id, &mut reports); + } + if access_tag == Unknown { + add_report(ReportCode::UnknownTemplate, meta, file_id, &mut reports); + } } else if *op == AssignOp::AssignConstraintSignal { constraints_declared = true; if is_non_quadratic(rhe, &environment) { @@ -157,10 +175,12 @@ 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 } }; constraints_declared = else_case_info.constraints_declared || if_case_info.constraints_declared; + tags_modified = else_case_info.tags_modified || if_case_info.tags_modified; modified_variables.extend(if_case_info.modified_variables); modified_variables.extend(else_case_info.modified_variables); for report in if_case_info.reports { @@ -190,6 +210,14 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma &mut reports, ); } + if tag_cond == Unknown && tags_modified { + add_report( + ReportCode::UnreachableTags, + cond.get_meta(), + file_id, + &mut reports, + ); + } } While { cond, stmt, .. } => { let mut entry_info = environment.clone(); @@ -206,6 +234,7 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma }; constraints_declared = exit.constraints_declared; + tags_modified = exit.tags_modified; for report in exit.reports { reports.push(report); } @@ -228,26 +257,36 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma &mut reports, ); } + if tag_out == Unknown && tags_modified { + add_report( + ReportCode::UnreachableTags, + cond.get_meta(), + file_id, + &mut reports, + ); + } } Block { stmts, .. } => { environment.add_variable_block(); - let (nc, nr, ne, nm) = iterate_statements(stmts, reports, environment, file_id); + let (nc, tags, 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; } InitializationBlock { initializations, .. } => { - let (nc, nr, ne, nm) = iterate_statements(initializations, reports, environment, file_id); + let (nc, tags, nr, ne, nm) = iterate_statements(initializations, reports, environment, file_id); constraints_declared = nc; reports = nr; environment = ne; modified_variables = nm; + tags_modified = tags; } _ => {} } - ExitInformation { reports, environment, constraints_declared, modified_variables } + ExitInformation { reports, environment, constraints_declared, modified_variables, tags_modified } } fn tag(expression: &Expression, environment: &Environment) -> Tag { @@ -261,7 +300,11 @@ fn tag(expression: &Expression, environment: &Environment) -> Tag { } else if environment.has_component(name) { *environment.get_component_or_break(name, file!(), line!()) } else { + if environment.has_intermediate(name) && !all_array_are_accesses(access) { + Known /* In this case, it is a tag. */ + } else{ *environment.get_intermediate_or_break(name, file!(), line!()) + } }; let mut index = 0; loop { @@ -273,7 +316,7 @@ fn tag(expression: &Expression, environment: &Environment) -> Tag { } if let Access::ArrayAccess(exp) = &access[index] { symbol_tag = tag(exp, environment); - } else { + } else if !environment.has_intermediate(name) { symbol_tag = Unknown; } index += 1; @@ -300,6 +343,7 @@ fn tag(expression: &Expression, environment: &Environment) -> Tag { } PrefixOp { rhe, .. } => tag(rhe, environment), ParallelOp { rhe, .. } => tag(rhe, environment), + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } // ***************************** Compare two variable states ******************** @@ -325,6 +369,19 @@ fn check_modified( modified } +fn all_array_are_accesses(accesses: &[Access]) -> bool { + let mut i = 0; + let mut all_array_accesses = true; + while i < accesses.len() && all_array_accesses { + let aux = accesses.get(i).unwrap(); + if let Access::ComponentAccess(_) = aux { + all_array_accesses = false; + } + i = i + 1; + } + all_array_accesses +} + // ****************************** Expression utils ****************************** fn expression_iterator( values: &[Expression], @@ -351,13 +408,20 @@ enum Symbol { Signal, Component, Variable, + SignalTag } fn simplify_symbol(environment: &Environment, name: &str, access: &[Access]) -> Symbol { use Symbol::*; if environment.has_variable(name) { Variable } else if environment.has_signal(name) { - Signal + let mut symbol = Signal; + for acc in access { + if let Access::ComponentAccess(_) = acc { + symbol = SignalTag; + } + } + symbol } else { let mut symbol = Component; for acc in access { @@ -407,6 +471,7 @@ fn unknown_index(exp: &Expression, environment: &Environment) -> bool { (false, bucket) } UniformArray{ value, dimension, .. } => (false, vec![value.as_ref(), dimension.as_ref()]), + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } }; let mut has_unknown_index = init; let mut index = 0; @@ -434,6 +499,7 @@ fn add_report( UnknownTemplate => "Every component instantiation must be resolved 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(), _ => panic!("Unimplemented error code") }; report.add_primary(location, file_id, message); diff --git a/type_analysis/src/check_types.rs b/type_analysis/src/check_types.rs index a300352f3..5cc3c5682 100644 --- a/type_analysis/src/check_types.rs +++ b/type_analysis/src/check_types.rs @@ -132,10 +132,6 @@ fn semantic_analyses( unknown_known_analysis(template_name, program_archive) { errors.append(&mut unknown_known_report); } - if let Result::Err(mut tag_analysis_reports) = - tag_analysis(template_name, program_archive) { - errors.append(&mut tag_analysis_reports); - } if program_archive.get_template_data(template_name).is_custom_gate() { let body = program_archive.get_template_data(template_name).get_body(); match custom_gate_analysis(template_name, body) { diff --git a/type_analysis/src/decorators/component_type_inference.rs b/type_analysis/src/decorators/component_type_inference.rs index e021953fc..253d0ef00 100644 --- a/type_analysis/src/decorators/component_type_inference.rs +++ b/type_analysis/src/decorators/component_type_inference.rs @@ -46,7 +46,8 @@ fn infer_component_types(stmt: &Statement, templates: &TemplateInfo, data: &mut infer_component_types(s, templates, data); } } - Declaration { xtype, name, .. } if VariableType::Component == *xtype => { + Declaration { xtype, name, .. } + if VariableType::Component == *xtype || VariableType::AnonymousComponent == *xtype =>{ data.components.insert(name.clone()); } Substitution { var, rhe, .. } if data.components.contains(var) => { @@ -98,8 +99,9 @@ fn apply_inference(stmt: &mut Statement, env: &mut Environment) { apply_inference(s, env); } } - Declaration { xtype, name, meta, .. } if VariableType::Component == *xtype => { - meta.component_inference = env.remove(name); + Declaration { xtype, name, meta, .. } + if VariableType::Component == *xtype || VariableType::AnonymousComponent == *xtype => { + meta.component_inference = env.remove(name); } _ => {} } diff --git a/type_analysis/src/decorators/constants_handler.rs b/type_analysis/src/decorators/constants_handler.rs index ded11b2e8..11782ddc9 100644 --- a/type_analysis/src/decorators/constants_handler.rs +++ b/type_analysis/src/decorators/constants_handler.rs @@ -143,6 +143,7 @@ fn statement_invariant_check(stmt: &Statement, environment: &mut Constants) -> R } While { stmt, .. } => while_invariant_check(stmt, environment), Block { stmts, .. } => block_invariant_check(stmts, environment), + MultSubstitution { .. } => unreachable!(), _ => ReportCollection::new(), } } @@ -223,6 +224,7 @@ fn has_constant_value(expr: &Expression, environment: &Constants) -> bool { Variable { name, .. } => variable(name, environment), ArrayInLine { .. } => array_inline(), UniformArray { .. } => uniform_array(), + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } @@ -289,6 +291,7 @@ fn expand_statement(stmt: &mut Statement, environment: &mut ExpressionHolder) { LogCall { args, .. } => expand_log_call(args, environment), Assert { arg, .. } => expand_assert(arg, environment), Block { stmts, .. } => expand_block(stmts, environment), + MultSubstitution { .. } => unreachable!() } } @@ -404,6 +407,7 @@ fn expand_expression(expr: Expression, environment: &ExpressionHolder) -> Expres expand_inline_switch_op(meta, *cond, *if_true, *if_false, environment) } Variable { meta, name, access } => expand_variable(meta, name, access, environment), + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } diff --git a/type_analysis/src/decorators/type_reduction.rs b/type_analysis/src/decorators/type_reduction.rs index aa222632e..bade4526a 100644 --- a/type_analysis/src/decorators/type_reduction.rs +++ b/type_analysis/src/decorators/type_reduction.rs @@ -29,7 +29,7 @@ fn reduce_types_in_statement(stmt: &mut Statement, environment: &mut Environment reduce_types_in_substitution(var, access, environment, rhe, meta) } Declaration { name, xtype, dimensions, .. } => { - reduce_types_in_declaration(*xtype, name, dimensions, environment) + reduce_types_in_declaration(xtype, name, dimensions, environment) } While { cond, stmt, .. } => reduce_types_in_while(cond, stmt, environment), Block { stmts, .. } => reduce_types_in_vec_of_statements(stmts, environment), @@ -48,6 +48,7 @@ fn reduce_types_in_statement(stmt: &mut Statement, environment: &mut Environment ConstraintEquality { lhe, rhe, .. } => { reduce_types_in_constraint_equality(lhe, rhe, environment) } + MultSubstitution { .. } => unreachable!() } } @@ -78,6 +79,7 @@ fn reduce_types_in_expression(expression: &mut Expression, environment: &Environ reduce_types_in_expression(dimension, environment); } Number(..) => {} + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } @@ -91,15 +93,15 @@ fn reduce_types_in_constraint_equality( } fn reduce_types_in_declaration( - xtype: VariableType, + xtype: &VariableType, name: &str, dimensions: &mut [Expression], environment: &mut Environment, ) { use VariableType::*; - if xtype == Var { + if *xtype == Var { environment.add_variable(name, ()); - } else if xtype == Component { + } else if *xtype == Component || *xtype == AnonymousComponent { environment.add_component(name, ()); } else { environment.add_intermediate(name, ()); @@ -165,6 +167,8 @@ fn reduce_types_in_variable( for acc in access { if let ArrayAccess(exp) = acc { reduce_types_in_expression(exp, environment) + } else if reduction == Signal{ + reduction = Tag; } else { reduction = Signal; }