Skip to content
Merged
20 changes: 19 additions & 1 deletion compiler/noirc_evaluator/src/ssa/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,25 @@ impl<'ssa, W: Write> Interpreter<'ssa, W> {
let element = elements.get(index as usize).ok_or_else(|| {
InterpreterError::IndexOutOfBounds { index, length: elements.len() as u32 }
})?;
element.clone()

// Either return a fresh nested array (in constrained context) or just clone the element.
if !self.in_unconstrained_context() {
if let Some(array) = element.as_array_or_slice() {
// In the ACIR runtime we expect fresh arrays when accessing a nested array.
// If we do not clone the elements here a mutable array set afterwards could mutate
// not just this returned array but the array we are fetching from in this array get.
Value::ArrayOrSlice(ArrayValue {
elements: Shared::new(array.elements.borrow().to_vec()),
rc: array.rc,
element_types: array.element_types,
is_slice: array.is_slice,
})
} else {
element.clone()
}
} else {
element.clone()
}
};
self.define(result, element)?;
Ok(())
Expand Down
72 changes: 72 additions & 0 deletions compiler/noirc_frontend/src/ownership/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,75 @@ fn analyzes_expression_before_lvalue_in_assignment() {
}
");
}

#[test]
fn clone_nested_array_used_as_call_arg() {
let src = "
unconstrained fn main(i: u32) -> pub bool {
let G_A: [[bool; 3]; 2] = [[false, false, false], [false, false, false]];
let result = mutate_array(G_A[i])[1];
if i != 0 {
G_A[0][1]
} else {
result
}
}
unconstrained fn mutate_array(mut a: [bool; 3]) -> [bool; 3] {
a[1] = true;
a
}
";

let program = get_monomorphized_no_emit_test(src).unwrap();
insta::assert_snapshot!(program, @r"
unconstrained fn main$f0(i$l0: u32) -> pub bool {
let G_A$l1 = [[false, false, false], [false, false, false]];
let result$l2 = mutate_array$f1(G_A$l1[i$l0].clone())[1];
if (i$l0 != 0) {
G_A$l1[0][1]
} else {
result$l2
}
}
unconstrained fn mutate_array$f1(mut a$l3: [bool; 3]) -> [bool; 3] {
a$l3[1] = true;
a$l3
}
");
}

#[test]
fn clone_global_nested_array_used_as_call_arg() {
let src = "
global G_A: [[bool; 3]; 2] = [[false, false, false], [false, false, false]];
unconstrained fn main(i: u32) -> pub bool {
let result = mutate_array(G_A[i])[1];
if i != 0 {
result
} else {
G_A[0][1]
}
}
unconstrained fn mutate_array(mut a: [bool; 3]) -> [bool; 3] {
a[1] = true;
a
}
";

let program = get_monomorphized_no_emit_test(src).unwrap();
insta::assert_snapshot!(program, @r"
global G_A$g0: [[bool; 3]; 2] = [[false, false, false], [false, false, false]];
unconstrained fn main$f0(i$l0: u32) -> pub bool {
let result$l1 = mutate_array$f1(G_A$g0[i$l0].clone())[1];
if (i$l0 != 0) {
result$l1
} else {
G_A$g0[0][1]
}
}
unconstrained fn mutate_array$f1(mut a$l2: [bool; 3]) -> [bool; 3] {
a$l2[1] = true;
a$l2
}
");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "global_nested_array_call_arg_regression"
type = "bin"
authors = [""]

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
a = true
zero = 0
return = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
global G_C: [[bool; 3]; 1] = [[true, false, true]];
fn main(a: bool, zero: u32) -> pub bool {
// NOTE: If the nested array input is not cloned this test will fail
// with `--minimal-ssa` as `func_1` will not be inlined and G_C will be mutated directly.
let b = func_1(G_C[zero])[zero + 1];
if a {
G_C[zero][zero + 1]
} else {
b
}
}
fn func_1(mut b: [bool; 3]) -> [bool; 3] {
b[1] = true;
b
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "nested_array_call_arg_regression"
type = "bin"
authors = [""]

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
a = true
zero = 0
return = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fn main(a: bool, zero: u32) -> pub bool {
let G_C: [[bool; 3]; 1] = [[true, false, true]];
// NOTE: If the nested array input is not cloned this test will fail
// with `--minimal-ssa` as `func_1` will not be inlined and G_C will be mutated directly.
let b = func_1(G_C[zero])[zero + 1];
if a {
G_C[zero][zero + 1]
} else {
b
}
}
fn func_1(mut b: [bool; 3]) -> [bool; 3] {
b[1] = true;
b
}

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