From 72c3661c86712b99236eafaac99f76f13d42b9d9 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 2 Oct 2023 17:20:29 +0100 Subject: [PATCH] feat: Dynamic indexing of non-homogenous slices (#2883) --- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 2 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 227 +++++++++++++----- .../src/ssa/function_builder/mod.rs | 3 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 16 +- .../noirc_evaluator/src/ssa/ir/printer.rs | 11 +- .../src/ssa/opt/flatten_cfg.rs | 34 ++- .../src/ssa/ssa_gen/context.rs | 14 +- .../nested_slice_dynamic/Nargo.toml | 7 + .../nested_slice_dynamic/Prover.toml | 1 + .../nested_slice_dynamic/src/main.nr | 49 ++++ 10 files changed, 273 insertions(+), 91 deletions(-) create mode 100644 tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Nargo.toml create mode 100644 tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Prover.toml create mode 100644 tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 0567118b419..8313aa3b9fe 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -913,7 +913,7 @@ impl AcirContext { self.brillig_array_input(var_expressions, var)?; } } - AcirValue::DynamicArray(AcirDynamicArray { block_id, len }) => { + AcirValue::DynamicArray(AcirDynamicArray { block_id, len, .. }) => { for i in 0..len { // We generate witnesses corresponding to the array values let index_var = self.add_constant(FieldElement::from(i as u128)); diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index d16985eb31c..a7a4e03af93 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -83,10 +83,17 @@ pub(crate) struct AcirDynamicArray { block_id: BlockId, /// Length of the array len: usize, + /// Identification for the ACIR dynamic array + /// inner element type sizes array + element_type_sizes: BlockId, } impl Debug for AcirDynamicArray { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "id: {}, len: {}", self.block_id.0, self.len) + write!( + f, + "id: {}, len: {}, element_type_sizes: {:?}", + self.block_id.0, self.len, self.element_type_sizes.0 + ) } } @@ -249,12 +256,17 @@ impl Context { let value = self.convert_ssa_block_param(&typ)?; match &value { AcirValue::Var(_, _) => (), - AcirValue::Array(values) => { + AcirValue::Array(_) => { let block_id = self.block_id(param_id); let len = if matches!(typ, Type::Array(_, _)) { typ.flattened_size() } else { - values.len() + return Err(InternalError::UnExpected { + expected: "Block params should be an array".to_owned(), + found: format!("Instead got {:?}", typ), + call_stack: self.acir_context.get_call_stack(), + } + .into()); }; self.initialize_array(block_id, len, Some(value.clone()))?; } @@ -384,6 +396,7 @@ impl Context { AcirValue::DynamicArray(AcirDynamicArray { block_id: lhs_block_id, len, + .. }), AcirValue::DynamicArray(AcirDynamicArray { block_id: rhs_block_id, @@ -461,14 +474,13 @@ impl Context { match &output { // We need to make sure we initialize arrays returned from intrinsic calls // or else they will fail if accessed with a dynamic index - AcirValue::Array(values) => { + AcirValue::Array(_) => { let block_id = self.block_id(result); - let values = vecmap(values, |v| v.clone()); let array_typ = dfg.type_of_value(*result); let len = if matches!(array_typ, Type::Array(_, _)) { array_typ.flattened_size() } else { - values.len() + Self::flattened_value_size(&output) }; self.initialize_array(block_id, len, Some(output.clone()))?; } @@ -673,11 +685,13 @@ impl Context { /// We need to properly setup the inputs for array operations in ACIR. /// From the original SSA values we compute the following AcirVars: - /// - index_var is the index of the array + /// - new_index is the index of the array. ACIR memory operations work with a flat memory, so we fully flattened the specified index + /// in case we have a nested array. The index for SSA array operations only represents the flattened index of the current array. + /// Thus internal array element type sizes need to be computed to accurately transform the index. /// - predicate_index is 0, or the index if the predicate is true /// - new_value is the optional value when the operation is an array_set - /// When there is a predicate, it is predicate*value + (1-predicate)*dummy, where dummy is the value of the array at the requested index. - /// It is a dummy value because in the case of a false predicate, the value stored at the requested index will be itself. + /// When there is a predicate, it is predicate*value + (1-predicate)*dummy, where dummy is the value of the array at the requested index. + /// It is a dummy value because in the case of a false predicate, the value stored at the requested index will be itself. fn convert_array_operation_inputs( &mut self, array: ValueId, @@ -688,13 +702,7 @@ impl Context { let (array_id, array_typ, block_id) = self.check_array_is_initialized(array, dfg)?; let index_var = self.convert_numeric_value(index, dfg)?; - // TODO(#2752): Need to add support for dynamic indices with non-homogenous slices - let index_var = if matches!(array_typ, Type::Array(_, _)) { - let array_len = dfg.try_get_array_length(array_id).expect("ICE: expected an array"); - self.get_flattened_index(&array_typ, array_id, array_len, index_var)? - } else { - index_var - }; + let index_var = self.get_flattened_index(&array_typ, array_id, index_var, dfg)?; let predicate_index = self.acir_context.mul_var(index_var, self.current_side_effects_enabled_var)?; @@ -779,7 +787,6 @@ impl Context { let results = dfg.instruction_results(instruction); let res_typ = dfg.type_of_value(results[0]); - let value = self.array_get_value(&res_typ, block_id, &mut var_index)?; self.define_result(dfg, instruction, value.clone()); @@ -816,6 +823,7 @@ impl Context { } Type::Slice(_) => { // TODO(#2752): need SSA values here to fetch the len like we do for a Type::Array + // Update this to enable fetching slices from nested arrays Err(InternalError::UnExpected { expected: "array".to_owned(), found: ssa_type.to_string(), @@ -839,8 +847,8 @@ impl Context { map_array: bool, ) -> Result<(), RuntimeError> { // Pass the instruction between array methods rather than the internal fields themselves - let (array, length) = match dfg[instruction] { - Instruction::ArraySet { array, length, .. } => (array, length), + let array = match dfg[instruction] { + Instruction::ArraySet { array, .. } => array, _ => { return Err(InternalError::UnExpected { expected: "Instruction should be an ArraySet".to_owned(), @@ -851,7 +859,7 @@ impl Context { } }; - let (_, array_typ, block_id) = self.check_array_is_initialized(array, dfg)?; + let (array_id, array_typ, block_id) = self.check_array_is_initialized(array, dfg)?; // Every array has a length in its type, so we fetch that from // the SSA IR. @@ -865,16 +873,7 @@ impl Context { // Flatten the array length to handle arrays of complex types array_typ.flattened_size() } - Type::Slice(_) => { - // Fetch the true length of the slice from the array_set instruction - let length = length - .expect("ICE: array set on slice must have a length associated with the call"); - let length_acir_var = self.convert_value(length, dfg).into_var()?; - let len = self.acir_context.var_to_expression(length_acir_var)?.to_const(); - let len = len - .expect("ICE: slice length should be fully tracked and constant by ACIR gen"); - len.to_u128() as usize - } + Type::Slice(_) => self.flattened_slice_size(array_id, dfg), _ => unreachable!("ICE - expected an array"), }; @@ -892,23 +891,17 @@ impl Context { } else { // Initialize the new array with the values from the old array result_block_id = self.block_id(result_id); - let init_values = try_vecmap(0..array_len, |i| { - let index_var = self.acir_context.add_constant(FieldElement::from(i as u128)); - - let read = self.acir_context.read_from_memory(block_id, &index_var)?; - Ok::(AcirValue::Var(read, AcirType::field())) - })?; - self.initialize_array( - result_block_id, - array_len, - Some(AcirValue::Array(init_values.into())), - )?; + self.copy_dynamic_array(block_id, result_block_id, array_len)?; } self.array_set_value(store_value, result_block_id, &mut var_index)?; - let result_value = - AcirValue::DynamicArray(AcirDynamicArray { block_id: result_block_id, len: array_len }); + let arr_element_type_sizes = self.internal_block_id(&array_id); + let result_value = AcirValue::DynamicArray(AcirDynamicArray { + block_id: result_block_id, + len: array_len, + element_type_sizes: arr_element_type_sizes, + }); self.define_result(dfg, instruction, result_value); Ok(()) } @@ -958,18 +951,18 @@ impl Context { if !already_initialized { let value = &dfg[array_id]; match value { - Value::Array { array, .. } => { + Value::Array { .. } => { let value = self.convert_value(array_id, dfg); let len = if matches!(array_typ, Type::Array(_, _)) { array_typ.flattened_size() } else { - array.len() + self.flattened_slice_size(array_id, dfg) }; self.initialize_array(block_id, len, Some(value))?; } _ => { return Err(InternalError::General { - message: format!("Array {array} should be initialized"), + message: format!("Array {array_id} should be initialized"), call_stack: self.acir_context.get_call_stack(), } .into()) @@ -980,16 +973,18 @@ impl Context { Ok((array_id, array_typ, block_id)) } - fn init_element_types_size_array( + fn init_element_type_sizes_array( &mut self, array_typ: &Type, array_id: ValueId, + dfg: &DataFlowGraph, ) -> Result { let element_type_sizes = self.internal_block_id(&array_id); // Check whether an internal type sizes array has already been initialized if self.initialized_arrays.contains(&element_type_sizes) { return Ok(element_type_sizes); } + let mut flat_elem_type_sizes = Vec::new(); flat_elem_type_sizes.push(0); match array_typ { @@ -998,17 +993,78 @@ impl Context { flat_elem_type_sizes.push(typ.flattened_size() + flat_elem_type_sizes[i]); } } + Type::Slice(element_types) => { + match &dfg[array_id] { + Value::Array { array, .. } => { + for i in 0..element_types.len() { + flat_elem_type_sizes.push( + self.flattened_slice_size(array[i], dfg) + flat_elem_type_sizes[i], + ); + } + } + Value::Instruction { .. } => { + // An instruction representing the slice means it has been processed previously during ACIR gen. + // Use the previously defined result of an array operation to fetch the internal type information. + let array_acir_value = self.convert_value(array_id, dfg); + match array_acir_value { + AcirValue::DynamicArray(AcirDynamicArray { + element_type_sizes: inner_elem_type_sizes, + .. + }) => { + if self.initialized_arrays.contains(&inner_elem_type_sizes) { + self.copy_dynamic_array( + inner_elem_type_sizes, + element_type_sizes, + element_types.len() + 1, + )?; + return Ok(element_type_sizes); + } else { + return Err(InternalError::General { + message: format!("Array {array_id}'s inner element type sizes array should be initialized"), + call_stack: self.acir_context.get_call_stack(), + } + .into()); + } + } + AcirValue::Array(values) => { + for i in 0..element_types.len() { + flat_elem_type_sizes.push( + Self::flattened_value_size(&values[i]) + + flat_elem_type_sizes[i], + ); + } + } + _ => { + return Err(InternalError::UnExpected { + expected: "AcirValue::DynamicArray or AcirValue::Array" + .to_owned(), + found: format!("{:?}", array_acir_value), + call_stack: self.acir_context.get_call_stack(), + } + .into()) + } + } + } + _ => { + return Err(InternalError::UnExpected { + expected: "array or instruction".to_owned(), + found: format!("{:?}", &dfg[array_id]), + call_stack: self.acir_context.get_call_stack(), + } + .into()) + } + }; + } _ => { return Err(InternalError::UnExpected { - expected: "array".to_owned(), + expected: "array or slice".to_owned(), found: array_typ.to_string(), call_stack: self.acir_context.get_call_stack(), } .into()) } } - // We do not have to initialize the last elem size value as that is the maximum array size - flat_elem_type_sizes.pop(); + // The final array should will the flattened index at each outer array index let init_values = vecmap(flat_elem_type_sizes, |type_size| { let var = self.acir_context.add_constant(FieldElement::from(type_size as u128)); AcirValue::Var(var, AcirType::field()) @@ -1022,20 +1078,32 @@ impl Context { Ok(element_type_sizes) } + fn copy_dynamic_array( + &mut self, + source: BlockId, + destination: BlockId, + array_len: usize, + ) -> Result<(), RuntimeError> { + let init_values = try_vecmap(0..array_len, |i| { + let index_var = self.acir_context.add_constant(FieldElement::from(i as u128)); + + let read = self.acir_context.read_from_memory(source, &index_var)?; + Ok::(AcirValue::Var(read, AcirType::field())) + })?; + self.initialize_array(destination, array_len, Some(AcirValue::Array(init_values.into())))?; + Ok(()) + } + fn get_flattened_index( &mut self, array_typ: &Type, array_id: ValueId, - array_len: usize, var_index: AcirVar, + dfg: &DataFlowGraph, ) -> Result { - let element_type_sizes = self.init_element_types_size_array(array_typ, array_id)?; + let element_type_sizes = self.init_element_type_sizes_array(array_typ, array_id, dfg)?; let element_size = array_typ.element_size(); - let flat_array_size = array_typ.flattened_size(); - let flat_elem_size = flat_array_size / array_len; - let flat_element_size_var = - self.acir_context.add_constant(FieldElement::from(flat_elem_size as u128)); let element_size_var = self.acir_context.add_constant(FieldElement::from(element_size as u128)); @@ -1054,10 +1122,54 @@ impl Context { let inner_offset = self.acir_context.read_from_memory(element_type_sizes, &inner_offset_index)?; + let flat_element_size_var = + self.acir_context.read_from_memory(element_type_sizes, &element_size_var)?; let var_index = self.acir_context.mul_var(outer_offset, flat_element_size_var)?; self.acir_context.add_var(var_index, inner_offset) } + fn flattened_slice_size(&mut self, array_id: ValueId, dfg: &DataFlowGraph) -> usize { + let mut size = 0; + match &dfg[array_id] { + Value::Array { array, .. } => { + // The array is going to be the flattened outer array + // Flattened slice size from SSA value does not need to be multiplied by the len + for value in array { + size += self.flattened_slice_size(*value, dfg); + } + } + Value::NumericConstant { .. } => { + size += 1; + } + Value::Instruction { .. } => { + let array_acir_value = self.convert_value(array_id, dfg); + size += Self::flattened_value_size(&array_acir_value); + } + _ => { + unreachable!("ICE: Unexpected SSA value when computing the slice size"); + } + } + size + } + + fn flattened_value_size(value: &AcirValue) -> usize { + let mut size = 0; + match value { + AcirValue::DynamicArray(AcirDynamicArray { len, .. }) => { + size += len; + } + AcirValue::Var(_, _) => { + size += 1; + } + AcirValue::Array(values) => { + for value in values { + size += Self::flattened_value_size(value); + } + } + } + size + } + /// Initializes an array with the given values and caches the fact that we /// have initialized this array. fn initialize_array( @@ -1597,7 +1709,7 @@ impl Context { self.slice_intrinsic_input(old_slice, var)?; } } - AcirValue::DynamicArray(AcirDynamicArray { block_id, len }) => { + AcirValue::DynamicArray(AcirDynamicArray { block_id, len, .. }) => { for i in 0..len { // We generate witnesses corresponding to the array values let index_var = self.acir_context.add_constant(FieldElement::from(i as u128)); @@ -1733,3 +1845,4 @@ mod tests { assert_eq!(acir.return_witnesses, vec![Witness(1)]); } } +// diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 3489cd271ed..6ccab6638fd 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -275,9 +275,8 @@ impl FunctionBuilder { array: ValueId, index: ValueId, value: ValueId, - length: Option, ) -> ValueId { - self.insert_instruction(Instruction::ArraySet { array, index, value, length }, None).first() + self.insert_instruction(Instruction::ArraySet { array, index, value }, None).first() } /// Terminates the current block with the given terminator instruction diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index c911c3f1ed7..3a12d508f95 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -178,9 +178,7 @@ pub(crate) enum Instruction { /// Creates a new array with the new value at the given index. All other elements are identical /// to those in the given array. This will not modify the original array. - /// - /// An optional length can be provided to enable handling of dynamic slice indices. - ArraySet { array: ValueId, index: ValueId, value: ValueId, length: Option }, + ArraySet { array: ValueId, index: ValueId, value: ValueId }, } impl Instruction { @@ -306,12 +304,9 @@ impl Instruction { Instruction::ArrayGet { array, index } => { Instruction::ArrayGet { array: f(*array), index: f(*index) } } - Instruction::ArraySet { array, index, value, length } => Instruction::ArraySet { - array: f(*array), - index: f(*index), - value: f(*value), - length: length.map(f), - }, + Instruction::ArraySet { array, index, value } => { + Instruction::ArraySet { array: f(*array), index: f(*index), value: f(*value) } + } } } @@ -348,11 +343,10 @@ impl Instruction { f(*array); f(*index); } - Instruction::ArraySet { array, index, value, length } => { + Instruction::ArraySet { array, index, value } => { f(*array); f(*index); f(*value); - length.map(&mut f); } Instruction::EnableSideEffects { condition } => { f(*condition); diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 537b474a8d9..037f8aaac2b 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -163,19 +163,14 @@ pub(crate) fn display_instruction( Instruction::ArrayGet { array, index } => { writeln!(f, "array_get {}, index {}", show(*array), show(*index)) } - Instruction::ArraySet { array, index, value, length } => { - write!( + Instruction::ArraySet { array, index, value } => { + writeln!( f, "array_set {}, index {}, value {}", show(*array), show(*index), show(*value) - )?; - if let Some(length) = length { - writeln!(f, ", length {}", show(*length)) - } else { - writeln!(f) - } + ) } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index c05c4a02181..a786f6c3080 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -461,8 +461,7 @@ impl<'f> Context<'f> { // The smaller slice is filled with placeholder data. Codegen for slice accesses must // include checks against the dynamic slice length so that this placeholder data is not incorrectly accessed. if len <= index_value.to_u128() as usize { - let zero = FieldElement::zero(); - self.inserter.function.dfg.make_constant(zero, Type::field()) + self.make_slice_dummy_data(element_type) } else { let get = Instruction::ArrayGet { array, index }; self.insert_instruction_with_typevars(get, typevars).first() @@ -484,6 +483,37 @@ impl<'f> Context<'f> { self.inserter.function.dfg.make_array(merged, typ) } + /// Construct a dummy value to be attached to the smaller of two slices being merged. + /// We need to make sure we follow the internal element type structure of the slice type + /// even for dummy data to ensure that we do not have errors later in the compiler, + /// such as with dynamic indexing of non-homogenous slices. + fn make_slice_dummy_data(&mut self, typ: &Type) -> ValueId { + match typ { + Type::Numeric(_) => { + let zero = FieldElement::zero(); + self.inserter.function.dfg.make_constant(zero, Type::field()) + } + Type::Array(element_types, len) => { + let mut array = im::Vector::new(); + for _ in 0..*len { + for typ in element_types.iter() { + array.push_back(self.make_slice_dummy_data(typ)); + } + } + self.inserter.function.dfg.make_array(array, typ.clone()) + } + Type::Slice(_) => { + unreachable!("ICE: Slices of slice is unsupported") + } + Type::Reference => { + unreachable!("ICE: Merging references is unsupported") + } + Type::Function => { + unreachable!("ICE: Merging functions is unsupported") + } + } + } + fn get_slice_length(&mut self, value_id: ValueId) -> usize { let value = &self.inserter.function.dfg[value_id]; match value { diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index bba7f40d721..f7e2d7df935 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -661,19 +661,14 @@ impl<'a> FunctionContext<'a> { match lvalue { LValue::Ident => unreachable!("Cannot assign to a variable without a reference"), LValue::Index { old_array: mut array, index, array_lvalue, location } => { - array = self.assign_lvalue_index(new_value, array, index, None, location); + array = self.assign_lvalue_index(new_value, array, index, location); self.assign_new_value(*array_lvalue, array.into()); } LValue::SliceIndex { old_slice: slice, index, slice_lvalue, location } => { let mut slice_values = slice.into_value_list(self); - slice_values[1] = self.assign_lvalue_index( - new_value, - slice_values[1], - index, - Some(slice_values[0]), - location, - ); + slice_values[1] = + self.assign_lvalue_index(new_value, slice_values[1], index, location); // The size of the slice does not change in a slice index assignment so we can reuse the same length value let new_slice = Tree::Branch(vec![slice_values[0].into(), slice_values[1].into()]); @@ -694,7 +689,6 @@ impl<'a> FunctionContext<'a> { new_value: Values, mut array: ValueId, index: ValueId, - length: Option, location: Location, ) -> ValueId { let element_size = self.builder.field_constant(self.element_size(array)); @@ -706,7 +700,7 @@ impl<'a> FunctionContext<'a> { new_value.for_each(|value| { let value = value.eval(self); - array = self.builder.insert_array_set(array, index, value, length); + array = self.builder.insert_array_set(array, index, value); index = self.builder.insert_binary(index, BinaryOp::Add, one); }); array diff --git a/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Nargo.toml b/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Nargo.toml new file mode 100644 index 00000000000..00dfbffbe45 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "nested_slice_dynamic" +type = "bin" +authors = [""] +compiler_version = "0.13.0" + +[dependencies] \ No newline at end of file diff --git a/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Prover.toml b/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Prover.toml new file mode 100644 index 00000000000..7127baac5bf --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Prover.toml @@ -0,0 +1 @@ +y = "3" diff --git a/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/src/main.nr b/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/src/main.nr new file mode 100644 index 00000000000..d4740902a52 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/src/main.nr @@ -0,0 +1,49 @@ +struct Bar { + inner: [Field; 3], +} + +struct Foo { + a: Field, + b: [Field; 3], + bar: Bar, +} + +fn main(y : Field) { + let foo_one = Foo { a: 1, b: [2, 3, 20], bar: Bar { inner: [100, 101, 102] } }; + let foo_two = Foo { a: 4, b: [5, 6, 21], bar: Bar { inner: [103, 104, 105] } }; + let foo_three = Foo { a: 7, b: [8, 9, 22], bar: Bar { inner: [106, 107, 108] } }; + let foo_four = Foo { a: 10, b: [11, 12, 23], bar: Bar { inner: [109, 110, 111] } }; + let mut x = [foo_one]; + x = x.push_back(foo_two); + x = x.push_back(foo_three); + x = x.push_back(foo_four); + + assert(x[y - 3].a == 1); + assert(x[y - 3].b == [2, 3, 20]); + assert(x[y - 2].a == 4); + assert(x[y - 2].b == [5, 6, 21]); + assert(x[y - 1].a == 7); + assert(x[y - 1].b == [8, 9, 22]); + assert(x[y].a == 10); + assert(x[y].b == [11, 12, 23]); + assert(x[y].bar.inner == [109, 110, 111]); + + if y != 2 { + x[y - 2].a = 50; + } else { + x[y - 2].a = 100; + } + assert(x[y - 2].a == 50); + + if y == 2 { + x[y - 1].b = [50, 51, 52]; + } else { + x[y - 1].b = [100, 101, 102]; + } + assert(x[2].b == [100, 101, 102]); + + assert(x[y - 3].bar.inner == [100, 101, 102]); + assert(x[y - 2].bar.inner == [103, 104, 105]); + assert(x[y - 1].bar.inner == [106, 107, 108]); + assert(x[y].bar.inner == [109, 110, 111]); +} \ No newline at end of file