diff --git a/compiler/noirc_evaluator/src/acir/call/intrinsics/vector_ops.rs b/compiler/noirc_evaluator/src/acir/call/intrinsics/vector_ops.rs index 7c16d00e1c6..6c3de7877c0 100644 --- a/compiler/noirc_evaluator/src/acir/call/intrinsics/vector_ops.rs +++ b/compiler/noirc_evaluator/src/acir/call/intrinsics/vector_ops.rs @@ -464,13 +464,6 @@ impl Context<'_> { /// - If within the insertion window, write values from `flattened_elements`. /// - If above the window, shift elements upward by the size of the inserted data. /// 4. Initialize a new memory block for the resulting vector, ensuring its type information is preserved. - /// - /// # Empty Vector Handling - /// - /// If the vector has zero length, this function skips the memory read and returns zero values. - /// It asserts that the current side effects must be disabled (predicate = 0), otherwise fails - /// with "Index out of bounds, vector has size 0". This prevents reading from empty memory blocks - /// which would cause "Index out of bounds" errors. pub(super) fn convert_vector_insert( &mut self, arguments: &[ValueId], @@ -483,24 +476,9 @@ impl Context<'_> { let vector_typ = dfg.type_of_value(vector_contents); let block_id = self.ensure_array_is_initialized(vector_contents, dfg)?; - // Check if we're trying to insert into an empty vector - if self.has_zero_length(vector_contents, dfg) { - // Make sure this code is disabled, or fail with "Index out of bounds". - let msg = "Index out of bounds, vector has size 0".to_string(); - self.acir_context.assert_zero_var(self.current_side_effects_enabled_var, msg)?; - - // Fill the result with default values. - let mut results = Vec::with_capacity(result_ids.len()); - - // For insert, results are: [new_len, new_vector] - let vector_length_value = self.convert_value(arguments[0], dfg); - results.push(vector_length_value); - - let vector = self.convert_value(vector_contents, dfg); - results.push(vector); - - return Ok(results); - } + // Check if we're trying to insert into an empty vector. + // If so, we must avoid trying to read from the original vector. + let has_zero_length = self.has_zero_length(vector_contents, dfg); let vector = self.convert_value(vector_contents, dfg); let insert_index = self.convert_value(arguments[2], dfg).into_var()?; @@ -614,8 +592,15 @@ impl Context<'_> { self.acir_context.add_var(use_shifted_index_pred, use_current_index_pred)? }; - let value_shifted_index = - self.acir_context.read_from_memory(block_id, &shifted_index)?; + // Read the original value, which we blend with the inserted ones. + let value_shifted_index = if has_zero_length { + // The original vector is empty, so we cannot read from it. + // The `should_insert_value_pred` will always be 1 in this case, + // and `not_pred` will be 0, so it doesn't matter what value we use here. + one + } else { + self.acir_context.read_from_memory(block_id, &shifted_index)? + }; // Final predicate to determine whether we are within the insertion bounds let should_insert_value_pred = diff --git a/test_programs/execution_success/vector_insert_empty_oob/Nargo.toml b/test_programs/execution_success/vector_insert_empty_oob/Nargo.toml new file mode 100644 index 00000000000..e376f908b4e --- /dev/null +++ b/test_programs/execution_success/vector_insert_empty_oob/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "vector_insert_empty_oob" +type = "bin" +authors = [""] + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/vector_insert_empty_oob/Prover.toml b/test_programs/execution_success/vector_insert_empty_oob/Prover.toml new file mode 100644 index 00000000000..e0e4fc56983 --- /dev/null +++ b/test_programs/execution_success/vector_insert_empty_oob/Prover.toml @@ -0,0 +1,3 @@ +b = true +i = 0 +v = 10 diff --git a/test_programs/execution_success/vector_insert_empty_oob/src/main.nr b/test_programs/execution_success/vector_insert_empty_oob/src/main.nr new file mode 100644 index 00000000000..839de0d3314 --- /dev/null +++ b/test_programs/execution_success/vector_insert_empty_oob/src/main.nr @@ -0,0 +1,9 @@ +// Similar to vector_insert_oob, but without assertions that can cause false negatives, +// ie. this circuit should fail for only one reason: if inserting into an empty array is rejected. +pub fn main(b: bool, i: u32, v: u32) { + if b { + let mut arr = @[]; + arr = arr.insert(i, v); + println(arr); + } +} diff --git a/test_programs/execution_failure/vector_insert_oob/Nargo.toml b/test_programs/execution_success/vector_insert_oob/Nargo.toml similarity index 100% rename from test_programs/execution_failure/vector_insert_oob/Nargo.toml rename to test_programs/execution_success/vector_insert_oob/Nargo.toml diff --git a/test_programs/execution_failure/vector_insert_oob/Prover.toml b/test_programs/execution_success/vector_insert_oob/Prover.toml similarity index 85% rename from test_programs/execution_failure/vector_insert_oob/Prover.toml rename to test_programs/execution_success/vector_insert_oob/Prover.toml index 7d5ce44dd7e..5c2a2c715d8 100644 --- a/test_programs/execution_failure/vector_insert_oob/Prover.toml +++ b/test_programs/execution_success/vector_insert_oob/Prover.toml @@ -1,4 +1,4 @@ dummy_arr = [5] dummy_val = 2 x = 0 -y = 3 +y = 5 diff --git a/test_programs/execution_failure/vector_insert_oob/src/main.nr b/test_programs/execution_success/vector_insert_oob/src/main.nr similarity index 72% rename from test_programs/execution_failure/vector_insert_oob/src/main.nr rename to test_programs/execution_success/vector_insert_oob/src/main.nr index 8e0b93de1bd..1878d34c540 100644 --- a/test_programs/execution_failure/vector_insert_oob/src/main.nr +++ b/test_programs/execution_success/vector_insert_oob/src/main.nr @@ -4,11 +4,10 @@ fn main(dummy_val: u32, dummy_arr: [u32; 1], x: u32, y: u32) { vector = vector.push_back(dummy_val); } else { // This else branch executes when x != 3 - // Should fail - inserting into empty vector with active predicate let (vector, _) = vector.remove(0); let s2 = vector.insert(0, 5); - assert_eq(x, y); assert_eq(s2.len(), 1); + assert_eq(s2[0], y); } - assert_eq(vector.len(), 2); + assert_eq(vector.len(), 1); } diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_empty_oob/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_empty_oob/execute__tests__expanded.snap new file mode 100644 index 00000000000..d0a20a4a46b --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_empty_oob/execute__tests__expanded.snap @@ -0,0 +1,11 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +pub fn main(b: bool, i: u32, v: u32) { + if b { + let mut arr: [u32] = @[]; + arr = arr.insert(i, v); + println(arr); + } +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_empty_oob/execute__tests__stdout.snap b/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_empty_oob/execute__tests__stdout.snap new file mode 100644 index 00000000000..95d0d145d6f --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_empty_oob/execute__tests__stdout.snap @@ -0,0 +1,5 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: stdout +--- +@[10] diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_oob/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_oob/execute__tests__expanded.snap new file mode 100644 index 00000000000..a5c182bb67a --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_oob/execute__tests__expanded.snap @@ -0,0 +1,16 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +fn main(dummy_val: u32, dummy_arr: [u32; 1], x: u32, y: u32) { + let mut vector: [u32] = dummy_arr.as_vector(); + if x == 3_u32 { + vector = vector.push_back(dummy_val); + } else { + let (vector, _): ([u32], u32) = vector.remove(0_u32); + let s2: [u32] = vector.insert(0_u32, 5_u32); + assert(s2.len() == 1_u32); + assert(s2[0_u32] == y); + }; + assert(vector.len() == 1_u32); +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_oob/execute__tests__stdout.snap b/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_oob/execute__tests__stdout.snap new file mode 100644 index 00000000000..e86e3de90e1 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/vector_insert_oob/execute__tests__stdout.snap @@ -0,0 +1,5 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: stdout +--- +