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
12 changes: 8 additions & 4 deletions compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,10 +773,14 @@ impl<'a> FunctionContext<'a> {
// Checks for index Out-of-bounds
match array_type {
Type::Array(_, len) => {
let len = self
.builder
.numeric_constant(u128::from(*len), NumericType::length_type());
self.codegen_access_check(index, len);
// Out of bounds array accesses are guaranteed to fail in ACIR so this check is performed implicitly.
// We then only need to inject it for brillig functions.
if self.builder.current_function.runtime().is_brillig() {
let len = self
.builder
.numeric_constant(u128::from(*len), NumericType::length_type());
self.codegen_access_check(index, len);
}
}
_ => unreachable!("must have array or slice but got {array_type}"),
}
Expand Down
96 changes: 96 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,99 @@ fn basic_loop() {

assert_normalized_ssa_equals(ssa, expected);
}

#[test]
fn acir_no_access_check_on_array_read() {
let src = "
fn main(mut array: [Field; 3], index: u32) -> pub Field {
array[index]
}
";
let ssa = get_initial_ssa(src).unwrap();

let expected = "
acir(inline) fn main f0 {
b0(v0: [Field; 3], v1: u32):
v2 = allocate -> &mut [Field; 3]
store v0 at v2
v3 = load v2 -> [Field; 3]
v4 = array_get v3, index v1 -> Field
return v4
}
";
assert_normalized_ssa_equals(ssa, expected);
}

#[test]
fn acir_no_access_check_on_array_assignment() {
let src = "
fn main(mut array: [Field; 3], index: u32, x: Field) {
array[index] = x;
}
";
let ssa = get_initial_ssa(src).unwrap();

let expected = "
acir(inline) fn main f0 {
b0(v0: [Field; 3], v1: u32, v2: Field):
v3 = allocate -> &mut [Field; 3]
store v0 at v3
v4 = load v3 -> [Field; 3]
v5 = array_set v4, index v1, value v2
v7 = unchecked_add v1, u32 1
store v5 at v3
return
}
";
assert_normalized_ssa_equals(ssa, expected);
}

#[test]
fn brillig_access_check_on_array_read() {
let src = "
unconstrained fn main(mut array: [Field; 3], index: u32) -> pub Field {
array[index]
}
";
let ssa = get_initial_ssa(src).unwrap();

let expected = r#"
brillig(inline) fn main f0 {
b0(v0: [Field; 3], v1: u32):
v2 = allocate -> &mut [Field; 3]
store v0 at v2
v3 = load v2 -> [Field; 3]
v5 = lt v1, u32 3
constrain v5 == u1 1, "Index out of bounds"
v7 = array_get v3, index v1 -> Field
return v7
}
"#;
assert_normalized_ssa_equals(ssa, expected);
}

#[test]
fn brillig_access_check_on_array_assignment() {
let src = "
unconstrained fn main(mut array: [Field; 3], index: u32, x: Field) {
array[index] = x;
}
";
let ssa = get_initial_ssa(src).unwrap();

let expected = r#"
brillig(inline) fn main f0 {
b0(v0: [Field; 3], v1: u32, v2: Field):
v3 = allocate -> &mut [Field; 3]
store v0 at v3
v4 = load v3 -> [Field; 3]
v6 = lt v1, u32 3
constrain v6 == u1 1, "Index out of bounds"
v8 = array_set v4, index v1, value v2
v10 = unchecked_add v1, u32 1
store v8 at v3
return
}
"#;
assert_normalized_ssa_equals(ssa, expected);
}
Loading