Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 12 additions & 27 deletions compiler/noirc_evaluator/src/acir/call/intrinsics/vector_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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()?;
Expand Down Expand Up @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "vector_insert_empty_oob"
type = "bin"
authors = [""]

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
b = true
i = 0
v = 10
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
dummy_arr = [5]
dummy_val = 2
x = 0
y = 3
y = 5
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading