diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 33ecc794f7..d7229c0adc 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -42,6 +42,8 @@ pub enum RuntimeError { UnknownLoopBound { call_stack: CallStack }, #[error("Argument is not constant")] AssertConstantFailed { call_stack: CallStack }, + #[error("Nested slices are not supported")] + NestedSlice { call_stack: CallStack }, } // We avoid showing the actual lhs and rhs since most of the time they are just 0 @@ -129,7 +131,8 @@ impl RuntimeError { | RuntimeError::UnknownLoopBound { call_stack } | RuntimeError::AssertConstantFailed { call_stack } | RuntimeError::IntegerOutOfBounds { call_stack, .. } - | RuntimeError::UnsupportedIntegerSize { call_stack, .. } => call_stack, + | RuntimeError::UnsupportedIntegerSize { call_stack, .. } + | RuntimeError::NestedSlice { call_stack, .. } => call_stack, } } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 0b8c3a37ef..eb35ba9a65 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -187,12 +187,14 @@ impl<'a> FunctionContext<'a> { let typ = Self::convert_type(&array.typ).flatten(); Ok(match array.typ { - ast::Type::Array(_, _) => self.codegen_array(elements, typ[0].clone()), + ast::Type::Array(_, _) => { + self.codegen_array_checked(elements, typ[0].clone())? + } ast::Type::Slice(_) => { let slice_length = self.builder.field_constant(array.contents.len() as u128); - - let slice_contents = self.codegen_array(elements, typ[1].clone()); + let slice_contents = + self.codegen_array_checked(elements, typ[1].clone())?; Tree::Branch(vec![slice_length.into(), slice_contents]) } _ => unreachable!( @@ -231,6 +233,18 @@ impl<'a> FunctionContext<'a> { self.codegen_array(elements, typ) } + // Codegen an array but make sure that we do not have a nested slice + fn codegen_array_checked( + &mut self, + elements: Vec, + typ: Type, + ) -> Result { + if typ.is_nested_slice() { + return Err(RuntimeError::NestedSlice { call_stack: self.builder.get_call_stack() }); + } + Ok(self.codegen_array(elements, typ)) + } + /// Codegen an array by allocating enough space for each element and inserting separate /// store instructions until each element is stored. The store instructions will be separated /// by add instructions to calculate the new offset address to store to next. diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index e95f533f09..390807afd1 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -82,6 +82,8 @@ pub enum ResolverError { NonCrateFunctionCalled { name: String, span: Span }, #[error("Only sized types may be used in the entry point to a program")] InvalidTypeForEntryPoint { span: Span }, + #[error("Nested slices are not supported")] + NestedSlices { span: Span }, } impl ResolverError { @@ -304,6 +306,11 @@ impl From for Diagnostic { ResolverError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error( "Only sized types may be used in the entry point to a program".to_string(), "Slices, references, or any type containing them may not be used in main or a contract function".to_string(), span), + ResolverError::NestedSlices { span } => Diagnostic::simple_error( + "Nested slices are not supported".into(), + "Try to use a constant sized array instead".into(), + span, + ), } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index b78c6a9e86..bb7acf1037 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -705,7 +705,12 @@ impl<'a> Resolver<'a> { /// Translates an UnresolvedType to a Type pub fn resolve_type(&mut self, typ: UnresolvedType) -> Type { - self.resolve_type_inner(typ, &mut vec![]) + let span = typ.span; + let resolved_type = self.resolve_type_inner(typ, &mut vec![]); + if resolved_type.is_nested_slice() { + self.errors.push(ResolverError::NestedSlices { span: span.unwrap() }); + } + resolved_type } pub fn resolve_type_aliases( diff --git a/compiler/noirc_frontend/src/hir/resolution/structs.rs b/compiler/noirc_frontend/src/hir/resolution/structs.rs index 72a7b73643..cf3e3436c8 100644 --- a/compiler/noirc_frontend/src/hir/resolution/structs.rs +++ b/compiler/noirc_frontend/src/hir/resolution/structs.rs @@ -24,6 +24,10 @@ pub(crate) fn resolve_structs( crate_id: CrateId, ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; + // This is necessary to avoid cloning the entire struct map + // when adding checks after each struct field is resolved. + let struct_ids = structs.keys().copied().collect::>(); + // Resolve each field in each struct. // Each struct should already be present in the NodeInterner after def collection. for (type_id, typ) in structs { @@ -35,6 +39,28 @@ pub(crate) fn resolve_structs( struct_def.generics = generics; }); } + + // Check whether the struct fields have nested slices + // We need to check after all structs are resolved to + // make sure every struct's fields is accurately set. + for id in struct_ids { + let struct_type = context.def_interner.get_struct(id); + // Only handle structs without generics as any generics args will be checked + // after monomorphization when performing SSA codegen + if struct_type.borrow().generics.is_empty() { + let fields = struct_type.borrow().get_fields(&[]); + for field in fields.iter() { + if field.1.is_nested_slice() { + errors.push(( + ResolverError::NestedSlices { span: struct_type.borrow().location.span } + .into(), + struct_type.borrow().location.file, + )); + } + } + } + } + errors } @@ -49,5 +75,6 @@ fn resolve_struct_fields( let (generics, fields, errors) = Resolver::new(&mut context.def_interner, &path_resolver, &context.def_maps, file_id) .resolve_struct_fields(unresolved.struct_def); + (generics, fields, errors) } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 69ae6e36d2..f59341e5b1 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -143,6 +143,43 @@ impl Type { | Type::Error => unreachable!("This type cannot exist as a parameter to main"), } } + + pub(crate) fn is_nested_slice(&self) -> bool { + match self { + Type::Array(size, elem) => { + if let Type::NotConstant = size.as_ref() { + elem.as_ref().contains_slice() + } else { + false + } + } + _ => false, + } + } + + fn contains_slice(&self) -> bool { + match self { + Type::Array(size, _) => matches!(size.as_ref(), Type::NotConstant), + Type::Struct(struct_typ, generics) => { + let fields = struct_typ.borrow().get_fields(generics); + for field in fields.iter() { + if field.1.contains_slice() { + return true; + } + } + false + } + Type::Tuple(types) => { + for typ in types.iter() { + if typ.contains_slice() { + return true; + } + } + false + } + _ => false, + } + } } /// A list of TypeVariableIds to bind to a type. Storing the diff --git a/docs/docs/noir/concepts/data_types/arrays.md b/docs/docs/noir/concepts/data_types/arrays.md index 075d39dadd..7f275a2d77 100644 --- a/docs/docs/noir/concepts/data_types/arrays.md +++ b/docs/docs/noir/concepts/data_types/arrays.md @@ -70,6 +70,10 @@ You can define multidimensional arrays: let array : [[Field; 2]; 2]; let element = array[0][0]; ``` +However, multidimensional slices are not supported. For example, the following code will error at compile time: +```rust +let slice : [[Field]] = []; +``` ## Types diff --git a/test_programs/execution_success/brillig_nested_slices/Nargo.toml b/test_programs/compile_failure/brillig_nested_slices/Nargo.toml similarity index 100% rename from test_programs/execution_success/brillig_nested_slices/Nargo.toml rename to test_programs/compile_failure/brillig_nested_slices/Nargo.toml diff --git a/test_programs/execution_success/brillig_nested_slices/Prover.toml b/test_programs/compile_failure/brillig_nested_slices/Prover.toml similarity index 100% rename from test_programs/execution_success/brillig_nested_slices/Prover.toml rename to test_programs/compile_failure/brillig_nested_slices/Prover.toml diff --git a/test_programs/execution_success/brillig_nested_slices/src/main.nr b/test_programs/compile_failure/brillig_nested_slices/src/main.nr similarity index 100% rename from test_programs/execution_success/brillig_nested_slices/src/main.nr rename to test_programs/compile_failure/brillig_nested_slices/src/main.nr diff --git a/test_programs/compile_failure/nested_slice_declared_type/Nargo.toml b/test_programs/compile_failure/nested_slice_declared_type/Nargo.toml new file mode 100644 index 0000000000..b0ba05135e --- /dev/null +++ b/test_programs/compile_failure/nested_slice_declared_type/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "nested_slice_declared_type" +type = "bin" +authors = [""] +compiler_version = ">=0.22.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/nested_slice_declared_type/src/main.nr b/test_programs/compile_failure/nested_slice_declared_type/src/main.nr new file mode 100644 index 0000000000..417f9a092e --- /dev/null +++ b/test_programs/compile_failure/nested_slice_declared_type/src/main.nr @@ -0,0 +1,6 @@ +fn main(x: Field, y: pub Field) { + assert(x != y); + + let slice: [[Field]] = []; + assert(slice.len() != 10); +} diff --git a/test_programs/compile_failure/nested_slice_literal/Nargo.toml b/test_programs/compile_failure/nested_slice_literal/Nargo.toml new file mode 100644 index 0000000000..db919955de --- /dev/null +++ b/test_programs/compile_failure/nested_slice_literal/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "nested_slice_literal" +type = "bin" +authors = [""] +compiler_version = ">=0.22.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/nested_slice_literal/src/main.nr b/test_programs/compile_failure/nested_slice_literal/src/main.nr new file mode 100644 index 0000000000..3140818d0b --- /dev/null +++ b/test_programs/compile_failure/nested_slice_literal/src/main.nr @@ -0,0 +1,23 @@ +struct FooParent { + parent_arr: [Field; 3], + foos: [Foo], +} + +struct Bar { + inner: [Field; 3], +} + +struct Foo { + a: Field, + b: T, + bar: Bar, +} + +fn main(x: Field, y: pub Field) { + assert(x != y); + + let foo = Foo { a: 7, b: [8, 9, 22].as_slice(), bar: Bar { inner: [106, 107, 108] } }; + let mut slice = [foo, foo]; + slice = slice.push_back(foo); + assert(slice.len() == 3); +} diff --git a/test_programs/compile_failure/nested_slice_struct/Nargo.toml b/test_programs/compile_failure/nested_slice_struct/Nargo.toml new file mode 100644 index 0000000000..a37fe6c339 --- /dev/null +++ b/test_programs/compile_failure/nested_slice_struct/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "nested_slice_struct" +type = "bin" +authors = [""] +compiler_version = ">=0.22.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/nested_slice_struct/src/main.nr b/test_programs/compile_failure/nested_slice_struct/src/main.nr new file mode 100644 index 0000000000..9fed4cfc29 --- /dev/null +++ b/test_programs/compile_failure/nested_slice_struct/src/main.nr @@ -0,0 +1,18 @@ +struct FooParent { + parent_arr: [Field; 3], + foos: [Foo], +} + +struct Bar { + inner: [Field; 3], +} + +struct Foo { + a: Field, + b: [Field], + bar: Bar, +} + +fn main(x: Field, y: pub Field) { + assert(x != y); +} diff --git a/test_programs/execution_success/brillig_set_slice_of_slice/Nargo.toml b/test_programs/execution_success/brillig_set_slice_of_slice/Nargo.toml deleted file mode 100644 index 071254266f..0000000000 --- a/test_programs/execution_success/brillig_set_slice_of_slice/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "brillig_set_slice_of_slice" -type = "bin" -authors = [""] -compiler_version = ">=0.19.4" - -[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/brillig_set_slice_of_slice/src/main.nr b/test_programs/execution_success/brillig_set_slice_of_slice/src/main.nr deleted file mode 100644 index c0e9c7d172..0000000000 --- a/test_programs/execution_success/brillig_set_slice_of_slice/src/main.nr +++ /dev/null @@ -1,51 +0,0 @@ -struct Property -{ - key : [u8], - value : [u8], -} - -struct JSON -{ - doc : [Property] -} - -unconstrained fn slice_eq(self: [u8], other: [u8]) -> bool { - let mut equal = true; - for i in 0..self.len() { - if self[i] != other[i] { - equal = false; - } - } - equal -} - -// This test acts a regression for issue #3476 -unconstrained fn main() { - let mut json = JSON { doc: [] }; - let mut prop = Property { key: [], value:[] }; - - let other_prop = Property { key: [0, 1, 2], value:[10] }; - json.doc = json.doc.push_back(other_prop); - - for i in 0..3 { - prop.key = prop.key.push_back(i as u8); - } - prop.value = prop.value.push_back(5); - - // add property to json or replace existing - let len : Field = json.doc.len(); - let mut found = false; - for i in 0..len - { - if (!found) - { - if (slice_eq(prop.key, json.doc[i].key)) - { - json.doc[i].value = prop.value; - found = true; - } - } - } - assert(found == true); - assert(json.doc[0].value[0] == 5); -} \ No newline at end of file diff --git a/test_programs/execution_success/slice_struct_field/Nargo.toml b/test_programs/execution_success/slice_struct_field/Nargo.toml deleted file mode 100644 index 9530ebf927..0000000000 --- a/test_programs/execution_success/slice_struct_field/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "slice_struct_field" -type = "bin" -authors = [""] - -[dependencies] diff --git a/test_programs/execution_success/slice_struct_field/Prover.toml b/test_programs/execution_success/slice_struct_field/Prover.toml deleted file mode 100644 index 7127baac5b..0000000000 --- a/test_programs/execution_success/slice_struct_field/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -y = "3" diff --git a/test_programs/execution_success/slice_struct_field/src/main.nr b/test_programs/execution_success/slice_struct_field/src/main.nr deleted file mode 100644 index a5b971ada4..0000000000 --- a/test_programs/execution_success/slice_struct_field/src/main.nr +++ /dev/null @@ -1,472 +0,0 @@ -struct FooParent { - parent_arr: [Field; 3], - foos: [Foo], -} - -struct Bar { - inner: [Field; 3], -} - -struct Foo { - a: Field, - b: [Field], - bar: Bar, -} - -fn main(y: pub Field) { - let mut b_one = [2, 3, 20]; - b_one = b_one.push_back(20); - let foo_one = Foo { a: 1, b: b_one, bar: Bar { inner: [100, 101, 102] } }; - - let mut b_two = [5, 6, 21]; - b_two = b_two.push_back(21); - let foo_two = Foo { a: 4, b: b_two, bar: Bar { inner: [103, 104, 105] } }; - - let foo_three = Foo { a: 7, b: [8, 9, 22], bar: Bar { inner: [106, 107, 108] } }; - let mut foo_four = Foo { a: 10, b: [11, 12, 23], bar: Bar { inner: [109, 110, 111] } }; - - let mut x = [foo_one, foo_two]; - x = x.push_back(foo_three); - x = x.push_back(foo_four); - - assert(x[y - 3].a == 1); - let struct_slice = x[y - 3].b; - for i in 0..4 { - assert(struct_slice[i] == b_one[i]); - } - - assert(x[y - 2].a == 4); - let struct_slice = x[y - 2].b; - for i in 0..4 { - assert(struct_slice[i] == b_two[i]); - } - - assert(x[y - 1].a == 7); - let struct_slice = x[y - 1].b; - assert(struct_slice[0] == 8); - assert(struct_slice[1] == 9); - assert(struct_slice[2] == 22); - - assert(x[y].a == 10); - let struct_slice = x[y].b; - assert(struct_slice[0] == 11); - assert(struct_slice[1] == 12); - assert(struct_slice[2] == 23); - assert(x[y].bar.inner == [109, 110, 111]); - - 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]); - // Check that switching the lhs and rhs is still valid - assert([109, 110, 111] == x[y].bar.inner); - - 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]); - // Check that switching the lhs and rhs is still valid - assert([109, 110, 111] == x[y].bar.inner); - - // TODO: Enable merging nested slices - // if y != 2 { - // x[y].a = 50; - // } else { - // x[y].a = 100; - // } - // assert(x[3].a == 50); - // if y == 2 { - // x[y - 1].b = [50, 51, 52]; - // } else { - // x[y - 1].b = [100, 101, 102]; - // } - // assert(x[2].b[0] == 100); - // assert(x[2].b[1] == 101); - // assert(x[2].b[2] == 102); - - let q = x.push_back(foo_four); - let foo_parent_one = FooParent { parent_arr: [0, 1, 2], foos: x }; - let foo_parent_two = FooParent { parent_arr: [3, 4, 5], foos: q }; - let mut foo_parents = [foo_parent_one]; - foo_parents = foo_parents.push_back(foo_parent_two); - // TODO: make a separate test for entirely compile time - // foo_parents[1].foos.push_back(foo_four); - // TODO: Merging nested slices is broken - // if y == 3 { - // foo_parents[y - 2].foos[y - 1].b[y - 1] = 5000; - // } else { - // foo_parents[y - 2].foos[y - 1].b[y - 1] = 1000; - // } - - assert(foo_parents[y - 2].foos[y - 2].b[y - 1] == 21); - foo_parents[y - 2].foos[y - 2].b[y - 1] = 5000; - assert(foo_parents[y - 2].foos[y - 2].b[y - 1] == 5000); - - let b_array = foo_parents[y - 2].foos[y - 3].b; - assert(foo_parents[y - 2].foos[y - 3].a == 1); - assert(b_array[0] == 2); - assert(b_array[1] == 3); - assert(b_array[2] == 20); - assert(b_array[3] == 20); - - let b_array = foo_parents[y - 2].foos[y - 2].b; - assert(foo_parents[y - 2].foos[y - 2].a == 4); - assert(b_array[0] == 5); - assert(b_array[1] == 6); - assert(b_array[2] == 5000); - assert(b_array[3] == 21); - - assert(foo_parents[y - 2].foos[y - 1].a == 7); - foo_parents[y - 2].foos[y - 1].a = 50; - assert(foo_parents[y - 2].foos[y - 1].a == 50); - - let b_array = foo_parents[y - 2].foos[y - 1].b; - assert(b_array[0] == 8); - assert(b_array[1] == 9); - assert(b_array[2] == 22); - assert(b_array.len() == 3); - - // // Test setting a nested array with non-dynamic - let x = [5, 6, 5000, 21, 100, 101].as_slice(); - foo_parents[y - 2].foos[y - 1].b = x; - - assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); - assert(foo_parents[y - 2].foos[y - 1].b[4] == 100); - assert(foo_parents[y - 2].foos[y - 1].b[5] == 101); - - // Need to account for that foo_parents is not modified outside of this function - test_basic_intrinsics_nested_slices(foo_parents, y); - test_complex_intrinsic_nested_slices(foo_parents, y); - - foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_back(500); - assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); - assert(foo_parents[y - 2].foos[y - 1].b[6] == 500); - - let (popped_slice, last_elem) = foo_parents[y - 2].foos[y - 1].b.pop_back(); - foo_parents[y - 2].foos[y - 1].b = popped_slice; - assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); - assert(last_elem == 500); - - foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_front(11); - assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); - assert(foo_parents[y - 2].foos[y - 1].b[0] == 11); - - assert(foo_parents[y - 2].foos.len() == 5); - foo_four.a = 40; - foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); - assert(foo_parents[y - 2].foos.len() == 6); - assert(foo_parents[y - 2].foos[y + 2].bar.inner == [109, 110, 111]); - - foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); - assert(foo_parents[y - 2].foos.len() == 7); - assert(foo_parents[y - 2].foos[6].a == 40); - assert(foo_parents[y - 2].foos[5].bar.inner == [109, 110, 111]); - assert(foo_parents[y - 2].foos[6].bar.inner == [109, 110, 111]); - - foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); - assert(foo_parents[y - 2].foos.len() == 8); - assert(foo_parents[y - 2].foos[6].a == 40); - assert(foo_parents[y - 2].foos[5].bar.inner == [109, 110, 111]); - assert(foo_parents[y - 2].foos[6].bar.inner == [109, 110, 111]); - - foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); - assert(foo_parents[y - 2].foos.len() == 9); - - foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); - assert(foo_parents[y - 2].foos.len() == 10); - - let b_array = foo_parents[y - 2].foos[y - 1].b; - assert(b_array[0] == 11); - assert(b_array[1] == 5); - assert(b_array[2] == 6); - assert(b_array[3] == 5000); - - let b_array = foo_parents[y - 2].foos[y].b; - assert(foo_parents[y - 2].foos[y].a == 10); - assert(b_array[0] == 11); - assert(b_array[1] == 12); - assert(b_array[2] == 23); - - assert(foo_parents[y - 2].foos[y - 3].bar.inner == [100, 101, 102]); - assert(foo_parents[y - 2].foos[y - 2].bar.inner == [103, 104, 105]); - assert(foo_parents[y - 2].foos[y - 1].bar.inner == [106, 107, 108]); - assert(foo_parents[y - 2].foos[y].bar.inner == [109, 110, 111]); -} - -fn test_basic_intrinsics_nested_slices(mut foo_parents: [FooParent], y: Field) { - foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_back(500); - assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); - assert(foo_parents[y - 2].foos[y - 1].b[6] == 500); - - let (popped_slice, last_elem) = foo_parents[y - 2].foos[y - 1].b.pop_back(); - foo_parents[y - 2].foos[y - 1].b = popped_slice; - assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); - assert(last_elem == 500); - - foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_front(11); - assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); - assert(foo_parents[y - 2].foos[y - 1].b[0] == 11); - - let (first_elem, rest_of_slice) = foo_parents[y - 2].foos[y - 1].b.pop_front(); - foo_parents[y - 2].foos[y - 1].b = rest_of_slice; - assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); - assert(first_elem == 11); - - foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.insert(2, 20); - assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); - assert(foo_parents[y - 2].foos[y - 1].b[y - 1] == 20); - assert(foo_parents[y - 2].foos[y - 1].b[y] == 5000); - assert(foo_parents[y - 2].foos[y - 1].b[6] == 101); - - let (rest_of_slice, removed_elem) = foo_parents[y - 2].foos[y - 1].b.remove(3); - foo_parents[y - 2].foos[y - 1].b = rest_of_slice; - assert(removed_elem == 5000); - assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); - assert(foo_parents[y - 2].foos[y - 1].b[2] == 20); - assert(foo_parents[y - 2].foos[y - 1].b[3] == 21); -} - -// This method test intrinsics on nested slices with complex inputs such as -// pushing a `Foo` struct onto a slice in `FooParents`. -fn test_complex_intrinsic_nested_slices(mut foo_parents: [FooParent], y: Field) { - let mut foo = Foo { a: 13, b: [14, 15, 16], bar: Bar { inner: [109, 110, 111] } }; - assert(foo_parents[y - 2].foos.len() == 5); - foo.a = 40; - foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo); - assert(foo_parents[1].foos.len() == 6); - assert(foo_parents[1].foos[5].a == 40); - assert(foo_parents[1].foos[5].b[0] == 14); - assert(foo_parents[1].foos[5].b[2] == 16); - assert(foo_parents[1].foos[5].b.len() == 3); - assert(foo_parents[1].foos[5].bar.inner == [109, 110, 111]); - - foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_back(500); - assert(foo_parents[1].foos[2].b.len() == 7); - assert(foo_parents[1].foos[2].b[6] == 500); - assert(foo_parents[1].foos[2].bar.inner == [106, 107, 108]); - assert(foo_parents[1].foos[5].a == 40); - assert(foo_parents[1].foos[5].b[0] == 14); - assert(foo_parents[1].foos[5].b[2] == 16); - assert(foo_parents[1].foos[5].b.len() == 3); - assert(foo_parents[1].foos[5].bar.inner == [109, 110, 111]); - - let (popped_slice, last_foo) = foo_parents[y - 2].foos.pop_back(); - foo_parents[y - 2].foos = popped_slice; - assert(foo_parents[y - 2].foos.len() == 5); - assert(last_foo.a == 40); - assert(last_foo.b[0] == 14); - assert(last_foo.b[1] == 15); - assert(last_foo.b[2] == 16); - assert(last_foo.bar.inner == [109, 110, 111]); - - foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_front(foo); - assert(foo_parents[1].foos.len() == 6); - assert(foo_parents[1].foos[0].a == 40); - assert(foo_parents[1].foos[0].b[0] == 14); - assert(foo_parents[1].foos[0].b[1] == 15); - assert(foo_parents[1].foos[0].b[2] == 16); - assert(foo_parents[1].foos[5].a == 10); - assert(foo_parents[1].foos[5].b.len() == 3); - assert(foo_parents[1].foos[5].b[0] == 11); - assert(foo_parents[1].foos[5].b[2] == 23); - assert(foo_parents[1].foos[5].bar.inner == [109, 110, 111]); - - assert(foo_parents[1].foos[1].a == 1); - assert(foo_parents[1].foos[1].bar.inner == [100, 101, 102]); - - let (first_foo, rest_of_slice) = foo_parents[y - 2].foos.pop_front(); - - foo_parents[y - 2].foos = rest_of_slice; - assert(first_foo.a == 40); - assert(first_foo.b[0] == 14); - assert(first_foo.b[1] == 15); - assert(first_foo.b[2] == 16); - assert(first_foo.bar.inner == [109, 110, 111]); - - assert(foo_parents[1].foos[0].a == 1); - assert(foo_parents[1].foos[0].b[0] == 2); - assert(foo_parents[1].foos[0].b[1] == 3); - assert(foo_parents[1].foos[0].b[2] == 20); - assert(foo_parents[1].foos[0].b[3] == 20); - assert(foo_parents[1].foos[0].bar.inner == [100, 101, 102]); - - test_insert_remove_const_index(foo_parents, y, foo); - - // Check values before insertion - assert(foo_parents[1].foos[1].a == 4); - assert(foo_parents[1].foos[1].b[0] == 5); - assert(foo_parents[1].foos[1].b[1] == 6); - assert(foo_parents[1].foos[1].b[2] == 5000); - assert(foo_parents[1].foos[1].b[3] == 21); - assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); - - assert(foo_parents[1].foos.len() == 5); - assert(foo_parents[1].foos[2].a == 50); - assert(foo_parents[1].foos[2].b[0] == 5); - assert(foo_parents[1].foos[2].b[2] == 5000); - assert(foo_parents[1].foos[2].bar.inner == [106, 107, 108]); - - assert(foo_parents[1].foos[3].a == 10); - assert(foo_parents[1].foos[3].b[0] == 11); - assert(foo_parents[1].foos[3].b[2] == 23); - assert(foo_parents[1].foos[3].bar.inner == [109, 110, 111]); - - foo_parents[y - 2].foos = foo_parents[y - 2].foos.insert(y - 1, foo); - assert(foo_parents[1].foos.len() == 6); - - // Check values correctly moved after insertion - assert(foo_parents[1].foos[0].a == 1); - assert(foo_parents[1].foos[0].b[0] == 2); - assert(foo_parents[1].foos[0].b[1] == 3); - assert(foo_parents[1].foos[0].b[2] == 20); - assert(foo_parents[1].foos[0].b[3] == 20); - assert(foo_parents[1].foos[0].bar.inner == [100, 101, 102]); - - assert(foo_parents[1].foos[1].a == 4); - assert(foo_parents[1].foos[1].b[0] == 5); - assert(foo_parents[1].foos[1].b[1] == 6); - assert(foo_parents[1].foos[1].b[2] == 5000); - assert(foo_parents[1].foos[1].b[3] == 21); - assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); - - assert(foo_parents[1].foos[2].a == 40); - assert(foo_parents[1].foos[2].b[0] == 14); - assert(foo_parents[1].foos[2].b[2] == 16); - assert(foo_parents[1].foos[2].bar.inner == [109, 110, 111]); - - assert(foo_parents[1].foos[3].a == 50); - assert(foo_parents[1].foos[3].b[0] == 5); - assert(foo_parents[1].foos[3].b[2] == 5000); - assert(foo_parents[1].foos[3].bar.inner == [106, 107, 108]); - - assert(foo_parents[1].foos[4].a == 10); - assert(foo_parents[1].foos[4].b[0] == 11); - assert(foo_parents[1].foos[4].b[2] == 23); - assert(foo_parents[1].foos[4].bar.inner == [109, 110, 111]); - - assert(foo_parents[1].foos[5].a == 10); - assert(foo_parents[1].foos[5].b[0] == 11); - assert(foo_parents[1].foos[5].b[2] == 23); - assert(foo_parents[1].foos[5].bar.inner == [109, 110, 111]); - - let (rest_of_slice, removed_elem) = foo_parents[y - 2].foos.remove(y - 1); - foo_parents[1].foos = rest_of_slice; - - // Check that the accurate element was removed - assert(removed_elem.a == 40); - assert(removed_elem.b[0] == 14); - assert(removed_elem.b[2] == 16); - assert(removed_elem.bar.inner == [109, 110, 111]); - - // Check that we have altered our slice accurately following a removal - assert(foo_parents[1].foos[1].a == 4); - assert(foo_parents[1].foos[1].b[0] == 5); - assert(foo_parents[1].foos[1].b[1] == 6); - assert(foo_parents[1].foos[1].b[2] == 5000); - assert(foo_parents[1].foos[1].b[3] == 21); - assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); - - assert(foo_parents[1].foos[2].a == 50); - assert(foo_parents[1].foos[2].b[0] == 5); - assert(foo_parents[1].foos[2].b[2] == 5000); - assert(foo_parents[1].foos[2].bar.inner == [106, 107, 108]); - - assert(foo_parents[1].foos[3].a == 10); - assert(foo_parents[1].foos[3].b[0] == 11); - assert(foo_parents[1].foos[3].b[2] == 23); - assert(foo_parents[1].foos[3].bar.inner == [109, 110, 111]); - - assert(foo_parents[1].foos[4].b[0] == 11); - assert(foo_parents[1].foos[4].b[2] == 23); - assert(foo_parents[1].foos[4].bar.inner == [109, 110, 111]); -} - -fn test_insert_remove_const_index(mut foo_parents: [FooParent], y: Field, foo: Foo) { - // Check values before insertion - assert(foo_parents[1].foos[1].a == 4); - assert(foo_parents[1].foos[1].b[0] == 5); - assert(foo_parents[1].foos[1].b[1] == 6); - assert(foo_parents[1].foos[1].b[2] == 5000); - assert(foo_parents[1].foos[1].b[3] == 21); - assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); - - assert(foo_parents[1].foos.len() == 5); - assert(foo_parents[1].foos[2].a == 50); - assert(foo_parents[1].foos[2].b[0] == 5); - assert(foo_parents[1].foos[2].b[2] == 5000); - assert(foo_parents[1].foos[2].bar.inner == [106, 107, 108]); - - assert(foo_parents[1].foos[3].a == 10); - assert(foo_parents[1].foos[3].b[0] == 11); - assert(foo_parents[1].foos[3].b[2] == 23); - assert(foo_parents[1].foos[3].bar.inner == [109, 110, 111]); - - foo_parents[y - 2].foos = foo_parents[y - 2].foos.insert(2, foo); - assert(foo_parents[1].foos.len() == 6); - - // Check values correctly moved after insertion - assert(foo_parents[1].foos[0].a == 1); - assert(foo_parents[1].foos[0].b[0] == 2); - assert(foo_parents[1].foos[0].b[1] == 3); - assert(foo_parents[1].foos[0].b[2] == 20); - assert(foo_parents[1].foos[0].b[3] == 20); - assert(foo_parents[1].foos[0].bar.inner == [100, 101, 102]); - - assert(foo_parents[1].foos[1].a == 4); - assert(foo_parents[1].foos[1].b[0] == 5); - assert(foo_parents[1].foos[1].b[1] == 6); - assert(foo_parents[1].foos[1].b[2] == 5000); - assert(foo_parents[1].foos[1].b[3] == 21); - assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); - - assert(foo_parents[1].foos[2].a == 40); - assert(foo_parents[1].foos[2].b[0] == 14); - assert(foo_parents[1].foos[2].b[2] == 16); - assert(foo_parents[1].foos[2].bar.inner == [109, 110, 111]); - - assert(foo_parents[1].foos[3].a == 50); - assert(foo_parents[1].foos[3].b[0] == 5); - assert(foo_parents[1].foos[3].b[2] == 5000); - assert(foo_parents[1].foos[3].bar.inner == [106, 107, 108]); - - assert(foo_parents[1].foos[4].a == 10); - assert(foo_parents[1].foos[4].b[0] == 11); - assert(foo_parents[1].foos[4].b[2] == 23); - assert(foo_parents[1].foos[4].bar.inner == [109, 110, 111]); - - assert(foo_parents[1].foos[5].a == 10); - assert(foo_parents[1].foos[5].b[0] == 11); - assert(foo_parents[1].foos[5].b[2] == 23); - assert(foo_parents[1].foos[5].bar.inner == [109, 110, 111]); - - let (rest_of_slice, removed_elem) = foo_parents[y - 2].foos.remove(2); - foo_parents[1].foos = rest_of_slice; - - // Check that the accurate element was removed - assert(removed_elem.a == 40); - assert(removed_elem.b[0] == 14); - assert(removed_elem.b[2] == 16); - assert(removed_elem.bar.inner == [109, 110, 111]); - - // Check that we have altered our slice accurately following a removal - assert(foo_parents[1].foos[1].a == 4); - assert(foo_parents[1].foos[1].b[0] == 5); - assert(foo_parents[1].foos[1].b[1] == 6); - assert(foo_parents[1].foos[1].b[2] == 5000); - assert(foo_parents[1].foos[1].b[3] == 21); - assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); - - assert(foo_parents[1].foos[2].a == 50); - assert(foo_parents[1].foos[2].b[0] == 5); - assert(foo_parents[1].foos[2].b[2] == 5000); - assert(foo_parents[1].foos[2].bar.inner == [106, 107, 108]); - - assert(foo_parents[1].foos[3].a == 10); - assert(foo_parents[1].foos[3].b[0] == 11); - assert(foo_parents[1].foos[3].b[2] == 23); - assert(foo_parents[1].foos[3].bar.inner == [109, 110, 111]); - - assert(foo_parents[1].foos[4].b[0] == 11); - assert(foo_parents[1].foos[4].b[2] == 23); - assert(foo_parents[1].foos[4].bar.inner == [109, 110, 111]); -}