From 1ad13d4d00576555c6cd4fb0d2d0a87f35e814fe Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 28 Aug 2025 09:21:19 -0300 Subject: [PATCH 1/8] Bring back Display for SSA --- compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs index d52a4b5b77a..c5d923e388d 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs @@ -165,6 +165,12 @@ impl Ssa { } } +impl std::fmt::Display for Ssa { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.print_without_locations().fmt(f) + } +} + #[cfg(test)] mod test { use crate::ssa::ir::map::Id; From 4b9219aee4482d5f0edba89d6bf8551180fe5d55 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 28 Aug 2025 09:21:44 -0300 Subject: [PATCH 2/8] array_get aliasing only happens if result has a reference --- compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index ed311470544..827faca1649 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -513,9 +513,8 @@ impl<'f> PerFunctionContext<'f> { Instruction::ArrayGet { array, .. } => { let result = self.inserter.function.dfg.instruction_results(instruction)[0]; - let array = *array; - let array_typ = self.inserter.function.dfg.type_of_value(array); - if array_typ.contains_reference() { + if self.inserter.function.dfg.type_of_value(result).contains_reference() { + let array = *array; self.instruction_input_references .extend(references.get_aliases_for_value(array).iter()); references.mark_value_used(array, self.inserter.function); From 7a33f38ebe77d3e0435809a509c449a90467fa29 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 28 Aug 2025 09:22:04 -0300 Subject: [PATCH 3/8] fix: reuse existing expression and add missing alias --- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 827faca1649..1894cfd8d6c 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -519,11 +519,20 @@ impl<'f> PerFunctionContext<'f> { .extend(references.get_aliases_for_value(array).iter()); references.mark_value_used(array, self.inserter.function); - let expression = Expression::ArrayElement(array); + // An expression for the array might already exist, so try to fetch it first + let expression = references.expressions.get(&array).copied(); + let expression = expression.unwrap_or(Expression::ArrayElement(array)); if let Some(aliases) = references.aliases.get_mut(&expression) { aliases.insert(result); } + + // In this SSA: + // + // v2 = array_get v0, index v1 -> Field + // + // make v2 point to v0 so they share the same alias set + references.expressions.insert(result, expression); } } Instruction::ArraySet { array, value, .. } => { @@ -1956,4 +1965,36 @@ mod tests { } "); } + + #[test] + fn does_not_reuse_load_from_aliased_array_element() { + let src = " + brillig(inline) fn main f0 { + b0(v0: &mut Field, v1: &mut Field, v2: u32): + v3 = make_array [v0] : [&mut Field; 1] + v4 = array_set v3, index v2, value v1 + v5 = load v0 -> Field + v6 = array_get v4, index v2 -> &mut Field + store Field 0 at v6 + v7 = load v0 -> Field + return v7 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.mem2reg(); + assert_ssa_snapshot!(ssa, @r#" + brillig(inline) fn main f0 { + b0(v0: &mut Field, v1: &mut Field, v2: u32): + v3 = make_array [v0] : [&mut Field; 1] + v4 = array_set v3, index v2, value v1 + v5 = load v0 -> Field + constrain v2 == u32 0, "Index out of bounds" + v7 = array_get v4, index u32 0 -> &mut Field + store Field 0 at v7 + v9 = load v0 -> Field + return v9 + } + "#); + } } From bb643b8c24cf9e91d910c08b28f2fe2047780c8d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 28 Aug 2025 09:23:00 -0300 Subject: [PATCH 4/8] Reuse existing expression in another place --- compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 1894cfd8d6c..64912c88fac 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -543,7 +543,8 @@ impl<'f> PerFunctionContext<'f> { let result = self.inserter.function.dfg.instruction_results(instruction)[0]; let array = *array; - let expression = Expression::ArrayElement(array); + let expression = references.expressions.get(&array).copied(); + let expression = expression.unwrap_or(Expression::ArrayElement(array)); let mut aliases = if let Some(aliases) = references.aliases.get_mut(&expression) { From c5b9cabcc42f5ce6bfecb52dbeefbcf1cdd79126 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 28 Aug 2025 09:36:36 -0300 Subject: [PATCH 5/8] Snapshots --- ...sts__force_brillig_false_inliner_-9223372036854775808.snap | 4 ++-- .../execute__tests__force_brillig_false_inliner_0.snap | 4 ++-- ...ests__force_brillig_false_inliner_9223372036854775807.snap | 4 ++-- ...ests__force_brillig_true_inliner_-9223372036854775808.snap | 4 ++-- .../execute__tests__force_brillig_true_inliner_0.snap | 4 ++-- ...tests__force_brillig_true_inliner_9223372036854775807.snap | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap index 4b19120d448..241fbfd1f70 100644 --- a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap @@ -37,9 +37,9 @@ expression: artifact "return value indices : []", "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", "unconstrained func 0", - "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 24 }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Field, value: 2 }, Store { destination_pointer: Relative(2), source: Relative(3) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 23 }, Call { location: 30 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 29 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 20 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 19 }, Call { location: 26 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 25 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" ], - "debug_symbols": "dZBBDoMgEEXvMmsWSGttvYoxBnU0JAQNQpPGcPcOoq1ddDOPYfh/wl+hx9aPjTLDtEBZrdBapbUaGz110qnJ0O0KPJYshzJjkN0SioR7wmOD4AlZgiCEwODwapxFjFYnc1o5S4vGQWm81gyeUvvt0TJLs9FJS1POAE1PJMNBaYynwL5q/l9a5Lu2uH3EOalr6mSn7M9nQ/SxSrYa93bwpjtN3Ws+JkdYs5067L3F6HRKjGolrkw8aoqIQqkunF1EHeLqNw==", + "debug_symbols": "dVDbCoQgEP2XefahAmvpVyLCbApBVEwXlvDfd+yy2z7syxzHcxHPBhOOcRmUme0KbbfB6JXWahm0lSIoa+h2gyKPkkNbMijrA5oDHgQpMbj0Q/CIWX4LoFgnPJoArYlaM3gKHXfR6oTZMQhPbMEAzURIgbPSmE+Jfd3Ff2vDT29Tf8yc3D1tQir/86GUc7wSo8ZznaORNza83MVchThvJU7RY066tUKzqwpW8Z7aKPNSs+rRp/z0Gw==", "file_map": { "50": { "source": "unconstrained fn main(index: u32) {\n let v0 = &mut 1;\n let v1 = &mut 2;\n let mut v3 = [v0];\n v3[index] = v1;\n *v3[index] = 3;\n assert_eq(*v3[index], 3);\n}\n", diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_0.snap index 4b19120d448..241fbfd1f70 100644 --- a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_0.snap +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_0.snap @@ -37,9 +37,9 @@ expression: artifact "return value indices : []", "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", "unconstrained func 0", - "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 24 }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Field, value: 2 }, Store { destination_pointer: Relative(2), source: Relative(3) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 23 }, Call { location: 30 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 29 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 20 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 19 }, Call { location: 26 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 25 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" ], - "debug_symbols": "dZBBDoMgEEXvMmsWSGttvYoxBnU0JAQNQpPGcPcOoq1ddDOPYfh/wl+hx9aPjTLDtEBZrdBapbUaGz110qnJ0O0KPJYshzJjkN0SioR7wmOD4AlZgiCEwODwapxFjFYnc1o5S4vGQWm81gyeUvvt0TJLs9FJS1POAE1PJMNBaYynwL5q/l9a5Lu2uH3EOalr6mSn7M9nQ/SxSrYa93bwpjtN3Ws+JkdYs5067L3F6HRKjGolrkw8aoqIQqkunF1EHeLqNw==", + "debug_symbols": "dVDbCoQgEP2XefahAmvpVyLCbApBVEwXlvDfd+yy2z7syxzHcxHPBhOOcRmUme0KbbfB6JXWahm0lSIoa+h2gyKPkkNbMijrA5oDHgQpMbj0Q/CIWX4LoFgnPJoArYlaM3gKHXfR6oTZMQhPbMEAzURIgbPSmE+Jfd3Ff2vDT29Tf8yc3D1tQir/86GUc7wSo8ZznaORNza83MVchThvJU7RY066tUKzqwpW8Z7aKPNSs+rRp/z0Gw==", "file_map": { "50": { "source": "unconstrained fn main(index: u32) {\n let v0 = &mut 1;\n let v1 = &mut 2;\n let mut v3 = [v0];\n v3[index] = v1;\n *v3[index] = 3;\n assert_eq(*v3[index], 3);\n}\n", diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_9223372036854775807.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_9223372036854775807.snap index 4b19120d448..241fbfd1f70 100644 --- a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_9223372036854775807.snap +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_false_inliner_9223372036854775807.snap @@ -37,9 +37,9 @@ expression: artifact "return value indices : []", "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", "unconstrained func 0", - "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 24 }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Field, value: 2 }, Store { destination_pointer: Relative(2), source: Relative(3) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 23 }, Call { location: 30 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 29 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 20 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 19 }, Call { location: 26 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 25 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" ], - "debug_symbols": "dZBBDoMgEEXvMmsWSGttvYoxBnU0JAQNQpPGcPcOoq1ddDOPYfh/wl+hx9aPjTLDtEBZrdBapbUaGz110qnJ0O0KPJYshzJjkN0SioR7wmOD4AlZgiCEwODwapxFjFYnc1o5S4vGQWm81gyeUvvt0TJLs9FJS1POAE1PJMNBaYynwL5q/l9a5Lu2uH3EOalr6mSn7M9nQ/SxSrYa93bwpjtN3Ws+JkdYs5067L3F6HRKjGolrkw8aoqIQqkunF1EHeLqNw==", + "debug_symbols": "dVDbCoQgEP2XefahAmvpVyLCbApBVEwXlvDfd+yy2z7syxzHcxHPBhOOcRmUme0KbbfB6JXWahm0lSIoa+h2gyKPkkNbMijrA5oDHgQpMbj0Q/CIWX4LoFgnPJoArYlaM3gKHXfR6oTZMQhPbMEAzURIgbPSmE+Jfd3Ff2vDT29Tf8yc3D1tQir/86GUc7wSo8ZznaORNza83MVchThvJU7RY066tUKzqwpW8Z7aKPNSs+rRp/z0Gw==", "file_map": { "50": { "source": "unconstrained fn main(index: u32) {\n let v0 = &mut 1;\n let v1 = &mut 2;\n let mut v3 = [v0];\n v3[index] = v1;\n *v3[index] = 3;\n assert_eq(*v3[index], 3);\n}\n", diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap index 4b19120d448..241fbfd1f70 100644 --- a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap @@ -37,9 +37,9 @@ expression: artifact "return value indices : []", "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", "unconstrained func 0", - "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 24 }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Field, value: 2 }, Store { destination_pointer: Relative(2), source: Relative(3) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 23 }, Call { location: 30 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 29 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 20 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 19 }, Call { location: 26 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 25 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" ], - "debug_symbols": "dZBBDoMgEEXvMmsWSGttvYoxBnU0JAQNQpPGcPcOoq1ddDOPYfh/wl+hx9aPjTLDtEBZrdBapbUaGz110qnJ0O0KPJYshzJjkN0SioR7wmOD4AlZgiCEwODwapxFjFYnc1o5S4vGQWm81gyeUvvt0TJLs9FJS1POAE1PJMNBaYynwL5q/l9a5Lu2uH3EOalr6mSn7M9nQ/SxSrYa93bwpjtN3Ws+JkdYs5067L3F6HRKjGolrkw8aoqIQqkunF1EHeLqNw==", + "debug_symbols": "dVDbCoQgEP2XefahAmvpVyLCbApBVEwXlvDfd+yy2z7syxzHcxHPBhOOcRmUme0KbbfB6JXWahm0lSIoa+h2gyKPkkNbMijrA5oDHgQpMbj0Q/CIWX4LoFgnPJoArYlaM3gKHXfR6oTZMQhPbMEAzURIgbPSmE+Jfd3Ff2vDT29Tf8yc3D1tQir/86GUc7wSo8ZznaORNza83MVchThvJU7RY066tUKzqwpW8Z7aKPNSs+rRp/z0Gw==", "file_map": { "50": { "source": "unconstrained fn main(index: u32) {\n let v0 = &mut 1;\n let v1 = &mut 2;\n let mut v3 = [v0];\n v3[index] = v1;\n *v3[index] = 3;\n assert_eq(*v3[index], 3);\n}\n", diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_0.snap index 4b19120d448..241fbfd1f70 100644 --- a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_0.snap +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_0.snap @@ -37,9 +37,9 @@ expression: artifact "return value indices : []", "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", "unconstrained func 0", - "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 24 }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Field, value: 2 }, Store { destination_pointer: Relative(2), source: Relative(3) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 23 }, Call { location: 30 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 29 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 20 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 19 }, Call { location: 26 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 25 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" ], - "debug_symbols": "dZBBDoMgEEXvMmsWSGttvYoxBnU0JAQNQpPGcPcOoq1ddDOPYfh/wl+hx9aPjTLDtEBZrdBapbUaGz110qnJ0O0KPJYshzJjkN0SioR7wmOD4AlZgiCEwODwapxFjFYnc1o5S4vGQWm81gyeUvvt0TJLs9FJS1POAE1PJMNBaYynwL5q/l9a5Lu2uH3EOalr6mSn7M9nQ/SxSrYa93bwpjtN3Ws+JkdYs5067L3F6HRKjGolrkw8aoqIQqkunF1EHeLqNw==", + "debug_symbols": "dVDbCoQgEP2XefahAmvpVyLCbApBVEwXlvDfd+yy2z7syxzHcxHPBhOOcRmUme0KbbfB6JXWahm0lSIoa+h2gyKPkkNbMijrA5oDHgQpMbj0Q/CIWX4LoFgnPJoArYlaM3gKHXfR6oTZMQhPbMEAzURIgbPSmE+Jfd3Ff2vDT29Tf8yc3D1tQir/86GUc7wSo8ZznaORNza83MVchThvJU7RY066tUKzqwpW8Z7aKPNSs+rRp/z0Gw==", "file_map": { "50": { "source": "unconstrained fn main(index: u32) {\n let v0 = &mut 1;\n let v1 = &mut 2;\n let mut v3 = [v0];\n v3[index] = v1;\n *v3[index] = 3;\n assert_eq(*v3[index], 3);\n}\n", diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_9223372036854775807.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_9223372036854775807.snap index 4b19120d448..241fbfd1f70 100644 --- a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_9223372036854775807.snap +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9415/execute__tests__force_brillig_true_inliner_9223372036854775807.snap @@ -37,9 +37,9 @@ expression: artifact "return value indices : []", "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", "unconstrained func 0", - "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 24 }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Field, value: 2 }, Store { destination_pointer: Relative(2), source: Relative(3) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 23 }, Call { location: 30 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 29 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 20 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 19 }, Call { location: 26 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 25 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" ], - "debug_symbols": "dZBBDoMgEEXvMmsWSGttvYoxBnU0JAQNQpPGcPcOoq1ddDOPYfh/wl+hx9aPjTLDtEBZrdBapbUaGz110qnJ0O0KPJYshzJjkN0SioR7wmOD4AlZgiCEwODwapxFjFYnc1o5S4vGQWm81gyeUvvt0TJLs9FJS1POAE1PJMNBaYynwL5q/l9a5Lu2uH3EOalr6mSn7M9nQ/SxSrYa93bwpjtN3Ws+JkdYs5067L3F6HRKjGolrkw8aoqIQqkunF1EHeLqNw==", + "debug_symbols": "dVDbCoQgEP2XefahAmvpVyLCbApBVEwXlvDfd+yy2z7syxzHcxHPBhOOcRmUme0KbbfB6JXWahm0lSIoa+h2gyKPkkNbMijrA5oDHgQpMbj0Q/CIWX4LoFgnPJoArYlaM3gKHXfR6oTZMQhPbMEAzURIgbPSmE+Jfd3Ff2vDT29Tf8yc3D1tQir/86GUc7wSo8ZznaORNza83MVchThvJU7RY066tUKzqwpW8Z7aKPNSs+rRp/z0Gw==", "file_map": { "50": { "source": "unconstrained fn main(index: u32) {\n let v0 = &mut 1;\n let v1 = &mut 2;\n let mut v3 = [v0];\n v3[index] = v1;\n *v3[index] = 3;\n assert_eq(*v3[index], 3);\n}\n", From f83963fab622d41492089b68dd4eba7594944190 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 28 Aug 2025 12:13:13 -0300 Subject: [PATCH 6/8] Add a failing regression test --- .../regression_9657/Nargo.toml | 6 ++ .../regression_9657/Prover.toml | 1 + .../regression_9657/src/main.nr | 14 +++++ ...ig_false_inliner_-9223372036854775808.snap | 55 +++++++++++++++++++ ...lig_true_inliner_-9223372036854775808.snap | 55 +++++++++++++++++++ .../execute__tests__stdout.snap | 5 ++ 6 files changed, 136 insertions(+) create mode 100644 test_programs/execution_success/regression_9657/Nargo.toml create mode 100644 test_programs/execution_success/regression_9657/Prover.toml create mode 100644 test_programs/execution_success/regression_9657/src/main.nr create mode 100644 tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap create mode 100644 tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap create mode 100644 tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__stdout.snap diff --git a/test_programs/execution_success/regression_9657/Nargo.toml b/test_programs/execution_success/regression_9657/Nargo.toml new file mode 100644 index 00000000000..0d6d3038951 --- /dev/null +++ b/test_programs/execution_success/regression_9657/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "regression_9657" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/regression_9657/Prover.toml b/test_programs/execution_success/regression_9657/Prover.toml new file mode 100644 index 00000000000..c9f163f5756 --- /dev/null +++ b/test_programs/execution_success/regression_9657/Prover.toml @@ -0,0 +1 @@ +v2 = 0 diff --git a/test_programs/execution_success/regression_9657/src/main.nr b/test_programs/execution_success/regression_9657/src/main.nr new file mode 100644 index 00000000000..1ed1630eccc --- /dev/null +++ b/test_programs/execution_success/regression_9657/src/main.nr @@ -0,0 +1,14 @@ +unconstrained fn main(v2: u32) { + let v0 = &mut 0; + let v7 = foo(v0, v0, v2); + assert_eq(v7, 100); +} + +unconstrained fn foo(v0: &mut Field, v1: &mut Field, v2: u32) -> Field { + let mut v3 = [v0]; + v3[v2] = v1; + let _v5 = *v0; + let v6 = v3[v2]; + *v6 = 100; + *v0 +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap new file mode 100644 index 00000000000..6049b32d2aa --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap @@ -0,0 +1,55 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [ + { + "name": "v2", + "type": { + "kind": "integer", + "sign": "unsigned", + "width": 32 + }, + "visibility": "private" + } + ], + "return_type": null, + "error_types": { + "14225679739041873922": { + "error_kind": "string", + "string": "Index out of bounds" + }, + "17843811134343075018": { + "error_kind": "string", + "string": "Stack too deep" + } + } + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : [_0]", + "public parameters indices : []", + "return value indices : []", + "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", + "unconstrained func 0", + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 15 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Const { destination: Direct(32835), bit_size: Field, value: 100 }, Return, Call { location: 34 }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Field, value: 0 }, Store { destination_pointer: Relative(2), source: Relative(3) }, Const { destination: Relative(4), bit_size: Integer(U32), value: 5 }, Mov { destination: Relative(5), source: Direct(0) }, Mov { destination: Relative(6), source: Relative(2) }, Mov { destination: Relative(7), source: Relative(2) }, Mov { destination: Relative(8), source: Relative(1) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(4) }, Call { location: 40 }, Mov { destination: Direct(0), source: Relative(0) }, Mov { destination: Relative(3), source: Relative(6) }, BinaryFieldOp { destination: Relative(1), op: Equals, lhs: Relative(3), rhs: Direct(32835) }, JumpIf { condition: Relative(1), location: 33 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(2) } }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 39 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 34 }, Const { destination: Relative(4), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(5), op: Equals, bit_size: U32, lhs: Relative(3), rhs: Relative(4) }, JumpIf { condition: Relative(5), location: 45 }, Call { location: 49 }, Store { destination_pointer: Relative(2), source: Direct(32835) }, Load { destination: Relative(2), source_pointer: Relative(1) }, Mov { destination: Relative(1), source: Relative(2) }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + ], + "debug_symbols": "nZLRioQgFIbfxWsv0jSrV4kIKxsEsXBqYYnefY8d220uFoa58et0/P6Uzk5G02+PzvppfpK62UkfrHP20bl50KudPbzdSRYXVpCaUcIUokRUJzjs4ACG4IgcIRASUSAUokRASk5JniEYgp8QWIlU5QiBgEwBgExxHJRch+7WYEw88+0WcLdFB+NXUvvNOUq+tNvOTc9F+5OrDtDNKDF+BELgZJ2JTwf9s7P/VcmSK/mvLN+2C5Xs8hO7KpPNMvmBzpVIPlfqxW+h0oMNLyNxxKRgde9MKqfND7fu+r1cnWukljAPZtyCiUm3uYK1yQXNqxbmCf52IyoqWXvET/8A", + "file_map": { + "50": { + "source": "unconstrained fn main(v2: u32) {\n let v0 = &mut 0;\n let v7 = foo(v0, v0, v2);\n assert_eq(v7, 100);\n}\n\nunconstrained fn foo(v0: &mut Field, v1: &mut Field, v2: u32) -> Field {\n let mut v3 = [v0];\n v3[v2] = v1;\n let _v5 = *v0;\n let v6 = v3[v2];\n *v6 = 100;\n *v0\n}\n", + "path": "" + } + }, + "names": [ + "main" + ], + "brillig_names": [ + "main" + ] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap new file mode 100644 index 00000000000..6049b32d2aa --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap @@ -0,0 +1,55 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [ + { + "name": "v2", + "type": { + "kind": "integer", + "sign": "unsigned", + "width": 32 + }, + "visibility": "private" + } + ], + "return_type": null, + "error_types": { + "14225679739041873922": { + "error_kind": "string", + "string": "Index out of bounds" + }, + "17843811134343075018": { + "error_kind": "string", + "string": "Stack too deep" + } + } + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : [_0]", + "public parameters indices : []", + "return value indices : []", + "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", + "unconstrained func 0", + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 15 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Const { destination: Direct(32835), bit_size: Field, value: 100 }, Return, Call { location: 34 }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Field, value: 0 }, Store { destination_pointer: Relative(2), source: Relative(3) }, Const { destination: Relative(4), bit_size: Integer(U32), value: 5 }, Mov { destination: Relative(5), source: Direct(0) }, Mov { destination: Relative(6), source: Relative(2) }, Mov { destination: Relative(7), source: Relative(2) }, Mov { destination: Relative(8), source: Relative(1) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(4) }, Call { location: 40 }, Mov { destination: Direct(0), source: Relative(0) }, Mov { destination: Relative(3), source: Relative(6) }, BinaryFieldOp { destination: Relative(1), op: Equals, lhs: Relative(3), rhs: Direct(32835) }, JumpIf { condition: Relative(1), location: 33 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(2) } }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 39 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 34 }, Const { destination: Relative(4), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(5), op: Equals, bit_size: U32, lhs: Relative(3), rhs: Relative(4) }, JumpIf { condition: Relative(5), location: 45 }, Call { location: 49 }, Store { destination_pointer: Relative(2), source: Direct(32835) }, Load { destination: Relative(2), source_pointer: Relative(1) }, Mov { destination: Relative(1), source: Relative(2) }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + ], + "debug_symbols": "nZLRioQgFIbfxWsv0jSrV4kIKxsEsXBqYYnefY8d220uFoa58et0/P6Uzk5G02+PzvppfpK62UkfrHP20bl50KudPbzdSRYXVpCaUcIUokRUJzjs4ACG4IgcIRASUSAUokRASk5JniEYgp8QWIlU5QiBgEwBgExxHJRch+7WYEw88+0WcLdFB+NXUvvNOUq+tNvOTc9F+5OrDtDNKDF+BELgZJ2JTwf9s7P/VcmSK/mvLN+2C5Xs8hO7KpPNMvmBzpVIPlfqxW+h0oMNLyNxxKRgde9MKqfND7fu+r1cnWukljAPZtyCiUm3uYK1yQXNqxbmCf52IyoqWXvET/8A", + "file_map": { + "50": { + "source": "unconstrained fn main(v2: u32) {\n let v0 = &mut 0;\n let v7 = foo(v0, v0, v2);\n assert_eq(v7, 100);\n}\n\nunconstrained fn foo(v0: &mut Field, v1: &mut Field, v2: u32) -> Field {\n let mut v3 = [v0];\n v3[v2] = v1;\n let _v5 = *v0;\n let v6 = v3[v2];\n *v6 = 100;\n *v0\n}\n", + "path": "" + } + }, + "names": [ + "main" + ], + "brillig_names": [ + "main" + ] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__stdout.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__stdout.snap new file mode 100644 index 00000000000..e86e3de90e1 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__stdout.snap @@ -0,0 +1,5 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: stdout +--- + From 0ef4838baf10f126e5035981e72435318073a7d2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 28 Aug 2025 12:41:33 -0300 Subject: [PATCH 7/8] Fix an issue with necessary stores being removed --- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 95 +++++++++++++++++-- ...ig_false_inliner_-9223372036854775808.snap | 4 +- ...lig_true_inliner_-9223372036854775808.snap | 4 +- .../execute__tests__expanded.snap | 18 ++++ ..._tests__force_brillig_false_inliner_0.snap | 55 +++++++++++ ...lig_false_inliner_9223372036854775807.snap | 55 +++++++++++ ...__tests__force_brillig_true_inliner_0.snap | 55 +++++++++++ ...llig_true_inliner_9223372036854775807.snap | 55 +++++++++++ 8 files changed, 330 insertions(+), 11 deletions(-) create mode 100644 tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__expanded.snap create mode 100644 tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_0.snap create mode 100644 tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_9223372036854775807.snap create mode 100644 tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_0.snap create mode 100644 tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_9223372036854775807.snap diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 64912c88fac..4b407105dbe 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -329,7 +329,7 @@ impl<'f> PerFunctionContext<'f> { // as well. We can't do this if there are multiple blocks since subsequent blocks may // reference these stores. if self.post_order.as_slice().len() == 1 { - self.remove_stores_that_do_not_alias_parameters(&references); + self.remove_stores_that_do_not_alias_parameters_or_returns(&references); } self.blocks.insert(block, references); @@ -384,17 +384,26 @@ impl<'f> PerFunctionContext<'f> { /// Add all instructions in `last_stores` to `self.instructions_to_remove` which do not /// possibly alias any parameters of the given function. - fn remove_stores_that_do_not_alias_parameters(&mut self, references: &Block) { + fn remove_stores_that_do_not_alias_parameters_or_returns(&mut self, references: &Block) { let reference_parameters = self.reference_parameters(); for (allocation, instruction) in &references.last_stores { let aliases = references.get_aliases_for_value(*allocation); + let allocation_aliases_parameter = aliases.any(|alias| reference_parameters.contains(&alias)); // If `allocation_aliases_parameter` is known to be false - if allocation_aliases_parameter == Some(false) { - self.instructions_to_remove.insert(*instruction); + if allocation_aliases_parameter != Some(false) { + continue; + } + + let allocation_aliases_input_reference = + aliases.any(|alias| self.instruction_input_references.contains(&alias)); + if allocation_aliases_input_reference != Some(false) { + continue; } + + self.instructions_to_remove.insert(*instruction); } } @@ -742,8 +751,8 @@ impl<'f> PerFunctionContext<'f> { // We need to appropriately mark each alias of a reference as being used as a return terminator argument. // This prevents us potentially removing a last store from a preceding block or is altered within another function. for return_value in return_values { - self.instruction_input_references - .extend(references.get_aliases_for_value(*return_value).iter()); + let aliases = references.get_aliases_for_value(*return_value); + self.instruction_input_references.extend(aliases.iter()); } // Removing all `last_stores` for each returned reference is more important here // than setting them all to unknown since no other block should @@ -1567,8 +1576,43 @@ mod tests { #[test] fn removes_last_store_in_single_block() { + let src = " + brillig(inline) impure fn main f0 { + b0(): + v0 = allocate -> &mut [Field; 2] + v1 = load v0 -> [Field; 2] + store v1 at v0 + return + } + + brillig(inline) impure fn append_note_hashes_with_logs f1 { + b0(v0: &mut [Field; 2]): + return + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + + let ssa = ssa.mem2reg(); + assert_ssa_snapshot!(ssa, @r" + brillig(inline) impure fn main f0 { + b0(): + v0 = allocate -> &mut [Field; 2] + v1 = load v0 -> [Field; 2] + return + } + brillig(inline) impure fn append_note_hashes_with_logs f1 { + b0(v0: &mut [Field; 2]): + return + } + "); + } + + #[test] + fn does_not_remove_last_store_in_single_block() { // Even though v0 is a reference passed to a function, the store that comes next - // can be removed as it is later never read, and this is the only block in the function. + // isn't removed because it's considered an "input reference" and we can't currently + // tell if that value is going to be returned from the function. let src = " brillig(inline) impure fn main f0 { b0(): @@ -1594,6 +1638,7 @@ mod tests { v0 = allocate -> &mut [Field; 2] call f1(v0) v2 = load v0 -> [Field; 2] + store v2 at v0 return } brillig(inline) impure fn append_note_hashes_with_logs f1 { @@ -1998,4 +2043,40 @@ mod tests { } "#); } + + #[test] + fn does_not_remove_store_from_aliased_array_element() { + let src = " + brillig(inline) fn main f0 { + b0(v0: u32): + v1 = allocate -> &mut Field + store Field 0 at v1 + v3 = make_array [v1] : [&mut Field; 1] + v5 = array_set v3, index v0, value v1 + v6 = array_get v5, index v0 -> &mut Field + store Field 100 at v6 + v8 = load v1 -> Field + constrain v8 == Field 100 + return v8 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.mem2reg(); + assert_ssa_snapshot!(ssa, @r#" + brillig(inline) fn main f0 { + b0(v0: u32): + v1 = allocate -> &mut Field + store Field 0 at v1 + v3 = make_array [v1] : [&mut Field; 1] + v4 = array_set v3, index v0, value v1 + constrain v0 == u32 0, "Index out of bounds" + v6 = array_get v4, index u32 0 -> &mut Field + store Field 100 at v6 + v8 = load v1 -> Field + constrain v8 == Field 100 + return v8 + } + "#); + } } diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/brillig_rc_regression_6123/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap b/tooling/nargo_cli/tests/snapshots/execution_success/brillig_rc_regression_6123/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap index b82834aa3a3..38491e75ecc 100644 --- a/tooling/nargo_cli/tests/snapshots/execution_success/brillig_rc_regression_6123/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap +++ b/tooling/nargo_cli/tests/snapshots/execution_success/brillig_rc_regression_6123/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap @@ -35,9 +35,9 @@ expression: artifact "return value indices : []", "BRILLIG CALL func 0: inputs: [], outputs: []", "unconstrained func 0", - "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 0 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32838), size_address: Relative(1), offset_address: Relative(2) }, Call { location: 11 }, Call { location: 15 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Const { destination: Direct(32835), bit_size: Integer(U32), value: 0 }, Const { destination: Direct(32836), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(32837), bit_size: Integer(U32), value: 2 }, Return, Call { location: 66 }, Const { destination: Relative(1), bit_size: Field, value: 0 }, Mov { destination: Relative(2), source: Direct(1) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 3 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(3) }, IndirectConst { destination_pointer: Relative(2), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, Mov { destination: Relative(4), source: Relative(3) }, Store { destination_pointer: Relative(4), source: Relative(1) }, BinaryIntOp { destination: Relative(4), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(1) }, Load { destination: Relative(1), source_pointer: Relative(2) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(4), op: Equals, bit_size: U32, lhs: Relative(3), rhs: Relative(1) }, Not { destination: Relative(4), source: Relative(4), bit_size: U1 }, JumpIf { condition: Relative(4), location: 32 }, Call { location: 72 }, BinaryIntOp { destination: Relative(1), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(1) }, Mov { destination: Relative(1), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(1), source: Relative(2) }, Mov { destination: Relative(4), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Direct(32835) }, Mov { destination: Relative(5), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(2) }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Direct(32835) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 7 }, Mov { destination: Relative(7), source: Direct(0) }, Mov { destination: Relative(8), source: Relative(1) }, Mov { destination: Relative(9), source: Relative(4) }, Mov { destination: Relative(10), source: Relative(5) }, Mov { destination: Relative(11), source: Relative(2) }, Mov { destination: Relative(12), source: Direct(32837) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(6) }, Call { location: 75 }, Mov { destination: Direct(0), source: Relative(0) }, Load { destination: Relative(2), source_pointer: Relative(1) }, Load { destination: Relative(1), source_pointer: Relative(2) }, Const { destination: Relative(4), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(5), op: Equals, bit_size: U32, lhs: Relative(4), rhs: Relative(1) }, Not { destination: Relative(5), source: Relative(5), bit_size: U1 }, JumpIf { condition: Relative(5), location: 63 }, Call { location: 72 }, BinaryIntOp { destination: Relative(1), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(1) }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 71 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 12049594436772143978 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 66 }, Load { destination: Relative(7), source_pointer: Relative(1) }, Load { destination: Relative(8), source_pointer: Relative(2) }, Load { destination: Relative(9), source_pointer: Relative(7) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(11), op: Equals, bit_size: U32, lhs: Relative(10), rhs: Relative(9) }, Not { destination: Relative(11), source: Relative(11), bit_size: U1 }, JumpIf { condition: Relative(11), location: 84 }, Call { location: 72 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Relative(9) }, Mov { destination: Relative(6), source: Direct(32835) }, Jump { location: 88 }, BinaryIntOp { destination: Relative(3), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Direct(32837) }, JumpIf { condition: Relative(3), location: 92 }, Jump { location: 91 }, Return, BinaryIntOp { destination: Relative(3), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Relative(5) }, JumpIf { condition: Relative(3), location: 95 }, Jump { location: 109 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, BinaryIntOp { destination: Relative(4), op: LessThanEquals, bit_size: U32, lhs: Relative(8), rhs: Relative(3) }, JumpIf { condition: Relative(4), location: 99 }, Call { location: 112 }, Cast { destination: Relative(4), source: Relative(3), bit_size: Field }, Const { destination: Relative(3), bit_size: Integer(U32), value: 9 }, Mov { destination: Relative(9), source: Direct(0) }, Mov { destination: Relative(10), source: Relative(1) }, Mov { destination: Relative(11), source: Relative(2) }, Mov { destination: Relative(12), source: Relative(4) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(3) }, Call { location: 115 }, Mov { destination: Direct(0), source: Relative(0) }, Jump { location: 109 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(32836) }, Mov { destination: Relative(6), source: Relative(3) }, Jump { location: 88 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 5019202896831570965 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 66 }, Load { destination: Relative(4), source_pointer: Relative(2) }, BinaryIntOp { destination: Relative(5), op: LessThan, bit_size: U32, lhs: Relative(4), rhs: Direct(32837) }, Const { destination: Relative(6), bit_size: Integer(U1), value: 1 }, JumpIf { condition: Relative(5), location: 121 }, Call { location: 136 }, Load { destination: Relative(5), source_pointer: Relative(1) }, Mov { destination: Direct(32771), source: Relative(5) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 3 }, Call { location: 139 }, Mov { destination: Relative(6), source: Direct(32773) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(2) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(7), rhs: Relative(4) }, Store { destination_pointer: Relative(8), source: Relative(3) }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(32836) }, BinaryIntOp { destination: Relative(5), op: LessThanEquals, bit_size: U32, lhs: Relative(4), rhs: Relative(3) }, JumpIf { condition: Relative(5), location: 133 }, Call { location: 112 }, Store { destination_pointer: Relative(1), source: Relative(6) }, Store { destination_pointer: Relative(2), source: Relative(3) }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 5727012404371710682 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Load { destination: Direct(32774), source_pointer: Direct(32771) }, BinaryIntOp { destination: Direct(32775), op: Equals, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, JumpIf { condition: Direct(32775), location: 143 }, Jump { location: 145 }, Mov { destination: Direct(32773), source: Direct(32771) }, Jump { location: 160 }, Mov { destination: Direct(32773), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(32772) }, BinaryIntOp { destination: Direct(32777), op: Add, bit_size: U32, lhs: Direct(32771), rhs: Direct(32772) }, Mov { destination: Direct(32778), source: Direct(32771) }, Mov { destination: Direct(32779), source: Direct(32773) }, BinaryIntOp { destination: Direct(32780), op: Equals, bit_size: U32, lhs: Direct(32778), rhs: Direct(32777) }, JumpIf { condition: Direct(32780), location: 157 }, Load { destination: Direct(32776), source_pointer: Direct(32778) }, Store { destination_pointer: Direct(32779), source: Direct(32776) }, BinaryIntOp { destination: Direct(32778), op: Add, bit_size: U32, lhs: Direct(32778), rhs: Direct(2) }, BinaryIntOp { destination: Direct(32779), op: Add, bit_size: U32, lhs: Direct(32779), rhs: Direct(2) }, Jump { location: 150 }, IndirectConst { destination_pointer: Direct(32773), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Direct(32774), op: Sub, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, Jump { location: 160 }, Return]" + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 0 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32838), size_address: Relative(1), offset_address: Relative(2) }, Call { location: 11 }, Call { location: 15 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Const { destination: Direct(32835), bit_size: Integer(U32), value: 0 }, Const { destination: Direct(32836), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(32837), bit_size: Integer(U32), value: 2 }, Return, Call { location: 112 }, Const { destination: Relative(1), bit_size: Field, value: 0 }, Mov { destination: Relative(2), source: Direct(1) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 3 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(3) }, IndirectConst { destination_pointer: Relative(2), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, Mov { destination: Relative(4), source: Relative(3) }, Store { destination_pointer: Relative(4), source: Relative(1) }, BinaryIntOp { destination: Relative(4), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(1) }, Load { destination: Relative(1), source_pointer: Relative(2) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(4), op: Equals, bit_size: U32, lhs: Relative(3), rhs: Relative(1) }, Not { destination: Relative(4), source: Relative(4), bit_size: U1 }, JumpIf { condition: Relative(4), location: 32 }, Call { location: 118 }, BinaryIntOp { destination: Relative(1), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(1) }, Mov { destination: Relative(1), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(1), source: Relative(2) }, Mov { destination: Relative(4), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Direct(32835) }, Mov { destination: Relative(5), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(2) }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Direct(32835) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 7 }, Mov { destination: Relative(7), source: Direct(0) }, Mov { destination: Relative(8), source: Relative(1) }, Mov { destination: Relative(9), source: Relative(4) }, Mov { destination: Relative(10), source: Relative(5) }, Mov { destination: Relative(11), source: Relative(2) }, Mov { destination: Relative(12), source: Direct(32837) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(6) }, Call { location: 121 }, Mov { destination: Direct(0), source: Relative(0) }, Load { destination: Relative(6), source_pointer: Relative(5) }, Load { destination: Relative(7), source_pointer: Relative(2) }, Const { destination: Relative(8), bit_size: Field, value: 27 }, Mov { destination: Direct(32771), source: Relative(6) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 3 }, Call { location: 158 }, Mov { destination: Relative(9), source: Direct(32773) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(32837) }, Store { destination_pointer: Relative(10), source: Relative(8) }, Store { destination_pointer: Relative(5), source: Relative(9) }, Store { destination_pointer: Relative(2), source: Relative(7) }, Load { destination: Relative(2), source_pointer: Relative(1) }, Load { destination: Relative(5), source_pointer: Relative(2) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(7), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(5) }, Not { destination: Relative(7), source: Relative(7), bit_size: U1 }, JumpIf { condition: Relative(7), location: 74 }, Call { location: 118 }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(5) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(32836) }, Load { destination: Relative(5), source_pointer: Relative(7) }, Load { destination: Relative(7), source_pointer: Relative(2) }, Const { destination: Relative(8), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(7) }, Not { destination: Relative(9), source: Relative(9), bit_size: U1 }, JumpIf { condition: Relative(9), location: 84 }, Call { location: 118 }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(7) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(32837) }, Load { destination: Relative(7), source_pointer: Relative(9) }, Load { destination: Relative(9), source_pointer: Relative(2) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(11), op: Equals, bit_size: U32, lhs: Relative(10), rhs: Relative(9) }, Not { destination: Relative(11), source: Relative(11), bit_size: U1 }, JumpIf { condition: Relative(11), location: 94 }, Call { location: 118 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(9) }, Load { destination: Relative(9), source_pointer: Relative(4) }, Mov { destination: Direct(32771), source: Relative(2) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 3 }, Call { location: 158 }, Mov { destination: Relative(11), source: Direct(32773) }, BinaryIntOp { destination: Relative(12), op: Add, bit_size: U32, lhs: Relative(11), rhs: Direct(32837) }, Store { destination_pointer: Relative(12), source: Relative(5) }, Mov { destination: Direct(32771), source: Relative(11) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 3 }, Call { location: 158 }, Mov { destination: Relative(2), source: Direct(32773) }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(32836) }, Store { destination_pointer: Relative(5), source: Relative(7) }, Store { destination_pointer: Relative(1), source: Relative(2) }, Store { destination_pointer: Relative(4), source: Relative(9) }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 117 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 12049594436772143978 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 112 }, Load { destination: Relative(7), source_pointer: Relative(1) }, Load { destination: Relative(8), source_pointer: Relative(2) }, Load { destination: Relative(9), source_pointer: Relative(7) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(11), op: Equals, bit_size: U32, lhs: Relative(10), rhs: Relative(9) }, Not { destination: Relative(11), source: Relative(11), bit_size: U1 }, JumpIf { condition: Relative(11), location: 130 }, Call { location: 118 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Relative(9) }, Mov { destination: Relative(6), source: Direct(32835) }, Jump { location: 134 }, BinaryIntOp { destination: Relative(3), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Direct(32837) }, JumpIf { condition: Relative(3), location: 138 }, Jump { location: 137 }, Return, BinaryIntOp { destination: Relative(3), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Relative(5) }, JumpIf { condition: Relative(3), location: 141 }, Jump { location: 155 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, BinaryIntOp { destination: Relative(4), op: LessThanEquals, bit_size: U32, lhs: Relative(8), rhs: Relative(3) }, JumpIf { condition: Relative(4), location: 145 }, Call { location: 180 }, Cast { destination: Relative(4), source: Relative(3), bit_size: Field }, Const { destination: Relative(3), bit_size: Integer(U32), value: 9 }, Mov { destination: Relative(9), source: Direct(0) }, Mov { destination: Relative(10), source: Relative(1) }, Mov { destination: Relative(11), source: Relative(2) }, Mov { destination: Relative(12), source: Relative(4) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(3) }, Call { location: 183 }, Mov { destination: Direct(0), source: Relative(0) }, Jump { location: 155 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(32836) }, Mov { destination: Relative(6), source: Relative(3) }, Jump { location: 134 }, Load { destination: Direct(32774), source_pointer: Direct(32771) }, BinaryIntOp { destination: Direct(32775), op: Equals, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, JumpIf { condition: Direct(32775), location: 162 }, Jump { location: 164 }, Mov { destination: Direct(32773), source: Direct(32771) }, Jump { location: 179 }, Mov { destination: Direct(32773), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(32772) }, BinaryIntOp { destination: Direct(32777), op: Add, bit_size: U32, lhs: Direct(32771), rhs: Direct(32772) }, Mov { destination: Direct(32778), source: Direct(32771) }, Mov { destination: Direct(32779), source: Direct(32773) }, BinaryIntOp { destination: Direct(32780), op: Equals, bit_size: U32, lhs: Direct(32778), rhs: Direct(32777) }, JumpIf { condition: Direct(32780), location: 176 }, Load { destination: Direct(32776), source_pointer: Direct(32778) }, Store { destination_pointer: Direct(32779), source: Direct(32776) }, BinaryIntOp { destination: Direct(32778), op: Add, bit_size: U32, lhs: Direct(32778), rhs: Direct(2) }, BinaryIntOp { destination: Direct(32779), op: Add, bit_size: U32, lhs: Direct(32779), rhs: Direct(2) }, Jump { location: 169 }, IndirectConst { destination_pointer: Direct(32773), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Direct(32774), op: Sub, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, Jump { location: 179 }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 5019202896831570965 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 112 }, Load { destination: Relative(4), source_pointer: Relative(2) }, BinaryIntOp { destination: Relative(5), op: LessThan, bit_size: U32, lhs: Relative(4), rhs: Direct(32837) }, Const { destination: Relative(6), bit_size: Integer(U1), value: 1 }, JumpIf { condition: Relative(5), location: 189 }, Call { location: 204 }, Load { destination: Relative(5), source_pointer: Relative(1) }, Mov { destination: Direct(32771), source: Relative(5) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 3 }, Call { location: 158 }, Mov { destination: Relative(6), source: Direct(32773) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(2) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(7), rhs: Relative(4) }, Store { destination_pointer: Relative(8), source: Relative(3) }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(32836) }, BinaryIntOp { destination: Relative(5), op: LessThanEquals, bit_size: U32, lhs: Relative(4), rhs: Relative(3) }, JumpIf { condition: Relative(5), location: 201 }, Call { location: 180 }, Store { destination_pointer: Relative(1), source: Relative(6) }, Store { destination_pointer: Relative(2), source: Relative(3) }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 5727012404371710682 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" ], - "debug_symbols": "tZbdbqMwEIXfhWsuPB7/jPsqVVXRhFRIiEQ0WWkV5d13hvGkzQWrLave8B1jzmGwB8G12fdvl/fXYTocP5qn52vzNg/jOLy/jsdddx6OE5+9Nk4OkJon3zaQFaQoC7xTgMIrUBEUUcEpgZEVpCgL0ClA4RWo4BRkREVSZAUpyoLgFKDwClRoStCUwCmRkRWkKAuiU4DCK1ARFFHBKYnBKcQgRVmQnAIUXoGKsCCzvTCygu3gmEVJrhIqfSVWhspYWf1FrgOmnOdlL6kyV1Kl3IeXApzcKIoAE94EmggmoolkIpuQQN4vkHaBJEKmsggyUaqQtlEBJuSmJAJNBBPRRDKRTZAJSeZlBGklFWDCm0ATklxut7axZn89z30vvf6l+/mdOHVzP52bp+kyjm3zqxsvy0Ufp25aeO5mnuVV76c9kwMPw9iLurWfbrduJYRqpkB3e3z0w7rfZ+drgM/e3xPSPxeQzE+5rBWAP1kAUfUXH9YK+Iu/IJo/xQ1+gGBbwH2Kawl5PSH5UANSgA0VJHTFAtDT95fQRyvAk9tQgC+2BQh5gx+TvQGY0xZ/tOdH2uTHZP64/hKF9YBA9gDRbdnB//Unuu9gogzf74BUXLSA4vOWgHCvoMSHgBcedLthfvgxuEnUPHRvY1+Hh8u0+zJ7/n2yGfuxOM3HXb+/zL0kff5d8OEZsLSQ3It8RniYUptBBrDMpRaQXm5Syh8=", + "debug_symbols": "tZfLbuJAEEX/xWsWXdXv/EoURYQ4EZIFyIGRRhH/PlW+XU5YGGVA2XCOMXXd7i638Gf32r+c3p+3u7f9R/fw+Nm9jNth2L4/D/vN+rjd7+Tbz87pB6XugVcdZaAAdQI7gAAGPBCACEhKEGSgAHWCdwABDHhAUrwgAgnIQAHqhOAAAhjwAFICUoKkREEGClAnRAcQwIAHAhABScmCDEhKEdQJyQEEMOCBAEQgAZKSBAWoE7IDCGDAAwGIgKRUQQYkhUhYweIaqZEbfWNojI2SRjJPJTdqnqxhqWB1jdTIjb4xNMZGzZMZq5ono62lsYLknAmZsIlmFpVgEk2SSTYpJrUJuSbam1RVvIn2p1OJJskkmxST2kR7FUImlqPtyDJ9pC3I+pBoE0K8STDRi3oVvWhUySbFpDbRpoSQCZt4Ew0M+oDqJXTqdDk5qySTbFJMahNdU9aJ0kWFsIk3CSbRJJloss6qri2kQlgXF0ImmlzP51Vnu83zcex73Wy+bT+yKR3WY787dg+70zCsuj/r4TT96OOw3k08rkc5K3Pf716FEvi2HXq18+qr2i2XFk+tuIQyl8fLelqu5+y4BXBmnhPSjweQrL7kujQA/5sDKKXVVw5LA7hSX723+hSX6tNyfZCOaQGBKS/cwJWA5F1tAclz+f8ZkD0itgAiqjfMAckTPycUv9hH1yJYn11EcAq3RKQ5IQVaDOB7l+LqEOK8FDktDiHcPYRwbztcu4nM1tE5uMWbyL/YkXcHcLRW4OJuaGieV8F/W4Sf1/tke7Jf7oNr9XMf+XJTvU9WHxe3db7Wh8VuIDq6YQD31qcyr2AqmW5ooTpvaqlyviUgzCOo8SLgSQ7Wm+148a5w1qhxu34Z+nb4dtptvp09/j3YGXvXOIz7Tf96GntN+nrhkI9HimVFuT7pfzk9lH2LKOuh/Il4ZBdW7NLTWQfzDw==", "file_map": { "6": { "source": "use crate::{cmp::Eq, convert::From, runtime::is_unconstrained, static_assert};\n\n/// A `BoundedVec` is a growable storage similar to a `Vec` except that it\n/// is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented\n/// via slices and thus is not subject to the same restrictions slices are (notably, nested\n/// slices - and thus nested vectors as well - are disallowed).\n///\n/// Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by\n/// pushing an additional element is also more efficient - the length only needs to be increased\n/// by one.\n///\n/// For these reasons `BoundedVec` should generally be preferred over `Vec` when there\n/// is a reasonable maximum bound that can be placed on the vector.\n///\n/// Example:\n///\n/// ```noir\n/// let mut vector: BoundedVec = BoundedVec::new();\n/// for i in 0..5 {\n/// vector.push(i);\n/// }\n/// assert(vector.len() == 5);\n/// assert(vector.max_len() == 10);\n/// ```\npub struct BoundedVec {\n storage: [T; MaxLen],\n len: u32,\n}\n\nimpl BoundedVec {\n /// Creates a new, empty vector of length zero.\n ///\n /// Since this container is backed by an array internally, it still needs an initial value\n /// to give each element. To resolve this, each element is zeroed internally. This value\n /// is guaranteed to be inaccessible unless `get_unchecked` is used.\n ///\n /// Example:\n ///\n /// ```noir\n /// let empty_vector: BoundedVec = BoundedVec::new();\n /// assert(empty_vector.len() == 0);\n /// ```\n ///\n /// Note that whenever calling `new` the maximum length of the vector should always be specified\n /// via a type signature:\n ///\n /// ```noir\n /// fn good() -> BoundedVec {\n /// // Ok! MaxLen is specified with a type annotation\n /// let v1: BoundedVec = BoundedVec::new();\n /// let v2 = BoundedVec::new();\n ///\n /// // Ok! MaxLen is known from the type of `good`'s return value\n /// v2\n /// }\n ///\n /// fn bad() {\n /// // Error: Type annotation needed\n /// // The compiler can't infer `MaxLen` from the following code:\n /// let mut v3 = BoundedVec::new();\n /// v3.push(5);\n /// }\n /// ```\n ///\n /// This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions\n /// but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a\n /// constraint failure at runtime when the vec is pushed to.\n pub fn new() -> Self {\n let zeroed = crate::mem::zeroed();\n BoundedVec { storage: [zeroed; MaxLen], len: 0 }\n }\n\n /// Retrieves an element from the vector at the given index, starting from zero.\n ///\n /// If the given index is equal to or greater than the length of the vector, this\n /// will issue a constraint failure.\n ///\n /// Example:\n ///\n /// ```noir\n /// fn foo(v: BoundedVec) {\n /// let first = v.get(0);\n /// let last = v.get(v.len() - 1);\n /// assert(first != last);\n /// }\n /// ```\n pub fn get(self, index: u32) -> T {\n assert(index < self.len, \"Attempted to read past end of BoundedVec\");\n self.get_unchecked(index)\n }\n\n /// Retrieves an element from the vector at the given index, starting from zero, without\n /// performing a bounds check.\n ///\n /// Since this function does not perform a bounds check on length before accessing the element,\n /// it is unsafe! Use at your own risk!\n ///\n /// Example:\n ///\n /// ```noir\n /// fn sum_of_first_three(v: BoundedVec) -> u32 {\n /// // Always ensure the length is larger than the largest\n /// // index passed to get_unchecked\n /// assert(v.len() > 2);\n /// let first = v.get_unchecked(0);\n /// let second = v.get_unchecked(1);\n /// let third = v.get_unchecked(2);\n /// first + second + third\n /// }\n /// ```\n pub fn get_unchecked(self, index: u32) -> T {\n self.storage[index]\n }\n\n /// Writes an element to the vector at the given index, starting from zero.\n ///\n /// If the given index is equal to or greater than the length of the vector, this will issue a constraint failure.\n ///\n /// Example:\n ///\n /// ```noir\n /// fn foo(v: BoundedVec) {\n /// let first = v.get(0);\n /// assert(first != 42);\n /// v.set(0, 42);\n /// let new_first = v.get(0);\n /// assert(new_first == 42);\n /// }\n /// ```\n pub fn set(&mut self, index: u32, value: T) {\n assert(index < self.len, \"Attempted to write past end of BoundedVec\");\n self.set_unchecked(index, value)\n }\n\n /// Writes an element to the vector at the given index, starting from zero, without performing a bounds check.\n ///\n /// Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk!\n ///\n /// Example:\n ///\n /// ```noir\n /// fn set_unchecked_example() {\n /// let mut vec: BoundedVec = BoundedVec::new();\n /// vec.extend_from_array([1, 2]);\n ///\n /// // Here we're safely writing within the valid range of `vec`\n /// // `vec` now has the value [42, 2]\n /// vec.set_unchecked(0, 42);\n ///\n /// // We can then safely read this value back out of `vec`.\n /// // Notice that we use the checked version of `get` which would prevent reading unsafe values.\n /// assert_eq(vec.get(0), 42);\n ///\n /// // We've now written past the end of `vec`.\n /// // As this index is still within the maximum potential length of `v`,\n /// // it won't cause a constraint failure.\n /// vec.set_unchecked(2, 42);\n /// println(vec);\n ///\n /// // This will write past the end of the maximum potential length of `vec`,\n /// // it will then trigger a constraint failure.\n /// vec.set_unchecked(5, 42);\n /// println(vec);\n /// }\n /// ```\n pub fn set_unchecked(&mut self, index: u32, value: T) {\n self.storage[index] = value;\n }\n\n /// Pushes an element to the end of the vector. This increases the length\n /// of the vector by one.\n ///\n /// Panics if the new length of the vector will be greater than the max length.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n ///\n /// v.push(1);\n /// v.push(2);\n ///\n /// // Panics with failed assertion \"push out of bounds\"\n /// v.push(3);\n /// ```\n pub fn push(&mut self, elem: T) {\n assert(self.len < MaxLen, \"push out of bounds\");\n\n self.storage[self.len] = elem;\n self.len += 1;\n }\n\n /// Returns the current length of this vector\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n /// assert(v.len() == 0);\n ///\n /// v.push(100);\n /// assert(v.len() == 1);\n ///\n /// v.push(200);\n /// v.push(300);\n /// v.push(400);\n /// assert(v.len() == 4);\n ///\n /// let _ = v.pop();\n /// let _ = v.pop();\n /// assert(v.len() == 2);\n /// ```\n pub fn len(self) -> u32 {\n self.len\n }\n\n /// Returns the maximum length of this vector. This is always\n /// equal to the `MaxLen` parameter this vector was initialized with.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n ///\n /// assert(v.max_len() == 5);\n /// v.push(10);\n /// assert(v.max_len() == 5);\n /// ```\n pub fn max_len(_self: BoundedVec) -> u32 {\n MaxLen\n }\n\n /// Returns the internal array within this vector.\n ///\n /// Since arrays in Noir are immutable, mutating the returned storage array will not mutate\n /// the storage held internally by this vector.\n ///\n /// Note that uninitialized elements may be zeroed out!\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n ///\n /// assert(v.storage() == [0, 0, 0, 0, 0]);\n ///\n /// v.push(57);\n /// assert(v.storage() == [57, 0, 0, 0, 0]);\n /// ```\n pub fn storage(self) -> [T; MaxLen] {\n self.storage\n }\n\n /// Pushes each element from the given array to this vector.\n ///\n /// Panics if pushing each element would cause the length of this vector\n /// to exceed the maximum length.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut vec: BoundedVec = BoundedVec::new();\n /// vec.extend_from_array([2, 4]);\n ///\n /// assert(vec.len == 2);\n /// assert(vec.get(0) == 2);\n /// assert(vec.get(1) == 4);\n /// ```\n pub fn extend_from_array(&mut self, array: [T; Len]) {\n let new_len = self.len + array.len();\n assert(new_len <= MaxLen, \"extend_from_array out of bounds\");\n for i in 0..array.len() {\n self.storage[self.len + i] = array[i];\n }\n self.len = new_len;\n }\n\n /// Pushes each element from the given slice to this vector.\n ///\n /// Panics if pushing each element would cause the length of this vector\n /// to exceed the maximum length.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut vec: BoundedVec = BoundedVec::new();\n /// vec.extend_from_slice(&[2, 4]);\n ///\n /// assert(vec.len == 2);\n /// assert(vec.get(0) == 2);\n /// assert(vec.get(1) == 4);\n /// ```\n pub fn extend_from_slice(&mut self, slice: [T]) {\n let new_len = self.len + slice.len();\n assert(new_len <= MaxLen, \"extend_from_slice out of bounds\");\n for i in 0..slice.len() {\n self.storage[self.len + i] = slice[i];\n }\n self.len = new_len;\n }\n\n /// Pushes each element from the other vector to this vector. The length of\n /// the other vector is left unchanged.\n ///\n /// Panics if pushing each element would cause the length of this vector\n /// to exceed the maximum length.\n ///\n /// ```noir\n /// let mut v1: BoundedVec = BoundedVec::new();\n /// let mut v2: BoundedVec = BoundedVec::new();\n ///\n /// v2.extend_from_array([1, 2, 3]);\n /// v1.extend_from_bounded_vec(v2);\n ///\n /// assert(v1.storage() == [1, 2, 3, 0, 0]);\n /// assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]);\n /// ```\n pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) {\n let append_len = vec.len();\n let new_len = self.len + append_len;\n assert(new_len <= MaxLen, \"extend_from_bounded_vec out of bounds\");\n\n if is_unconstrained() {\n for i in 0..append_len {\n self.storage[self.len + i] = vec.get_unchecked(i);\n }\n } else {\n let mut exceeded_len = false;\n for i in 0..Len {\n exceeded_len |= i == append_len;\n if !exceeded_len {\n self.storage[self.len + i] = vec.get_unchecked(i);\n }\n }\n }\n self.len = new_len;\n }\n\n /// Creates a new vector, populating it with values derived from an array input.\n /// The maximum length of the vector is determined based on the type signature.\n ///\n /// Example:\n ///\n /// ```noir\n /// let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3])\n /// ```\n pub fn from_array(array: [T; Len]) -> Self {\n static_assert(Len <= MaxLen, \"from array out of bounds\");\n let mut vec: BoundedVec = BoundedVec::new();\n vec.extend_from_array(array);\n vec\n }\n\n /// Pops the element at the end of the vector. This will decrease the length\n /// of the vector by one.\n ///\n /// Panics if the vector is empty.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n /// v.push(1);\n /// v.push(2);\n ///\n /// let two = v.pop();\n /// let one = v.pop();\n ///\n /// assert(two == 2);\n /// assert(one == 1);\n ///\n /// // error: cannot pop from an empty vector\n /// let _ = v.pop();\n /// ```\n pub fn pop(&mut self) -> T {\n assert(self.len > 0);\n self.len -= 1;\n\n let elem = self.storage[self.len];\n self.storage[self.len] = crate::mem::zeroed();\n elem\n }\n\n /// Returns true if the given predicate returns true for any element\n /// in this vector.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n /// v.extend_from_array([2, 4, 6]);\n ///\n /// let all_even = !v.any(|elem: u32| elem % 2 != 0);\n /// assert(all_even);\n /// ```\n pub fn any(self, predicate: fn[Env](T) -> bool) -> bool {\n let mut ret = false;\n if is_unconstrained() {\n for i in 0..self.len {\n ret |= predicate(self.storage[i]);\n }\n } else {\n let mut exceeded_len = false;\n for i in 0..MaxLen {\n exceeded_len |= i == self.len;\n if !exceeded_len {\n ret |= predicate(self.storage[i]);\n }\n }\n }\n ret\n }\n\n /// Creates a new vector of equal size by calling a closure on each element in this vector.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let result = vec.map(|value| value * 2);\n ///\n /// let expected = BoundedVec::from_array([2, 4, 6, 8]);\n /// assert_eq(result, expected);\n /// ```\n pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec {\n let mut ret = BoundedVec::new();\n ret.len = self.len();\n\n if is_unconstrained() {\n for i in 0..self.len() {\n ret.storage[i] = f(self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n ret.storage[i] = f(self.get_unchecked(i));\n }\n }\n }\n\n ret\n }\n\n /// Creates a new vector of equal size by calling a closure on each element\n /// in this vector, along with its index.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let result = vec.mapi(|i, value| i + value * 2);\n ///\n /// let expected = BoundedVec::from_array([2, 5, 8, 11]);\n /// assert_eq(result, expected);\n /// ```\n pub fn mapi(self, f: fn[Env](u32, T) -> U) -> BoundedVec {\n let mut ret = BoundedVec::new();\n ret.len = self.len();\n\n if is_unconstrained() {\n for i in 0..self.len() {\n ret.storage[i] = f(i, self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n ret.storage[i] = f(i, self.get_unchecked(i));\n }\n }\n }\n\n ret\n }\n\n /// Calls a closure on each element in this vector.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let mut result = BoundedVec::::new();\n /// vec.for_each(|value| result.push(value * 2));\n ///\n /// let expected = BoundedVec::from_array([2, 4, 6, 8]);\n /// assert_eq(result, expected);\n /// ```\n pub fn for_each(self, f: fn[Env](T) -> ()) {\n if is_unconstrained() {\n for i in 0..self.len() {\n f(self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n f(self.get_unchecked(i));\n }\n }\n }\n }\n\n /// Calls a closure on each element in this vector, along with its index.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let mut result = BoundedVec::::new();\n /// vec.for_eachi(|i, value| result.push(i + value * 2));\n ///\n /// let expected = BoundedVec::from_array([2, 5, 8, 11]);\n /// assert_eq(result, expected);\n /// ```\n pub fn for_eachi(self, f: fn[Env](u32, T) -> ()) {\n if is_unconstrained() {\n for i in 0..self.len() {\n f(i, self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n f(i, self.get_unchecked(i));\n }\n }\n }\n }\n\n /// Creates a new BoundedVec from the given array and length.\n /// The given length must be less than or equal to the length of the array.\n ///\n /// This function will zero out any elements at or past index `len` of `array`.\n /// This incurs an extra runtime cost of O(MaxLen). If you are sure your array is\n /// zeroed after that index, you can use `from_parts_unchecked` to remove the extra loop.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_parts([1, 2, 3, 0], 3);\n /// assert_eq(vec.len(), 3);\n /// ```\n pub fn from_parts(mut array: [T; MaxLen], len: u32) -> Self {\n assert(len <= MaxLen);\n let zeroed = crate::mem::zeroed();\n\n if is_unconstrained() {\n for i in len..MaxLen {\n array[i] = zeroed;\n }\n } else {\n for i in 0..MaxLen {\n if i >= len {\n array[i] = zeroed;\n }\n }\n }\n\n BoundedVec { storage: array, len }\n }\n\n /// Creates a new BoundedVec from the given array and length.\n /// The given length must be less than or equal to the length of the array.\n ///\n /// This function is unsafe because it expects all elements past the `len` index\n /// of `array` to be zeroed, but does not check for this internally. Use `from_parts`\n /// for a safe version of this function which does zero out any indices past the\n /// given length. Invalidating this assumption can notably cause `BoundedVec::eq`\n /// to give incorrect results since it will check even elements past `len`.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 0], 3);\n /// assert_eq(vec.len(), 3);\n ///\n /// // invalid use!\n /// let vec1: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 1], 3);\n /// let vec2: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 2], 3);\n ///\n /// // both vecs have length 3 so we'd expect them to be equal, but this\n /// // fails because elements past the length are still checked in eq\n /// assert_eq(vec1, vec2); // fails\n /// ```\n pub fn from_parts_unchecked(array: [T; MaxLen], len: u32) -> Self {\n assert(len <= MaxLen);\n BoundedVec { storage: array, len }\n }\n}\n\nimpl Eq for BoundedVec\nwhere\n T: Eq,\n{\n fn eq(self, other: BoundedVec) -> bool {\n // TODO: https://github.com/noir-lang/noir/issues/4837\n //\n // We make the assumption that the user has used the proper interface for working with `BoundedVec`s\n // rather than directly manipulating the internal fields as this can result in an inconsistent internal state.\n if self.len == other.len {\n self.storage == other.storage\n } else {\n false\n }\n }\n}\n\nimpl From<[T; Len]> for BoundedVec {\n fn from(array: [T; Len]) -> BoundedVec {\n BoundedVec::from_array(array)\n }\n}\n\nmod bounded_vec_tests {\n\n mod get {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test(should_fail_with = \"Attempted to read past end of BoundedVec\")]\n fn panics_when_reading_elements_past_end_of_vec() {\n let vec: BoundedVec = BoundedVec::new();\n\n let _ = vec.get(0);\n }\n }\n\n mod set {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn set_updates_values_properly() {\n let mut vec = BoundedVec::from_array([0, 0, 0, 0, 0]);\n\n vec.set(0, 42);\n assert_eq(vec.storage, [42, 0, 0, 0, 0]);\n\n vec.set(1, 43);\n assert_eq(vec.storage, [42, 43, 0, 0, 0]);\n\n vec.set(2, 44);\n assert_eq(vec.storage, [42, 43, 44, 0, 0]);\n\n vec.set(1, 10);\n assert_eq(vec.storage, [42, 10, 44, 0, 0]);\n\n vec.set(0, 0);\n assert_eq(vec.storage, [0, 10, 44, 0, 0]);\n }\n\n #[test(should_fail_with = \"Attempted to write past end of BoundedVec\")]\n fn panics_when_writing_elements_past_end_of_vec() {\n let mut vec: BoundedVec = BoundedVec::new();\n vec.set(0, 42);\n }\n }\n\n mod any {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn returns_false_if_predicate_not_satisfied() {\n let vec: BoundedVec = BoundedVec::from_array([false, false, false, false]);\n let result = vec.any(|value| value);\n\n assert(!result);\n }\n\n #[test]\n fn returns_true_if_predicate_satisfied() {\n let vec: BoundedVec = BoundedVec::from_array([false, false, true, true]);\n let result = vec.any(|value| value);\n\n assert(result);\n }\n\n #[test]\n fn returns_false_on_empty_boundedvec() {\n let vec: BoundedVec = BoundedVec::new();\n let result = vec.any(|value| value);\n\n assert(!result);\n }\n }\n\n mod map {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn applies_function_correctly() {\n // docs:start:bounded-vec-map-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.map(|value| value * 2);\n // docs:end:bounded-vec-map-example\n let expected = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.map(|value| (value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = vec.map(|value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod mapi {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn applies_function_correctly() {\n // docs:start:bounded-vec-mapi-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.mapi(|i, value| i + value * 2);\n // docs:end:bounded-vec-mapi-example\n let expected = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.mapi(|i, value| (i + value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = vec.mapi(|_, value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod for_each {\n use crate::collections::bounded_vec::BoundedVec;\n\n // map in terms of for_each\n fn for_each_map(\n input: BoundedVec,\n f: fn[Env](T) -> U,\n ) -> BoundedVec {\n let mut output = BoundedVec::::new();\n let output_ref = &mut output;\n input.for_each(|x| output_ref.push(f(x)));\n output\n }\n\n #[test]\n fn smoke_test() {\n let mut acc = 0;\n let acc_ref = &mut acc;\n // docs:start:bounded-vec-for-each-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3]);\n vec.for_each(|value| { *acc_ref += value; });\n // docs:end:bounded-vec-for-each-example\n assert_eq(acc, 6);\n }\n\n #[test]\n fn applies_function_correctly() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_each_map(vec, |value| value * 2);\n let expected = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_each_map(vec, |value| (value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = for_each_map(vec, |value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod for_eachi {\n use crate::collections::bounded_vec::BoundedVec;\n\n // mapi in terms of for_eachi\n fn for_eachi_mapi(\n input: BoundedVec,\n f: fn[Env](u32, T) -> U,\n ) -> BoundedVec {\n let mut output = BoundedVec::::new();\n let output_ref = &mut output;\n input.for_eachi(|i, x| output_ref.push(f(i, x)));\n output\n }\n\n #[test]\n fn smoke_test() {\n let mut acc = 0;\n let acc_ref = &mut acc;\n // docs:start:bounded-vec-for-eachi-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3]);\n vec.for_eachi(|i, value| { *acc_ref += i * value; });\n // docs:end:bounded-vec-for-eachi-example\n\n // 0 * 1 + 1 * 2 + 2 * 3\n assert_eq(acc, 8);\n }\n\n #[test]\n fn applies_function_correctly() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_eachi_mapi(vec, |i, value| i + value * 2);\n let expected = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_eachi_mapi(vec, |i, value| (i + value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = for_eachi_mapi(vec, |_, value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod from_array {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn empty() {\n let empty_array: [Field; 0] = [];\n let bounded_vec = BoundedVec::from_array([]);\n\n assert_eq(bounded_vec.max_len(), 0);\n assert_eq(bounded_vec.len(), 0);\n assert_eq(bounded_vec.storage(), empty_array);\n }\n\n #[test]\n fn equal_len() {\n let array = [1, 2, 3];\n let bounded_vec = BoundedVec::from_array(array);\n\n assert_eq(bounded_vec.max_len(), 3);\n assert_eq(bounded_vec.len(), 3);\n assert_eq(bounded_vec.storage(), array);\n }\n\n #[test]\n fn max_len_greater_then_array_len() {\n let array = [1, 2, 3];\n let bounded_vec: BoundedVec = BoundedVec::from_array(array);\n\n assert_eq(bounded_vec.max_len(), 10);\n assert_eq(bounded_vec.len(), 3);\n assert_eq(bounded_vec.get(0), 1);\n assert_eq(bounded_vec.get(1), 2);\n assert_eq(bounded_vec.get(2), 3);\n }\n\n #[test(should_fail_with = \"from array out of bounds\")]\n fn max_len_lower_then_array_len() {\n let _: BoundedVec = BoundedVec::from_array([0; 3]);\n }\n }\n\n mod trait_from {\n use crate::collections::bounded_vec::BoundedVec;\n use crate::convert::From;\n\n #[test]\n fn simple() {\n let array = [1, 2];\n let bounded_vec: BoundedVec = BoundedVec::from(array);\n\n assert_eq(bounded_vec.max_len(), 10);\n assert_eq(bounded_vec.len(), 2);\n assert_eq(bounded_vec.get(0), 1);\n assert_eq(bounded_vec.get(1), 2);\n }\n }\n\n mod trait_eq {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn empty_equality() {\n let mut bounded_vec1: BoundedVec = BoundedVec::new();\n let mut bounded_vec2: BoundedVec = BoundedVec::new();\n\n assert_eq(bounded_vec1, bounded_vec2);\n }\n\n #[test]\n fn inequality() {\n let mut bounded_vec1: BoundedVec = BoundedVec::new();\n let mut bounded_vec2: BoundedVec = BoundedVec::new();\n bounded_vec1.push(1);\n bounded_vec2.push(2);\n\n assert(bounded_vec1 != bounded_vec2);\n }\n }\n\n mod from_parts {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn from_parts() {\n // docs:start:from-parts\n let vec: BoundedVec = BoundedVec::from_parts([1, 2, 3, 0], 3);\n assert_eq(vec.len(), 3);\n\n // Any elements past the given length are zeroed out, so these\n // two BoundedVecs will be completely equal\n let vec1: BoundedVec = BoundedVec::from_parts([1, 2, 3, 1], 3);\n let vec2: BoundedVec = BoundedVec::from_parts([1, 2, 3, 2], 3);\n assert_eq(vec1, vec2);\n // docs:end:from-parts\n }\n\n #[test]\n fn from_parts_unchecked() {\n // docs:start:from-parts-unchecked\n let vec: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 0], 3);\n assert_eq(vec.len(), 3);\n\n // invalid use!\n let vec1: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 1], 3);\n let vec2: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 2], 3);\n\n // both vecs have length 3 so we'd expect them to be equal, but this\n // fails because elements past the length are still checked in eq\n assert(vec1 != vec2);\n // docs:end:from-parts-unchecked\n }\n }\n}\n", diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/brillig_rc_regression_6123/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap b/tooling/nargo_cli/tests/snapshots/execution_success/brillig_rc_regression_6123/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap index b82834aa3a3..38491e75ecc 100644 --- a/tooling/nargo_cli/tests/snapshots/execution_success/brillig_rc_regression_6123/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap +++ b/tooling/nargo_cli/tests/snapshots/execution_success/brillig_rc_regression_6123/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap @@ -35,9 +35,9 @@ expression: artifact "return value indices : []", "BRILLIG CALL func 0: inputs: [], outputs: []", "unconstrained func 0", - "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 0 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32838), size_address: Relative(1), offset_address: Relative(2) }, Call { location: 11 }, Call { location: 15 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Const { destination: Direct(32835), bit_size: Integer(U32), value: 0 }, Const { destination: Direct(32836), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(32837), bit_size: Integer(U32), value: 2 }, Return, Call { location: 66 }, Const { destination: Relative(1), bit_size: Field, value: 0 }, Mov { destination: Relative(2), source: Direct(1) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 3 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(3) }, IndirectConst { destination_pointer: Relative(2), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, Mov { destination: Relative(4), source: Relative(3) }, Store { destination_pointer: Relative(4), source: Relative(1) }, BinaryIntOp { destination: Relative(4), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(1) }, Load { destination: Relative(1), source_pointer: Relative(2) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(4), op: Equals, bit_size: U32, lhs: Relative(3), rhs: Relative(1) }, Not { destination: Relative(4), source: Relative(4), bit_size: U1 }, JumpIf { condition: Relative(4), location: 32 }, Call { location: 72 }, BinaryIntOp { destination: Relative(1), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(1) }, Mov { destination: Relative(1), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(1), source: Relative(2) }, Mov { destination: Relative(4), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Direct(32835) }, Mov { destination: Relative(5), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(2) }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Direct(32835) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 7 }, Mov { destination: Relative(7), source: Direct(0) }, Mov { destination: Relative(8), source: Relative(1) }, Mov { destination: Relative(9), source: Relative(4) }, Mov { destination: Relative(10), source: Relative(5) }, Mov { destination: Relative(11), source: Relative(2) }, Mov { destination: Relative(12), source: Direct(32837) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(6) }, Call { location: 75 }, Mov { destination: Direct(0), source: Relative(0) }, Load { destination: Relative(2), source_pointer: Relative(1) }, Load { destination: Relative(1), source_pointer: Relative(2) }, Const { destination: Relative(4), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(5), op: Equals, bit_size: U32, lhs: Relative(4), rhs: Relative(1) }, Not { destination: Relative(5), source: Relative(5), bit_size: U1 }, JumpIf { condition: Relative(5), location: 63 }, Call { location: 72 }, BinaryIntOp { destination: Relative(1), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(1) }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 71 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 12049594436772143978 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 66 }, Load { destination: Relative(7), source_pointer: Relative(1) }, Load { destination: Relative(8), source_pointer: Relative(2) }, Load { destination: Relative(9), source_pointer: Relative(7) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(11), op: Equals, bit_size: U32, lhs: Relative(10), rhs: Relative(9) }, Not { destination: Relative(11), source: Relative(11), bit_size: U1 }, JumpIf { condition: Relative(11), location: 84 }, Call { location: 72 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Relative(9) }, Mov { destination: Relative(6), source: Direct(32835) }, Jump { location: 88 }, BinaryIntOp { destination: Relative(3), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Direct(32837) }, JumpIf { condition: Relative(3), location: 92 }, Jump { location: 91 }, Return, BinaryIntOp { destination: Relative(3), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Relative(5) }, JumpIf { condition: Relative(3), location: 95 }, Jump { location: 109 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, BinaryIntOp { destination: Relative(4), op: LessThanEquals, bit_size: U32, lhs: Relative(8), rhs: Relative(3) }, JumpIf { condition: Relative(4), location: 99 }, Call { location: 112 }, Cast { destination: Relative(4), source: Relative(3), bit_size: Field }, Const { destination: Relative(3), bit_size: Integer(U32), value: 9 }, Mov { destination: Relative(9), source: Direct(0) }, Mov { destination: Relative(10), source: Relative(1) }, Mov { destination: Relative(11), source: Relative(2) }, Mov { destination: Relative(12), source: Relative(4) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(3) }, Call { location: 115 }, Mov { destination: Direct(0), source: Relative(0) }, Jump { location: 109 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(32836) }, Mov { destination: Relative(6), source: Relative(3) }, Jump { location: 88 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 5019202896831570965 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 66 }, Load { destination: Relative(4), source_pointer: Relative(2) }, BinaryIntOp { destination: Relative(5), op: LessThan, bit_size: U32, lhs: Relative(4), rhs: Direct(32837) }, Const { destination: Relative(6), bit_size: Integer(U1), value: 1 }, JumpIf { condition: Relative(5), location: 121 }, Call { location: 136 }, Load { destination: Relative(5), source_pointer: Relative(1) }, Mov { destination: Direct(32771), source: Relative(5) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 3 }, Call { location: 139 }, Mov { destination: Relative(6), source: Direct(32773) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(2) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(7), rhs: Relative(4) }, Store { destination_pointer: Relative(8), source: Relative(3) }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(32836) }, BinaryIntOp { destination: Relative(5), op: LessThanEquals, bit_size: U32, lhs: Relative(4), rhs: Relative(3) }, JumpIf { condition: Relative(5), location: 133 }, Call { location: 112 }, Store { destination_pointer: Relative(1), source: Relative(6) }, Store { destination_pointer: Relative(2), source: Relative(3) }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 5727012404371710682 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Load { destination: Direct(32774), source_pointer: Direct(32771) }, BinaryIntOp { destination: Direct(32775), op: Equals, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, JumpIf { condition: Direct(32775), location: 143 }, Jump { location: 145 }, Mov { destination: Direct(32773), source: Direct(32771) }, Jump { location: 160 }, Mov { destination: Direct(32773), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(32772) }, BinaryIntOp { destination: Direct(32777), op: Add, bit_size: U32, lhs: Direct(32771), rhs: Direct(32772) }, Mov { destination: Direct(32778), source: Direct(32771) }, Mov { destination: Direct(32779), source: Direct(32773) }, BinaryIntOp { destination: Direct(32780), op: Equals, bit_size: U32, lhs: Direct(32778), rhs: Direct(32777) }, JumpIf { condition: Direct(32780), location: 157 }, Load { destination: Direct(32776), source_pointer: Direct(32778) }, Store { destination_pointer: Direct(32779), source: Direct(32776) }, BinaryIntOp { destination: Direct(32778), op: Add, bit_size: U32, lhs: Direct(32778), rhs: Direct(2) }, BinaryIntOp { destination: Direct(32779), op: Add, bit_size: U32, lhs: Direct(32779), rhs: Direct(2) }, Jump { location: 150 }, IndirectConst { destination_pointer: Direct(32773), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Direct(32774), op: Sub, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, Jump { location: 160 }, Return]" + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 0 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32838), size_address: Relative(1), offset_address: Relative(2) }, Call { location: 11 }, Call { location: 15 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Const { destination: Direct(32835), bit_size: Integer(U32), value: 0 }, Const { destination: Direct(32836), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(32837), bit_size: Integer(U32), value: 2 }, Return, Call { location: 112 }, Const { destination: Relative(1), bit_size: Field, value: 0 }, Mov { destination: Relative(2), source: Direct(1) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 3 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(3) }, IndirectConst { destination_pointer: Relative(2), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, Mov { destination: Relative(4), source: Relative(3) }, Store { destination_pointer: Relative(4), source: Relative(1) }, BinaryIntOp { destination: Relative(4), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(1) }, Load { destination: Relative(1), source_pointer: Relative(2) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(4), op: Equals, bit_size: U32, lhs: Relative(3), rhs: Relative(1) }, Not { destination: Relative(4), source: Relative(4), bit_size: U1 }, JumpIf { condition: Relative(4), location: 32 }, Call { location: 118 }, BinaryIntOp { destination: Relative(1), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(1) }, Mov { destination: Relative(1), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(1), source: Relative(2) }, Mov { destination: Relative(4), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Direct(32835) }, Mov { destination: Relative(5), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(2) }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Direct(32835) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 7 }, Mov { destination: Relative(7), source: Direct(0) }, Mov { destination: Relative(8), source: Relative(1) }, Mov { destination: Relative(9), source: Relative(4) }, Mov { destination: Relative(10), source: Relative(5) }, Mov { destination: Relative(11), source: Relative(2) }, Mov { destination: Relative(12), source: Direct(32837) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(6) }, Call { location: 121 }, Mov { destination: Direct(0), source: Relative(0) }, Load { destination: Relative(6), source_pointer: Relative(5) }, Load { destination: Relative(7), source_pointer: Relative(2) }, Const { destination: Relative(8), bit_size: Field, value: 27 }, Mov { destination: Direct(32771), source: Relative(6) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 3 }, Call { location: 158 }, Mov { destination: Relative(9), source: Direct(32773) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(32837) }, Store { destination_pointer: Relative(10), source: Relative(8) }, Store { destination_pointer: Relative(5), source: Relative(9) }, Store { destination_pointer: Relative(2), source: Relative(7) }, Load { destination: Relative(2), source_pointer: Relative(1) }, Load { destination: Relative(5), source_pointer: Relative(2) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(7), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(5) }, Not { destination: Relative(7), source: Relative(7), bit_size: U1 }, JumpIf { condition: Relative(7), location: 74 }, Call { location: 118 }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(5) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(32836) }, Load { destination: Relative(5), source_pointer: Relative(7) }, Load { destination: Relative(7), source_pointer: Relative(2) }, Const { destination: Relative(8), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(7) }, Not { destination: Relative(9), source: Relative(9), bit_size: U1 }, JumpIf { condition: Relative(9), location: 84 }, Call { location: 118 }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(7) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(32837) }, Load { destination: Relative(7), source_pointer: Relative(9) }, Load { destination: Relative(9), source_pointer: Relative(2) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(11), op: Equals, bit_size: U32, lhs: Relative(10), rhs: Relative(9) }, Not { destination: Relative(11), source: Relative(11), bit_size: U1 }, JumpIf { condition: Relative(11), location: 94 }, Call { location: 118 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(9) }, Load { destination: Relative(9), source_pointer: Relative(4) }, Mov { destination: Direct(32771), source: Relative(2) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 3 }, Call { location: 158 }, Mov { destination: Relative(11), source: Direct(32773) }, BinaryIntOp { destination: Relative(12), op: Add, bit_size: U32, lhs: Relative(11), rhs: Direct(32837) }, Store { destination_pointer: Relative(12), source: Relative(5) }, Mov { destination: Direct(32771), source: Relative(11) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 3 }, Call { location: 158 }, Mov { destination: Relative(2), source: Direct(32773) }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(32836) }, Store { destination_pointer: Relative(5), source: Relative(7) }, Store { destination_pointer: Relative(1), source: Relative(2) }, Store { destination_pointer: Relative(4), source: Relative(9) }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 117 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 12049594436772143978 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 112 }, Load { destination: Relative(7), source_pointer: Relative(1) }, Load { destination: Relative(8), source_pointer: Relative(2) }, Load { destination: Relative(9), source_pointer: Relative(7) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(11), op: Equals, bit_size: U32, lhs: Relative(10), rhs: Relative(9) }, Not { destination: Relative(11), source: Relative(11), bit_size: U1 }, JumpIf { condition: Relative(11), location: 130 }, Call { location: 118 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Relative(9) }, Mov { destination: Relative(6), source: Direct(32835) }, Jump { location: 134 }, BinaryIntOp { destination: Relative(3), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Direct(32837) }, JumpIf { condition: Relative(3), location: 138 }, Jump { location: 137 }, Return, BinaryIntOp { destination: Relative(3), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Relative(5) }, JumpIf { condition: Relative(3), location: 141 }, Jump { location: 155 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, BinaryIntOp { destination: Relative(4), op: LessThanEquals, bit_size: U32, lhs: Relative(8), rhs: Relative(3) }, JumpIf { condition: Relative(4), location: 145 }, Call { location: 180 }, Cast { destination: Relative(4), source: Relative(3), bit_size: Field }, Const { destination: Relative(3), bit_size: Integer(U32), value: 9 }, Mov { destination: Relative(9), source: Direct(0) }, Mov { destination: Relative(10), source: Relative(1) }, Mov { destination: Relative(11), source: Relative(2) }, Mov { destination: Relative(12), source: Relative(4) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(3) }, Call { location: 183 }, Mov { destination: Direct(0), source: Relative(0) }, Jump { location: 155 }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(32836) }, Mov { destination: Relative(6), source: Relative(3) }, Jump { location: 134 }, Load { destination: Direct(32774), source_pointer: Direct(32771) }, BinaryIntOp { destination: Direct(32775), op: Equals, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, JumpIf { condition: Direct(32775), location: 162 }, Jump { location: 164 }, Mov { destination: Direct(32773), source: Direct(32771) }, Jump { location: 179 }, Mov { destination: Direct(32773), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(32772) }, BinaryIntOp { destination: Direct(32777), op: Add, bit_size: U32, lhs: Direct(32771), rhs: Direct(32772) }, Mov { destination: Direct(32778), source: Direct(32771) }, Mov { destination: Direct(32779), source: Direct(32773) }, BinaryIntOp { destination: Direct(32780), op: Equals, bit_size: U32, lhs: Direct(32778), rhs: Direct(32777) }, JumpIf { condition: Direct(32780), location: 176 }, Load { destination: Direct(32776), source_pointer: Direct(32778) }, Store { destination_pointer: Direct(32779), source: Direct(32776) }, BinaryIntOp { destination: Direct(32778), op: Add, bit_size: U32, lhs: Direct(32778), rhs: Direct(2) }, BinaryIntOp { destination: Direct(32779), op: Add, bit_size: U32, lhs: Direct(32779), rhs: Direct(2) }, Jump { location: 169 }, IndirectConst { destination_pointer: Direct(32773), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Direct(32774), op: Sub, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, Jump { location: 179 }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 5019202896831570965 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 112 }, Load { destination: Relative(4), source_pointer: Relative(2) }, BinaryIntOp { destination: Relative(5), op: LessThan, bit_size: U32, lhs: Relative(4), rhs: Direct(32837) }, Const { destination: Relative(6), bit_size: Integer(U1), value: 1 }, JumpIf { condition: Relative(5), location: 189 }, Call { location: 204 }, Load { destination: Relative(5), source_pointer: Relative(1) }, Mov { destination: Direct(32771), source: Relative(5) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 3 }, Call { location: 158 }, Mov { destination: Relative(6), source: Direct(32773) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(2) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(7), rhs: Relative(4) }, Store { destination_pointer: Relative(8), source: Relative(3) }, BinaryIntOp { destination: Relative(3), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(32836) }, BinaryIntOp { destination: Relative(5), op: LessThanEquals, bit_size: U32, lhs: Relative(4), rhs: Relative(3) }, JumpIf { condition: Relative(5), location: 201 }, Call { location: 180 }, Store { destination_pointer: Relative(1), source: Relative(6) }, Store { destination_pointer: Relative(2), source: Relative(3) }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 5727012404371710682 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" ], - "debug_symbols": "tZbdbqMwEIXfhWsuPB7/jPsqVVXRhFRIiEQ0WWkV5d13hvGkzQWrLave8B1jzmGwB8G12fdvl/fXYTocP5qn52vzNg/jOLy/jsdddx6OE5+9Nk4OkJon3zaQFaQoC7xTgMIrUBEUUcEpgZEVpCgL0ClA4RWo4BRkREVSZAUpyoLgFKDwClRoStCUwCmRkRWkKAuiU4DCK1ARFFHBKYnBKcQgRVmQnAIUXoGKsCCzvTCygu3gmEVJrhIqfSVWhspYWf1FrgOmnOdlL6kyV1Kl3IeXApzcKIoAE94EmggmoolkIpuQQN4vkHaBJEKmsggyUaqQtlEBJuSmJAJNBBPRRDKRTZAJSeZlBGklFWDCm0ATklxut7axZn89z30vvf6l+/mdOHVzP52bp+kyjm3zqxsvy0Ufp25aeO5mnuVV76c9kwMPw9iLurWfbrduJYRqpkB3e3z0w7rfZ+drgM/e3xPSPxeQzE+5rBWAP1kAUfUXH9YK+Iu/IJo/xQ1+gGBbwH2Kawl5PSH5UANSgA0VJHTFAtDT95fQRyvAk9tQgC+2BQh5gx+TvQGY0xZ/tOdH2uTHZP64/hKF9YBA9gDRbdnB//Unuu9gogzf74BUXLSA4vOWgHCvoMSHgBcedLthfvgxuEnUPHRvY1+Hh8u0+zJ7/n2yGfuxOM3HXb+/zL0kff5d8OEZsLSQ3It8RniYUptBBrDMpRaQXm5Syh8=", + "debug_symbols": "tZfLbuJAEEX/xWsWXdXv/EoURYQ4EZIFyIGRRhH/PlW+XU5YGGVA2XCOMXXd7i638Gf32r+c3p+3u7f9R/fw+Nm9jNth2L4/D/vN+rjd7+Tbz87pB6XugVcdZaAAdQI7gAAGPBCACEhKEGSgAHWCdwABDHhAUrwgAgnIQAHqhOAAAhjwAFICUoKkREEGClAnRAcQwIAHAhABScmCDEhKEdQJyQEEMOCBAEQgAZKSBAWoE7IDCGDAAwGIgKRUQQYkhUhYweIaqZEbfWNojI2SRjJPJTdqnqxhqWB1jdTIjb4xNMZGzZMZq5ono62lsYLknAmZsIlmFpVgEk2SSTYpJrUJuSbam1RVvIn2p1OJJskkmxST2kR7FUImlqPtyDJ9pC3I+pBoE0K8STDRi3oVvWhUySbFpDbRpoSQCZt4Ew0M+oDqJXTqdDk5qySTbFJMahNdU9aJ0kWFsIk3CSbRJJloss6qri2kQlgXF0ImmlzP51Vnu83zcex73Wy+bT+yKR3WY787dg+70zCsuj/r4TT96OOw3k08rkc5K3Pf716FEvi2HXq18+qr2i2XFk+tuIQyl8fLelqu5+y4BXBmnhPSjweQrL7kujQA/5sDKKXVVw5LA7hSX723+hSX6tNyfZCOaQGBKS/cwJWA5F1tAclz+f8ZkD0itgAiqjfMAckTPycUv9hH1yJYn11EcAq3RKQ5IQVaDOB7l+LqEOK8FDktDiHcPYRwbztcu4nM1tE5uMWbyL/YkXcHcLRW4OJuaGieV8F/W4Sf1/tke7Jf7oNr9XMf+XJTvU9WHxe3db7Wh8VuIDq6YQD31qcyr2AqmW5ooTpvaqlyviUgzCOo8SLgSQ7Wm+148a5w1qhxu34Z+nb4dtptvp09/j3YGXvXOIz7Tf96GntN+nrhkI9HimVFuT7pfzk9lH2LKOuh/Il4ZBdW7NLTWQfzDw==", "file_map": { "6": { "source": "use crate::{cmp::Eq, convert::From, runtime::is_unconstrained, static_assert};\n\n/// A `BoundedVec` is a growable storage similar to a `Vec` except that it\n/// is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented\n/// via slices and thus is not subject to the same restrictions slices are (notably, nested\n/// slices - and thus nested vectors as well - are disallowed).\n///\n/// Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by\n/// pushing an additional element is also more efficient - the length only needs to be increased\n/// by one.\n///\n/// For these reasons `BoundedVec` should generally be preferred over `Vec` when there\n/// is a reasonable maximum bound that can be placed on the vector.\n///\n/// Example:\n///\n/// ```noir\n/// let mut vector: BoundedVec = BoundedVec::new();\n/// for i in 0..5 {\n/// vector.push(i);\n/// }\n/// assert(vector.len() == 5);\n/// assert(vector.max_len() == 10);\n/// ```\npub struct BoundedVec {\n storage: [T; MaxLen],\n len: u32,\n}\n\nimpl BoundedVec {\n /// Creates a new, empty vector of length zero.\n ///\n /// Since this container is backed by an array internally, it still needs an initial value\n /// to give each element. To resolve this, each element is zeroed internally. This value\n /// is guaranteed to be inaccessible unless `get_unchecked` is used.\n ///\n /// Example:\n ///\n /// ```noir\n /// let empty_vector: BoundedVec = BoundedVec::new();\n /// assert(empty_vector.len() == 0);\n /// ```\n ///\n /// Note that whenever calling `new` the maximum length of the vector should always be specified\n /// via a type signature:\n ///\n /// ```noir\n /// fn good() -> BoundedVec {\n /// // Ok! MaxLen is specified with a type annotation\n /// let v1: BoundedVec = BoundedVec::new();\n /// let v2 = BoundedVec::new();\n ///\n /// // Ok! MaxLen is known from the type of `good`'s return value\n /// v2\n /// }\n ///\n /// fn bad() {\n /// // Error: Type annotation needed\n /// // The compiler can't infer `MaxLen` from the following code:\n /// let mut v3 = BoundedVec::new();\n /// v3.push(5);\n /// }\n /// ```\n ///\n /// This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions\n /// but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a\n /// constraint failure at runtime when the vec is pushed to.\n pub fn new() -> Self {\n let zeroed = crate::mem::zeroed();\n BoundedVec { storage: [zeroed; MaxLen], len: 0 }\n }\n\n /// Retrieves an element from the vector at the given index, starting from zero.\n ///\n /// If the given index is equal to or greater than the length of the vector, this\n /// will issue a constraint failure.\n ///\n /// Example:\n ///\n /// ```noir\n /// fn foo(v: BoundedVec) {\n /// let first = v.get(0);\n /// let last = v.get(v.len() - 1);\n /// assert(first != last);\n /// }\n /// ```\n pub fn get(self, index: u32) -> T {\n assert(index < self.len, \"Attempted to read past end of BoundedVec\");\n self.get_unchecked(index)\n }\n\n /// Retrieves an element from the vector at the given index, starting from zero, without\n /// performing a bounds check.\n ///\n /// Since this function does not perform a bounds check on length before accessing the element,\n /// it is unsafe! Use at your own risk!\n ///\n /// Example:\n ///\n /// ```noir\n /// fn sum_of_first_three(v: BoundedVec) -> u32 {\n /// // Always ensure the length is larger than the largest\n /// // index passed to get_unchecked\n /// assert(v.len() > 2);\n /// let first = v.get_unchecked(0);\n /// let second = v.get_unchecked(1);\n /// let third = v.get_unchecked(2);\n /// first + second + third\n /// }\n /// ```\n pub fn get_unchecked(self, index: u32) -> T {\n self.storage[index]\n }\n\n /// Writes an element to the vector at the given index, starting from zero.\n ///\n /// If the given index is equal to or greater than the length of the vector, this will issue a constraint failure.\n ///\n /// Example:\n ///\n /// ```noir\n /// fn foo(v: BoundedVec) {\n /// let first = v.get(0);\n /// assert(first != 42);\n /// v.set(0, 42);\n /// let new_first = v.get(0);\n /// assert(new_first == 42);\n /// }\n /// ```\n pub fn set(&mut self, index: u32, value: T) {\n assert(index < self.len, \"Attempted to write past end of BoundedVec\");\n self.set_unchecked(index, value)\n }\n\n /// Writes an element to the vector at the given index, starting from zero, without performing a bounds check.\n ///\n /// Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk!\n ///\n /// Example:\n ///\n /// ```noir\n /// fn set_unchecked_example() {\n /// let mut vec: BoundedVec = BoundedVec::new();\n /// vec.extend_from_array([1, 2]);\n ///\n /// // Here we're safely writing within the valid range of `vec`\n /// // `vec` now has the value [42, 2]\n /// vec.set_unchecked(0, 42);\n ///\n /// // We can then safely read this value back out of `vec`.\n /// // Notice that we use the checked version of `get` which would prevent reading unsafe values.\n /// assert_eq(vec.get(0), 42);\n ///\n /// // We've now written past the end of `vec`.\n /// // As this index is still within the maximum potential length of `v`,\n /// // it won't cause a constraint failure.\n /// vec.set_unchecked(2, 42);\n /// println(vec);\n ///\n /// // This will write past the end of the maximum potential length of `vec`,\n /// // it will then trigger a constraint failure.\n /// vec.set_unchecked(5, 42);\n /// println(vec);\n /// }\n /// ```\n pub fn set_unchecked(&mut self, index: u32, value: T) {\n self.storage[index] = value;\n }\n\n /// Pushes an element to the end of the vector. This increases the length\n /// of the vector by one.\n ///\n /// Panics if the new length of the vector will be greater than the max length.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n ///\n /// v.push(1);\n /// v.push(2);\n ///\n /// // Panics with failed assertion \"push out of bounds\"\n /// v.push(3);\n /// ```\n pub fn push(&mut self, elem: T) {\n assert(self.len < MaxLen, \"push out of bounds\");\n\n self.storage[self.len] = elem;\n self.len += 1;\n }\n\n /// Returns the current length of this vector\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n /// assert(v.len() == 0);\n ///\n /// v.push(100);\n /// assert(v.len() == 1);\n ///\n /// v.push(200);\n /// v.push(300);\n /// v.push(400);\n /// assert(v.len() == 4);\n ///\n /// let _ = v.pop();\n /// let _ = v.pop();\n /// assert(v.len() == 2);\n /// ```\n pub fn len(self) -> u32 {\n self.len\n }\n\n /// Returns the maximum length of this vector. This is always\n /// equal to the `MaxLen` parameter this vector was initialized with.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n ///\n /// assert(v.max_len() == 5);\n /// v.push(10);\n /// assert(v.max_len() == 5);\n /// ```\n pub fn max_len(_self: BoundedVec) -> u32 {\n MaxLen\n }\n\n /// Returns the internal array within this vector.\n ///\n /// Since arrays in Noir are immutable, mutating the returned storage array will not mutate\n /// the storage held internally by this vector.\n ///\n /// Note that uninitialized elements may be zeroed out!\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n ///\n /// assert(v.storage() == [0, 0, 0, 0, 0]);\n ///\n /// v.push(57);\n /// assert(v.storage() == [57, 0, 0, 0, 0]);\n /// ```\n pub fn storage(self) -> [T; MaxLen] {\n self.storage\n }\n\n /// Pushes each element from the given array to this vector.\n ///\n /// Panics if pushing each element would cause the length of this vector\n /// to exceed the maximum length.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut vec: BoundedVec = BoundedVec::new();\n /// vec.extend_from_array([2, 4]);\n ///\n /// assert(vec.len == 2);\n /// assert(vec.get(0) == 2);\n /// assert(vec.get(1) == 4);\n /// ```\n pub fn extend_from_array(&mut self, array: [T; Len]) {\n let new_len = self.len + array.len();\n assert(new_len <= MaxLen, \"extend_from_array out of bounds\");\n for i in 0..array.len() {\n self.storage[self.len + i] = array[i];\n }\n self.len = new_len;\n }\n\n /// Pushes each element from the given slice to this vector.\n ///\n /// Panics if pushing each element would cause the length of this vector\n /// to exceed the maximum length.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut vec: BoundedVec = BoundedVec::new();\n /// vec.extend_from_slice(&[2, 4]);\n ///\n /// assert(vec.len == 2);\n /// assert(vec.get(0) == 2);\n /// assert(vec.get(1) == 4);\n /// ```\n pub fn extend_from_slice(&mut self, slice: [T]) {\n let new_len = self.len + slice.len();\n assert(new_len <= MaxLen, \"extend_from_slice out of bounds\");\n for i in 0..slice.len() {\n self.storage[self.len + i] = slice[i];\n }\n self.len = new_len;\n }\n\n /// Pushes each element from the other vector to this vector. The length of\n /// the other vector is left unchanged.\n ///\n /// Panics if pushing each element would cause the length of this vector\n /// to exceed the maximum length.\n ///\n /// ```noir\n /// let mut v1: BoundedVec = BoundedVec::new();\n /// let mut v2: BoundedVec = BoundedVec::new();\n ///\n /// v2.extend_from_array([1, 2, 3]);\n /// v1.extend_from_bounded_vec(v2);\n ///\n /// assert(v1.storage() == [1, 2, 3, 0, 0]);\n /// assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]);\n /// ```\n pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) {\n let append_len = vec.len();\n let new_len = self.len + append_len;\n assert(new_len <= MaxLen, \"extend_from_bounded_vec out of bounds\");\n\n if is_unconstrained() {\n for i in 0..append_len {\n self.storage[self.len + i] = vec.get_unchecked(i);\n }\n } else {\n let mut exceeded_len = false;\n for i in 0..Len {\n exceeded_len |= i == append_len;\n if !exceeded_len {\n self.storage[self.len + i] = vec.get_unchecked(i);\n }\n }\n }\n self.len = new_len;\n }\n\n /// Creates a new vector, populating it with values derived from an array input.\n /// The maximum length of the vector is determined based on the type signature.\n ///\n /// Example:\n ///\n /// ```noir\n /// let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3])\n /// ```\n pub fn from_array(array: [T; Len]) -> Self {\n static_assert(Len <= MaxLen, \"from array out of bounds\");\n let mut vec: BoundedVec = BoundedVec::new();\n vec.extend_from_array(array);\n vec\n }\n\n /// Pops the element at the end of the vector. This will decrease the length\n /// of the vector by one.\n ///\n /// Panics if the vector is empty.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n /// v.push(1);\n /// v.push(2);\n ///\n /// let two = v.pop();\n /// let one = v.pop();\n ///\n /// assert(two == 2);\n /// assert(one == 1);\n ///\n /// // error: cannot pop from an empty vector\n /// let _ = v.pop();\n /// ```\n pub fn pop(&mut self) -> T {\n assert(self.len > 0);\n self.len -= 1;\n\n let elem = self.storage[self.len];\n self.storage[self.len] = crate::mem::zeroed();\n elem\n }\n\n /// Returns true if the given predicate returns true for any element\n /// in this vector.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n /// v.extend_from_array([2, 4, 6]);\n ///\n /// let all_even = !v.any(|elem: u32| elem % 2 != 0);\n /// assert(all_even);\n /// ```\n pub fn any(self, predicate: fn[Env](T) -> bool) -> bool {\n let mut ret = false;\n if is_unconstrained() {\n for i in 0..self.len {\n ret |= predicate(self.storage[i]);\n }\n } else {\n let mut exceeded_len = false;\n for i in 0..MaxLen {\n exceeded_len |= i == self.len;\n if !exceeded_len {\n ret |= predicate(self.storage[i]);\n }\n }\n }\n ret\n }\n\n /// Creates a new vector of equal size by calling a closure on each element in this vector.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let result = vec.map(|value| value * 2);\n ///\n /// let expected = BoundedVec::from_array([2, 4, 6, 8]);\n /// assert_eq(result, expected);\n /// ```\n pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec {\n let mut ret = BoundedVec::new();\n ret.len = self.len();\n\n if is_unconstrained() {\n for i in 0..self.len() {\n ret.storage[i] = f(self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n ret.storage[i] = f(self.get_unchecked(i));\n }\n }\n }\n\n ret\n }\n\n /// Creates a new vector of equal size by calling a closure on each element\n /// in this vector, along with its index.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let result = vec.mapi(|i, value| i + value * 2);\n ///\n /// let expected = BoundedVec::from_array([2, 5, 8, 11]);\n /// assert_eq(result, expected);\n /// ```\n pub fn mapi(self, f: fn[Env](u32, T) -> U) -> BoundedVec {\n let mut ret = BoundedVec::new();\n ret.len = self.len();\n\n if is_unconstrained() {\n for i in 0..self.len() {\n ret.storage[i] = f(i, self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n ret.storage[i] = f(i, self.get_unchecked(i));\n }\n }\n }\n\n ret\n }\n\n /// Calls a closure on each element in this vector.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let mut result = BoundedVec::::new();\n /// vec.for_each(|value| result.push(value * 2));\n ///\n /// let expected = BoundedVec::from_array([2, 4, 6, 8]);\n /// assert_eq(result, expected);\n /// ```\n pub fn for_each(self, f: fn[Env](T) -> ()) {\n if is_unconstrained() {\n for i in 0..self.len() {\n f(self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n f(self.get_unchecked(i));\n }\n }\n }\n }\n\n /// Calls a closure on each element in this vector, along with its index.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let mut result = BoundedVec::::new();\n /// vec.for_eachi(|i, value| result.push(i + value * 2));\n ///\n /// let expected = BoundedVec::from_array([2, 5, 8, 11]);\n /// assert_eq(result, expected);\n /// ```\n pub fn for_eachi(self, f: fn[Env](u32, T) -> ()) {\n if is_unconstrained() {\n for i in 0..self.len() {\n f(i, self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n f(i, self.get_unchecked(i));\n }\n }\n }\n }\n\n /// Creates a new BoundedVec from the given array and length.\n /// The given length must be less than or equal to the length of the array.\n ///\n /// This function will zero out any elements at or past index `len` of `array`.\n /// This incurs an extra runtime cost of O(MaxLen). If you are sure your array is\n /// zeroed after that index, you can use `from_parts_unchecked` to remove the extra loop.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_parts([1, 2, 3, 0], 3);\n /// assert_eq(vec.len(), 3);\n /// ```\n pub fn from_parts(mut array: [T; MaxLen], len: u32) -> Self {\n assert(len <= MaxLen);\n let zeroed = crate::mem::zeroed();\n\n if is_unconstrained() {\n for i in len..MaxLen {\n array[i] = zeroed;\n }\n } else {\n for i in 0..MaxLen {\n if i >= len {\n array[i] = zeroed;\n }\n }\n }\n\n BoundedVec { storage: array, len }\n }\n\n /// Creates a new BoundedVec from the given array and length.\n /// The given length must be less than or equal to the length of the array.\n ///\n /// This function is unsafe because it expects all elements past the `len` index\n /// of `array` to be zeroed, but does not check for this internally. Use `from_parts`\n /// for a safe version of this function which does zero out any indices past the\n /// given length. Invalidating this assumption can notably cause `BoundedVec::eq`\n /// to give incorrect results since it will check even elements past `len`.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 0], 3);\n /// assert_eq(vec.len(), 3);\n ///\n /// // invalid use!\n /// let vec1: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 1], 3);\n /// let vec2: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 2], 3);\n ///\n /// // both vecs have length 3 so we'd expect them to be equal, but this\n /// // fails because elements past the length are still checked in eq\n /// assert_eq(vec1, vec2); // fails\n /// ```\n pub fn from_parts_unchecked(array: [T; MaxLen], len: u32) -> Self {\n assert(len <= MaxLen);\n BoundedVec { storage: array, len }\n }\n}\n\nimpl Eq for BoundedVec\nwhere\n T: Eq,\n{\n fn eq(self, other: BoundedVec) -> bool {\n // TODO: https://github.com/noir-lang/noir/issues/4837\n //\n // We make the assumption that the user has used the proper interface for working with `BoundedVec`s\n // rather than directly manipulating the internal fields as this can result in an inconsistent internal state.\n if self.len == other.len {\n self.storage == other.storage\n } else {\n false\n }\n }\n}\n\nimpl From<[T; Len]> for BoundedVec {\n fn from(array: [T; Len]) -> BoundedVec {\n BoundedVec::from_array(array)\n }\n}\n\nmod bounded_vec_tests {\n\n mod get {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test(should_fail_with = \"Attempted to read past end of BoundedVec\")]\n fn panics_when_reading_elements_past_end_of_vec() {\n let vec: BoundedVec = BoundedVec::new();\n\n let _ = vec.get(0);\n }\n }\n\n mod set {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn set_updates_values_properly() {\n let mut vec = BoundedVec::from_array([0, 0, 0, 0, 0]);\n\n vec.set(0, 42);\n assert_eq(vec.storage, [42, 0, 0, 0, 0]);\n\n vec.set(1, 43);\n assert_eq(vec.storage, [42, 43, 0, 0, 0]);\n\n vec.set(2, 44);\n assert_eq(vec.storage, [42, 43, 44, 0, 0]);\n\n vec.set(1, 10);\n assert_eq(vec.storage, [42, 10, 44, 0, 0]);\n\n vec.set(0, 0);\n assert_eq(vec.storage, [0, 10, 44, 0, 0]);\n }\n\n #[test(should_fail_with = \"Attempted to write past end of BoundedVec\")]\n fn panics_when_writing_elements_past_end_of_vec() {\n let mut vec: BoundedVec = BoundedVec::new();\n vec.set(0, 42);\n }\n }\n\n mod any {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn returns_false_if_predicate_not_satisfied() {\n let vec: BoundedVec = BoundedVec::from_array([false, false, false, false]);\n let result = vec.any(|value| value);\n\n assert(!result);\n }\n\n #[test]\n fn returns_true_if_predicate_satisfied() {\n let vec: BoundedVec = BoundedVec::from_array([false, false, true, true]);\n let result = vec.any(|value| value);\n\n assert(result);\n }\n\n #[test]\n fn returns_false_on_empty_boundedvec() {\n let vec: BoundedVec = BoundedVec::new();\n let result = vec.any(|value| value);\n\n assert(!result);\n }\n }\n\n mod map {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn applies_function_correctly() {\n // docs:start:bounded-vec-map-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.map(|value| value * 2);\n // docs:end:bounded-vec-map-example\n let expected = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.map(|value| (value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = vec.map(|value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod mapi {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn applies_function_correctly() {\n // docs:start:bounded-vec-mapi-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.mapi(|i, value| i + value * 2);\n // docs:end:bounded-vec-mapi-example\n let expected = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.mapi(|i, value| (i + value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = vec.mapi(|_, value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod for_each {\n use crate::collections::bounded_vec::BoundedVec;\n\n // map in terms of for_each\n fn for_each_map(\n input: BoundedVec,\n f: fn[Env](T) -> U,\n ) -> BoundedVec {\n let mut output = BoundedVec::::new();\n let output_ref = &mut output;\n input.for_each(|x| output_ref.push(f(x)));\n output\n }\n\n #[test]\n fn smoke_test() {\n let mut acc = 0;\n let acc_ref = &mut acc;\n // docs:start:bounded-vec-for-each-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3]);\n vec.for_each(|value| { *acc_ref += value; });\n // docs:end:bounded-vec-for-each-example\n assert_eq(acc, 6);\n }\n\n #[test]\n fn applies_function_correctly() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_each_map(vec, |value| value * 2);\n let expected = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_each_map(vec, |value| (value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = for_each_map(vec, |value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod for_eachi {\n use crate::collections::bounded_vec::BoundedVec;\n\n // mapi in terms of for_eachi\n fn for_eachi_mapi(\n input: BoundedVec,\n f: fn[Env](u32, T) -> U,\n ) -> BoundedVec {\n let mut output = BoundedVec::::new();\n let output_ref = &mut output;\n input.for_eachi(|i, x| output_ref.push(f(i, x)));\n output\n }\n\n #[test]\n fn smoke_test() {\n let mut acc = 0;\n let acc_ref = &mut acc;\n // docs:start:bounded-vec-for-eachi-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3]);\n vec.for_eachi(|i, value| { *acc_ref += i * value; });\n // docs:end:bounded-vec-for-eachi-example\n\n // 0 * 1 + 1 * 2 + 2 * 3\n assert_eq(acc, 8);\n }\n\n #[test]\n fn applies_function_correctly() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_eachi_mapi(vec, |i, value| i + value * 2);\n let expected = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_eachi_mapi(vec, |i, value| (i + value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = for_eachi_mapi(vec, |_, value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod from_array {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn empty() {\n let empty_array: [Field; 0] = [];\n let bounded_vec = BoundedVec::from_array([]);\n\n assert_eq(bounded_vec.max_len(), 0);\n assert_eq(bounded_vec.len(), 0);\n assert_eq(bounded_vec.storage(), empty_array);\n }\n\n #[test]\n fn equal_len() {\n let array = [1, 2, 3];\n let bounded_vec = BoundedVec::from_array(array);\n\n assert_eq(bounded_vec.max_len(), 3);\n assert_eq(bounded_vec.len(), 3);\n assert_eq(bounded_vec.storage(), array);\n }\n\n #[test]\n fn max_len_greater_then_array_len() {\n let array = [1, 2, 3];\n let bounded_vec: BoundedVec = BoundedVec::from_array(array);\n\n assert_eq(bounded_vec.max_len(), 10);\n assert_eq(bounded_vec.len(), 3);\n assert_eq(bounded_vec.get(0), 1);\n assert_eq(bounded_vec.get(1), 2);\n assert_eq(bounded_vec.get(2), 3);\n }\n\n #[test(should_fail_with = \"from array out of bounds\")]\n fn max_len_lower_then_array_len() {\n let _: BoundedVec = BoundedVec::from_array([0; 3]);\n }\n }\n\n mod trait_from {\n use crate::collections::bounded_vec::BoundedVec;\n use crate::convert::From;\n\n #[test]\n fn simple() {\n let array = [1, 2];\n let bounded_vec: BoundedVec = BoundedVec::from(array);\n\n assert_eq(bounded_vec.max_len(), 10);\n assert_eq(bounded_vec.len(), 2);\n assert_eq(bounded_vec.get(0), 1);\n assert_eq(bounded_vec.get(1), 2);\n }\n }\n\n mod trait_eq {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn empty_equality() {\n let mut bounded_vec1: BoundedVec = BoundedVec::new();\n let mut bounded_vec2: BoundedVec = BoundedVec::new();\n\n assert_eq(bounded_vec1, bounded_vec2);\n }\n\n #[test]\n fn inequality() {\n let mut bounded_vec1: BoundedVec = BoundedVec::new();\n let mut bounded_vec2: BoundedVec = BoundedVec::new();\n bounded_vec1.push(1);\n bounded_vec2.push(2);\n\n assert(bounded_vec1 != bounded_vec2);\n }\n }\n\n mod from_parts {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn from_parts() {\n // docs:start:from-parts\n let vec: BoundedVec = BoundedVec::from_parts([1, 2, 3, 0], 3);\n assert_eq(vec.len(), 3);\n\n // Any elements past the given length are zeroed out, so these\n // two BoundedVecs will be completely equal\n let vec1: BoundedVec = BoundedVec::from_parts([1, 2, 3, 1], 3);\n let vec2: BoundedVec = BoundedVec::from_parts([1, 2, 3, 2], 3);\n assert_eq(vec1, vec2);\n // docs:end:from-parts\n }\n\n #[test]\n fn from_parts_unchecked() {\n // docs:start:from-parts-unchecked\n let vec: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 0], 3);\n assert_eq(vec.len(), 3);\n\n // invalid use!\n let vec1: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 1], 3);\n let vec2: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 2], 3);\n\n // both vecs have length 3 so we'd expect them to be equal, but this\n // fails because elements past the length are still checked in eq\n assert(vec1 != vec2);\n // docs:end:from-parts-unchecked\n }\n }\n}\n", diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__expanded.snap new file mode 100644 index 00000000000..dd6479f585f --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__expanded.snap @@ -0,0 +1,18 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +unconstrained fn main(v2: u32) { + let v0: &mut Field = &mut 0_Field; + let v7: Field = foo(v0, v0, v2); + assert(v7 == 100_Field); +} + +unconstrained fn foo(v0: &mut Field, v1: &mut Field, v2: u32) -> Field { + let mut v3: [&mut Field; 1] = [v0]; + v3[v2] = v1; + let _v5: Field = *v0; + let v6: &mut Field = v3[v2]; + *v6 = 100_Field; + *v0 +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_0.snap new file mode 100644 index 00000000000..651b8ac033f --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_0.snap @@ -0,0 +1,55 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [ + { + "name": "v2", + "type": { + "kind": "integer", + "sign": "unsigned", + "width": 32 + }, + "visibility": "private" + } + ], + "return_type": null, + "error_types": { + "14225679739041873922": { + "error_kind": "string", + "string": "Index out of bounds" + }, + "17843811134343075018": { + "error_kind": "string", + "string": "Stack too deep" + } + } + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : [_0]", + "public parameters indices : []", + "return value indices : []", + "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", + "unconstrained func 0", + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 20 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 19 }, Call { location: 26 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 25 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + ], + "debug_symbols": "dZDdCoMwDIXfJde9aAs656sMkapRCqVKbQdD+u5L/dn0wpucpsl3AmeBDpsw1Nr24wzla4HGaWP0UJuxVV6Pln4X4KmIDErJQOSbPDYpSGJkcOzX3iGm9ZMB2U7KofVQ2mAMg7cyYV2aJ2VX9crRlDNA25GSYa8Npldkf5rfo/ljZwv5g7MrLe7pZ7HTgmcXvKJOtdpd8ojJyGnVGNzbPtj2NPWf6ZgceU5ubLELDpPTKVSqL8mZzCoKU6QmZ7KoYjr9BQ==", + "file_map": { + "50": { + "source": "unconstrained fn main(v2: u32) {\n let v0 = &mut 0;\n let v7 = foo(v0, v0, v2);\n assert_eq(v7, 100);\n}\n\nunconstrained fn foo(v0: &mut Field, v1: &mut Field, v2: u32) -> Field {\n let mut v3 = [v0];\n v3[v2] = v1;\n let _v5 = *v0;\n let v6 = v3[v2];\n *v6 = 100;\n *v0\n}\n", + "path": "" + } + }, + "names": [ + "main" + ], + "brillig_names": [ + "main" + ] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_9223372036854775807.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_9223372036854775807.snap new file mode 100644 index 00000000000..651b8ac033f --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_false_inliner_9223372036854775807.snap @@ -0,0 +1,55 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [ + { + "name": "v2", + "type": { + "kind": "integer", + "sign": "unsigned", + "width": 32 + }, + "visibility": "private" + } + ], + "return_type": null, + "error_types": { + "14225679739041873922": { + "error_kind": "string", + "string": "Index out of bounds" + }, + "17843811134343075018": { + "error_kind": "string", + "string": "Stack too deep" + } + } + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : [_0]", + "public parameters indices : []", + "return value indices : []", + "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", + "unconstrained func 0", + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 20 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 19 }, Call { location: 26 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 25 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + ], + "debug_symbols": "dZDdCoMwDIXfJde9aAs656sMkapRCqVKbQdD+u5L/dn0wpucpsl3AmeBDpsw1Nr24wzla4HGaWP0UJuxVV6Pln4X4KmIDErJQOSbPDYpSGJkcOzX3iGm9ZMB2U7KofVQ2mAMg7cyYV2aJ2VX9crRlDNA25GSYa8Npldkf5rfo/ljZwv5g7MrLe7pZ7HTgmcXvKJOtdpd8ojJyGnVGNzbPtj2NPWf6ZgceU5ubLELDpPTKVSqL8mZzCoKU6QmZ7KoYjr9BQ==", + "file_map": { + "50": { + "source": "unconstrained fn main(v2: u32) {\n let v0 = &mut 0;\n let v7 = foo(v0, v0, v2);\n assert_eq(v7, 100);\n}\n\nunconstrained fn foo(v0: &mut Field, v1: &mut Field, v2: u32) -> Field {\n let mut v3 = [v0];\n v3[v2] = v1;\n let _v5 = *v0;\n let v6 = v3[v2];\n *v6 = 100;\n *v0\n}\n", + "path": "" + } + }, + "names": [ + "main" + ], + "brillig_names": [ + "main" + ] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_0.snap new file mode 100644 index 00000000000..651b8ac033f --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_0.snap @@ -0,0 +1,55 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [ + { + "name": "v2", + "type": { + "kind": "integer", + "sign": "unsigned", + "width": 32 + }, + "visibility": "private" + } + ], + "return_type": null, + "error_types": { + "14225679739041873922": { + "error_kind": "string", + "string": "Index out of bounds" + }, + "17843811134343075018": { + "error_kind": "string", + "string": "Stack too deep" + } + } + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : [_0]", + "public parameters indices : []", + "return value indices : []", + "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", + "unconstrained func 0", + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 20 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 19 }, Call { location: 26 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 25 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + ], + "debug_symbols": "dZDdCoMwDIXfJde9aAs656sMkapRCqVKbQdD+u5L/dn0wpucpsl3AmeBDpsw1Nr24wzla4HGaWP0UJuxVV6Pln4X4KmIDErJQOSbPDYpSGJkcOzX3iGm9ZMB2U7KofVQ2mAMg7cyYV2aJ2VX9crRlDNA25GSYa8Npldkf5rfo/ljZwv5g7MrLe7pZ7HTgmcXvKJOtdpd8ojJyGnVGNzbPtj2NPWf6ZgceU5ubLELDpPTKVSqL8mZzCoKU6QmZ7KoYjr9BQ==", + "file_map": { + "50": { + "source": "unconstrained fn main(v2: u32) {\n let v0 = &mut 0;\n let v7 = foo(v0, v0, v2);\n assert_eq(v7, 100);\n}\n\nunconstrained fn foo(v0: &mut Field, v1: &mut Field, v2: u32) -> Field {\n let mut v3 = [v0];\n v3[v2] = v1;\n let _v5 = *v0;\n let v6 = v3[v2];\n *v6 = 100;\n *v0\n}\n", + "path": "" + } + }, + "names": [ + "main" + ], + "brillig_names": [ + "main" + ] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_9223372036854775807.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_9223372036854775807.snap new file mode 100644 index 00000000000..651b8ac033f --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9657/execute__tests__force_brillig_true_inliner_9223372036854775807.snap @@ -0,0 +1,55 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [ + { + "name": "v2", + "type": { + "kind": "integer", + "sign": "unsigned", + "width": 32 + }, + "visibility": "private" + } + ], + "return_type": null, + "error_types": { + "14225679739041873922": { + "error_kind": "string", + "string": "Index out of bounds" + }, + "17843811134343075018": { + "error_kind": "string", + "string": "Stack too deep" + } + } + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : [_0]", + "public parameters indices : []", + "return value indices : []", + "BRILLIG CALL func 0: inputs: [EXPR [ (1, _0) 0 ]], outputs: []", + "unconstrained func 0", + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(2), offset_address: Relative(3) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Call { location: 13 }, Call { location: 14 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 32837 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Stop { return_data: HeapVector { pointer: Relative(1), size: Relative(2) } }, Return, Call { location: 20 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(3), location: 19 }, Call { location: 26 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 25 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 14225679739041873922 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + ], + "debug_symbols": "dZDdCoMwDIXfJde9aAs656sMkapRCqVKbQdD+u5L/dn0wpucpsl3AmeBDpsw1Nr24wzla4HGaWP0UJuxVV6Pln4X4KmIDErJQOSbPDYpSGJkcOzX3iGm9ZMB2U7KofVQ2mAMg7cyYV2aJ2VX9crRlDNA25GSYa8Npldkf5rfo/ljZwv5g7MrLe7pZ7HTgmcXvKJOtdpd8ojJyGnVGNzbPtj2NPWf6ZgceU5ubLELDpPTKVSqL8mZzCoKU6QmZ7KoYjr9BQ==", + "file_map": { + "50": { + "source": "unconstrained fn main(v2: u32) {\n let v0 = &mut 0;\n let v7 = foo(v0, v0, v2);\n assert_eq(v7, 100);\n}\n\nunconstrained fn foo(v0: &mut Field, v1: &mut Field, v2: u32) -> Field {\n let mut v3 = [v0];\n v3[v2] = v1;\n let _v5 = *v0;\n let v6 = v3[v2];\n *v6 = 100;\n *v0\n}\n", + "path": "" + } + }, + "names": [ + "main" + ], + "brillig_names": [ + "main" + ] +} From f874d9aa2e40d4edb88bdd9cfe33080bd6660e76 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 28 Aug 2025 12:48:43 -0300 Subject: [PATCH 8/8] Use Expression::Other instead of Expression::ArrayElement --- compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 4b407105dbe..5cfcf51c3db 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -530,7 +530,7 @@ impl<'f> PerFunctionContext<'f> { // An expression for the array might already exist, so try to fetch it first let expression = references.expressions.get(&array).copied(); - let expression = expression.unwrap_or(Expression::ArrayElement(array)); + let expression = expression.unwrap_or(Expression::Other(array)); if let Some(aliases) = references.aliases.get_mut(&expression) { aliases.insert(result); @@ -553,7 +553,7 @@ impl<'f> PerFunctionContext<'f> { let array = *array; let expression = references.expressions.get(&array).copied(); - let expression = expression.unwrap_or(Expression::ArrayElement(array)); + let expression = expression.unwrap_or(Expression::Other(array)); let mut aliases = if let Some(aliases) = references.aliases.get_mut(&expression) {