Skip to content

fix: Consume correct number of fields when printing references#9579

Merged
aakoshh merged 2 commits intomasterfrom
af/9578-fix-print-ref
Aug 21, 2025
Merged

fix: Consume correct number of fields when printing references#9579
aakoshh merged 2 commits intomasterfrom
af/9578-fix-print-ref

Conversation

@aakoshh
Copy link
Contributor

@aakoshh aakoshh commented Aug 20, 2025

Description

Problem*

Resolves #9578

Summary*

Fixes decode_printable_value handling reference types to consume a number of fields not based on the type, but how a reference to the type is passed to the foreign call handler.

Additional Context

The code assumed that when it encounters a PrintableType::Reference { typ, .. } then it can decode a typ from the following fields in the iterator. Then, when it came to displaying the value, it printed either <<ref>> or <<mutable ref>>, the decoded value wasn't actually shown.

Being able to decode the referenced value may have been the original intention (although if that's the case I don't know why it would not have display it), but that's now what was we got in practice. Instead we got what appears to be the memory addresses in the Brillig VM, which is a number above 32,000. The number of these addresses depends on the type: an Array is represented by a single number, while a Slice is 2 numbers, a Tuple depends on its fields.

It is unclear to me whether this is intentional. I guess we could use the address for debug purposes, but an actual foreign call handler would probably find little value in it. Furthermore, it seems to me that we can't handle enums this way: if we can't tell the value of the tag, we don't know how many fields will be in the data, where the rest continues.

ACIR

Printing references doesn't seem to work at all in ACIR, because it would require passing references to Brillig, which is impossible. Instead we get a compile error such as this:

error: Could not resolve some references to the array. All references must be resolved at compile time
  ┌─ src/main.nr:2:18
  │
2 │     println(&mut 1);

Brillig

It is possible to print references in Brillig, and debug_vars::assign_var uses it directly to store a printable value, for example:

cargo run -q -p nargo_cli -- debug --force --silence-warnings
...
> next
At opcode 0:0.26 :: Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }
At src/main.nr:3:10
  1    fn main(mut a: u32) -> pub u32 {
  2        let b = &mut a;
  3 ->     *b = *b + 1;
  4        a
  5    }
> vars
main(mut a)
  a:UnsignedInteger { width: 32 } = 1
  b:Reference { typ: UnsignedInteger { width: 32 }, mutable: true } = <<mutable ref>>
> memory
0 = 3: u32
1 = 32839: u32
2 = 1: u32
4 = 1: field
5 = 32838: u32
6 = 0: u32
8 = 1: u32
32771 = true: u1
32772 = 30720: u32
32836 = 1: u32
32838 = 1: u32

Notice how b is shown as b:Reference { typ: UnsignedInteger { width: 32 }, mutable: true } = <<mutable ref>>, so we neither learn where b points nor what its current value is. I wonder if we could make it more useful by either displaying the address values, or actually passing the current value.

Example

Here's what the data used in the integration test looks like when we are trying to decode it:

// show(1, ());
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Unit, mutable: true }, Unit] }):
  args[0] = Single(1)
(1, <<mutable ref>>, ())

// show(2, 123);
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Field, mutable: true }, Field] }):
  args[0] = Single(2)
  args[1] = Single(32975)
  args[2] = Single(123)
(2, <<mutable ref>>, 0x7b)

// show(3, [1, 2, 3]);
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Array { length: 3, typ: Field }, mutable: true }, Array { length: 3, typ: Field }] }):
  args[0] = Single(3)
  args[1] = Single(33121)
  args[2] = Array([1, 2, 3])
(3, <<mutable ref>>, [0x01, 0x02, 0x03])

// show(4, (1, 2, 3));
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Tuple { types: [Field, Field, Field] }, mutable: true }, Tuple { types: [Field, Field, Field] }] }):
  args[0] = Single(4)
  args[1] = Single(33333)
  args[2] = Single(33334)
  args[3] = Single(33335)
  args[4] = Single(1)
  args[5] = Single(2)
  args[6] = Single(3)
(4, <<mutable ref>>, (0x01, 0x02, 0x03))

// show(5, [(1, 2), (3, 4)]);
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Array { length: 2, typ: Tuple { types: [Field, Field] } }, mutable: true }, Array { length: 2, typ: Tuple { types: [Field, Field] } }] }):
  args[0] = Single(5)
  args[1] = Single(33604)
  args[2] = Array([1, 2, 3, 4])
(5, <<mutable ref>>, [(0x01, 0x02), (0x03, 0x04)])

// show(6, ((1, 2), [3]));
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Tuple { types: [Tuple { types: [Field, Field] }, Array { length: 1, typ: Field }] }, mutable: true }, Tuple { types: [Tuple { types: [Field, Field] }, Array { length: 1, typ: Field }] }] }):
  args[0] = Single(6)
  args[1] = Single(33906)
  args[2] = Single(33907)
  args[3] = Single(33908)
  args[4] = Single(1)
  args[5] = Single(2)
  args[6] = Array([3])
(6, <<mutable ref>>, ((0x01, 0x02), [0x03]))

// show(7, "123");
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: String { length: 3 }, mutable: true }, String { length: 3 }] }):
  args[0] = Single(7)
  args[1] = Single(34300)
  args[2] = Array([49, 50, 51])
(7, <<mutable ref>>, 123)

// show(8, (&mut (1, 2), 3));
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Tuple { types: [Reference { typ: Tuple { types: [Field, Field] }, mutable: true }, Field] }, mutable: true }, Tuple { types: [Reference { typ: Tuple { types: [Field, Field] }, mutable: true }, Field] }] }):
  args[0] = Single(8)
  args[1] = Single(34468)
  args[2] = Single(34469)
  args[3] = Single(34470)
  args[4] = Single(34466)
  args[5] = Single(34467)
  args[6] = Single(3)
(8, <<mutable ref>>, (<<mutable ref>>, 0x03))

// show(9, &[1, 2, 3]);
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Slice { typ: Field }, mutable: true }, Slice { typ: Field }] }):
  args[0] = Single(9)
  args[1] = Single(34878)
  args[2] = Single(34879)
  args[3] = Single(3)
  args[4] = Array([1, 2, 3])
(9, <<mutable ref>>, &[0x01, 0x02, 0x03])

// show(9, &[(1, 2), (3, 4)]);
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Slice { typ: Tuple { types: [Field, Field] } }, mutable: true }, Slice { typ: Tuple { types: [Field, Field] } }] }):
  args[0] = Single(9)
  args[1] = Single(35076)
  args[2] = Single(35077)
  args[3] = Single(2)
  args[4] = Array([1, 2, 3, 4])
(9, <<mutable ref>>, &[(0x01, 0x02), (0x03, 0x04)])

// show(10, Foo { a: 1, b: (true, [3, 4]), c: [5, 6, 7] });
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Struct { name: "Foo", fields: [("a", UnsignedInteger { width: 32 }), ("b", Tuple { types: [Boolean, Array { length: 2, typ: UnsignedInteger { width: 64 } }] }), ("c", Array { length: 3, typ: UnsignedInteger { width: 64 } })] }, mutable: true }, Struct { name: "Foo", fields: [("a", UnsignedInteger { width: 32 }), ("b", Tuple { types: [Boolean, Array { length: 2, typ: UnsignedInteger { width: 64 } }] }), ("c", Array { length: 3, typ: UnsignedInteger { width: 64 } })] }] }):
  args[0] = Single(10)
  args[1] = Single(35362)
  args[2] = Single(35363)
  args[3] = Single(35364)
  args[4] = Single(35365)
  args[5] = Single(1)
  args[6] = Single(1)
  args[7] = Array([3, 4])
  args[8] = Array([5, 6, 7])
(10, <<mutable ref>>, Foo { a: 1, b: (true, [3, 4]), c: [5, 6, 7] })

// show(11, Bar::Baz((1, [2, 3])));
convert_string_inputs(Tuple { types: [UnsignedInteger { width: 32 }, Reference { typ: Enum { name: "Bar", variants: [("Baz", [Tuple { types: [UnsignedInteger { width: 32 }, Array { length: 2, typ: UnsignedInteger { width: 32 } }] }])] }, mutable: true }, Enum { name: "Bar", variants: [("Baz", [Tuple { types: [UnsignedInteger { width: 32 }, Array { length: 2, typ: UnsignedInteger { width: 32 } }] }])] }] }):
  args[0] = Single(11)
  args[1] = Single(36056)
  args[2] = Single(36057)
  args[3] = Single(36058)
  args[4] = Single(0)
  args[5] = Single(1)
  args[6] = Array([2, 3])
The application panicked (crashed).
Message:  not yet implemented: enum ref length

Documentation*

Check one:

  • No documentation needed.
  • Documentation included in this PR.
  • [For Experimental Features] Documentation to be submitted in a separate PR.

PR Checklist*

  • I have tested the changes locally.
  • I have formatted the changes with Prettier and/or cargo fmt on default settings.

@aakoshh aakoshh requested a review from a team August 20, 2025 11:28
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Test Suite Duration'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.20.

Benchmark suite Current: dc09203 Previous: 4451b45 Ratio
test_report_zkpassport_noir-ecdsa_ 3 s 1 s 3

This comment was automatically generated by workflow using github-action-benchmark.

CC: @TomAFrench

Copy link
Collaborator

@asterite asterite left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

@aakoshh aakoshh added this pull request to the merge queue Aug 21, 2025
Merged via the queue into master with commit 49a3185 Aug 21, 2025
132 checks passed
@aakoshh aakoshh deleted the af/9578-fix-print-ref branch August 21, 2025 09:50
AztecBot pushed a commit to AztecProtocol/aztec-packages that referenced this pull request Aug 22, 2025
Automated pull of nightly from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
chore: some mem2reg refactors regarding expressions and aliases (noir-lang/noir#9610)
feat: keep last loads from predecessors in mem2reg (noir-lang/noir#9492)
chore: Update flattening docs (noir-lang/noir#9588)
chore: remove redundant globals creation (noir-lang/noir#9606)
chore: simplify Expression in mem2reg (noir-lang/noir#9599)
chore: remove duplicate `contains_reference` in mem2reg (noir-lang/noir#9602)
chore!: remove `verify_signature_slice` methods from stdlib (noir-lang/noir#9597)
fix(expand): correctly handle nested dereferences (noir-lang/noir#9598)
fix(ssa): Do not simplify on lhs being zero for shifts (noir-lang/noir#9596)
chore: store last loads in `HashSet` instead of `HashMap` in mem2reg (noir-lang/noir#9498)
chore: `--no-ssa-locations` for `nargo interpret` and show negative values when printing SSA (noir-lang/noir#9586)
fix: `assert_constant` refactors and fixes from audit (noir-lang/noir#9547)
fix(ssa): Consider `shl` and `shr` to have side effects (noir-lang/noir#9580)
fix: avoid invalid cast in `remove_bit_shifts` (noir-lang/noir#9570)
fix(mem2reg): Consider aliases of a loaded address to be loaded from as well (noir-lang/noir#9567)
fix: Consume correct number of fields when printing references (noir-lang/noir#9579)
chore: Add a section for numeric type aliases (noir-lang/noir#9589)
chore(remove_paired_rc): Add various unit tests (noir-lang/noir#9425)
fix: incorrect max bit size in `remove_bit_shifts` (noir-lang/noir#9585)
chore(ssa): Simplify shl/shr identity operations (noir-lang/noir#9587)
chore: greenlight `brillig_array_get_and_set` for audits (noir-lang/noir#9540)
chore(ssa): Update comments on `loop_invariant` for audit and some missing unit tests (noir-lang/noir#9574)
chore: Switch to node v22.15.0 (noir-lang/noir#9582)
chore: Update unrolling docs for audit (noir-lang/noir#9572)
chore: greenlight `array_set_optimization` (noir-lang/noir#9560)
fix(acir_gen): Keep range checks before side effects (noir-lang/noir#9340)
END_COMMIT_OVERRIDE
github-merge-queue bot pushed a commit to AztecProtocol/aztec-packages that referenced this pull request Aug 22, 2025
Automated pull of nightly from the
[noir](https://github.com/noir-lang/noir) programming language, a
dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
chore: some mem2reg refactors regarding expressions and aliases
(noir-lang/noir#9610)
feat: keep last loads from predecessors in mem2reg
(noir-lang/noir#9492)
chore: Update flattening docs
(noir-lang/noir#9588)
chore: remove redundant globals creation
(noir-lang/noir#9606)
chore: simplify Expression in mem2reg
(noir-lang/noir#9599)
chore: remove duplicate `contains_reference` in mem2reg
(noir-lang/noir#9602)
chore!: remove `verify_signature_slice` methods from stdlib
(noir-lang/noir#9597)
fix(expand): correctly handle nested dereferences
(noir-lang/noir#9598)
fix(ssa): Do not simplify on lhs being zero for shifts
(noir-lang/noir#9596)
chore: store last loads in `HashSet` instead of `HashMap` in mem2reg
(noir-lang/noir#9498)
chore: `--no-ssa-locations` for `nargo interpret` and show negative
values when printing SSA (noir-lang/noir#9586)
fix: `assert_constant` refactors and fixes from audit
(noir-lang/noir#9547)
fix(ssa): Consider `shl` and `shr` to have side effects
(noir-lang/noir#9580)
fix: avoid invalid cast in `remove_bit_shifts`
(noir-lang/noir#9570)
fix(mem2reg): Consider aliases of a loaded address to be loaded from as
well (noir-lang/noir#9567)
fix: Consume correct number of fields when printing references
(noir-lang/noir#9579)
chore: Add a section for numeric type aliases
(noir-lang/noir#9589)
chore(remove_paired_rc): Add various unit tests
(noir-lang/noir#9425)
fix: incorrect max bit size in `remove_bit_shifts`
(noir-lang/noir#9585)
chore(ssa): Simplify shl/shr identity operations
(noir-lang/noir#9587)
chore: greenlight `brillig_array_get_and_set` for audits
(noir-lang/noir#9540)
chore(ssa): Update comments on `loop_invariant` for audit and some
missing unit tests (noir-lang/noir#9574)
chore: Switch to node v22.15.0
(noir-lang/noir#9582)
chore: Update unrolling docs for audit
(noir-lang/noir#9572)
chore: greenlight `array_set_optimization`
(noir-lang/noir#9560)
fix(acir_gen): Keep range checks before side effects
(noir-lang/noir#9340)
END_COMMIT_OVERRIDE
github-merge-queue bot pushed a commit to AztecProtocol/aztec-packages that referenced this pull request Aug 23, 2025
Automated pull of nightly from the
[noir](https://github.com/noir-lang/noir) programming language, a
dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
chore: some mem2reg refactors regarding expressions and aliases
(noir-lang/noir#9610)
feat: keep last loads from predecessors in mem2reg
(noir-lang/noir#9492)
chore: Update flattening docs
(noir-lang/noir#9588)
chore: remove redundant globals creation
(noir-lang/noir#9606)
chore: simplify Expression in mem2reg
(noir-lang/noir#9599)
chore: remove duplicate `contains_reference` in mem2reg
(noir-lang/noir#9602)
chore!: remove `verify_signature_slice` methods from stdlib
(noir-lang/noir#9597)
fix(expand): correctly handle nested dereferences
(noir-lang/noir#9598)
fix(ssa): Do not simplify on lhs being zero for shifts
(noir-lang/noir#9596)
chore: store last loads in `HashSet` instead of `HashMap` in mem2reg
(noir-lang/noir#9498)
chore: `--no-ssa-locations` for `nargo interpret` and show negative
values when printing SSA (noir-lang/noir#9586)
fix: `assert_constant` refactors and fixes from audit
(noir-lang/noir#9547)
fix(ssa): Consider `shl` and `shr` to have side effects
(noir-lang/noir#9580)
fix: avoid invalid cast in `remove_bit_shifts`
(noir-lang/noir#9570)
fix(mem2reg): Consider aliases of a loaded address to be loaded from as
well (noir-lang/noir#9567)
fix: Consume correct number of fields when printing references
(noir-lang/noir#9579)
chore: Add a section for numeric type aliases
(noir-lang/noir#9589)
chore(remove_paired_rc): Add various unit tests
(noir-lang/noir#9425)
fix: incorrect max bit size in `remove_bit_shifts`
(noir-lang/noir#9585)
chore(ssa): Simplify shl/shr identity operations
(noir-lang/noir#9587)
chore: greenlight `brillig_array_get_and_set` for audits
(noir-lang/noir#9540)
chore(ssa): Update comments on `loop_invariant` for audit and some
missing unit tests (noir-lang/noir#9574)
chore: Switch to node v22.15.0
(noir-lang/noir#9582)
chore: Update unrolling docs for audit
(noir-lang/noir#9572)
chore: greenlight `array_set_optimization`
(noir-lang/noir#9560)
fix(acir_gen): Keep range checks before side effects
(noir-lang/noir#9340)
END_COMMIT_OVERRIDE
Aristotelis2002 added a commit to blocksense-network/noir that referenced this pull request Sep 3, 2025
…lues

**Problem:**
A recent change in the Noir compiler (noir-lang#9579) modified its output
for reference types. It no longer dereferences them to provide the
underlying value in the trace. This caused the tracer to panic because
it was designed to always receive a `PrintableValue` for a
`PrintableType::Reference`.

**Solution:**
This commit adapts the tracer to this new debugger
behavior. When processing a reference, it now acknowledges that the
`PrintableValue` may not be available.

It constructs a `ValueRecord::Reference` with the correct type
information but uses `ValueRecord::None` as a placeholder for the
dereferenced content. This prevents the panic and correctly represents a
reference to an unknown value within the trace.
mralj pushed a commit to AztecProtocol/aztec-packages that referenced this pull request Oct 13, 2025
Automated pull of nightly from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
chore: some mem2reg refactors regarding expressions and aliases (noir-lang/noir#9610)
feat: keep last loads from predecessors in mem2reg (noir-lang/noir#9492)
chore: Update flattening docs (noir-lang/noir#9588)
chore: remove redundant globals creation (noir-lang/noir#9606)
chore: simplify Expression in mem2reg (noir-lang/noir#9599)
chore: remove duplicate `contains_reference` in mem2reg (noir-lang/noir#9602)
chore!: remove `verify_signature_slice` methods from stdlib (noir-lang/noir#9597)
fix(expand): correctly handle nested dereferences (noir-lang/noir#9598)
fix(ssa): Do not simplify on lhs being zero for shifts (noir-lang/noir#9596)
chore: store last loads in `HashSet` instead of `HashMap` in mem2reg (noir-lang/noir#9498)
chore: `--no-ssa-locations` for `nargo interpret` and show negative values when printing SSA (noir-lang/noir#9586)
fix: `assert_constant` refactors and fixes from audit (noir-lang/noir#9547)
fix(ssa): Consider `shl` and `shr` to have side effects (noir-lang/noir#9580)
fix: avoid invalid cast in `remove_bit_shifts` (noir-lang/noir#9570)
fix(mem2reg): Consider aliases of a loaded address to be loaded from as well (noir-lang/noir#9567)
fix: Consume correct number of fields when printing references (noir-lang/noir#9579)
chore: Add a section for numeric type aliases (noir-lang/noir#9589)
chore(remove_paired_rc): Add various unit tests (noir-lang/noir#9425)
fix: incorrect max bit size in `remove_bit_shifts` (noir-lang/noir#9585)
chore(ssa): Simplify shl/shr identity operations (noir-lang/noir#9587)
chore: greenlight `brillig_array_get_and_set` for audits (noir-lang/noir#9540)
chore(ssa): Update comments on `loop_invariant` for audit and some missing unit tests (noir-lang/noir#9574)
chore: Switch to node v22.15.0 (noir-lang/noir#9582)
chore: Update unrolling docs for audit (noir-lang/noir#9572)
chore: greenlight `array_set_optimization` (noir-lang/noir#9560)
fix(acir_gen): Keep range checks before side effects (noir-lang/noir#9340)
END_COMMIT_OVERRIDE
ludamad pushed a commit to AztecProtocol/aztec-packages that referenced this pull request Dec 16, 2025
Automated pull of nightly from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
chore: some mem2reg refactors regarding expressions and aliases (noir-lang/noir#9610)
feat: keep last loads from predecessors in mem2reg (noir-lang/noir#9492)
chore: Update flattening docs (noir-lang/noir#9588)
chore: remove redundant globals creation (noir-lang/noir#9606)
chore: simplify Expression in mem2reg (noir-lang/noir#9599)
chore: remove duplicate `contains_reference` in mem2reg (noir-lang/noir#9602)
chore!: remove `verify_signature_slice` methods from stdlib (noir-lang/noir#9597)
fix(expand): correctly handle nested dereferences (noir-lang/noir#9598)
fix(ssa): Do not simplify on lhs being zero for shifts (noir-lang/noir#9596)
chore: store last loads in `HashSet` instead of `HashMap` in mem2reg (noir-lang/noir#9498)
chore: `--no-ssa-locations` for `nargo interpret` and show negative values when printing SSA (noir-lang/noir#9586)
fix: `assert_constant` refactors and fixes from audit (noir-lang/noir#9547)
fix(ssa): Consider `shl` and `shr` to have side effects (noir-lang/noir#9580)
fix: avoid invalid cast in `remove_bit_shifts` (noir-lang/noir#9570)
fix(mem2reg): Consider aliases of a loaded address to be loaded from as well (noir-lang/noir#9567)
fix: Consume correct number of fields when printing references (noir-lang/noir#9579)
chore: Add a section for numeric type aliases (noir-lang/noir#9589)
chore(remove_paired_rc): Add various unit tests (noir-lang/noir#9425)
fix: incorrect max bit size in `remove_bit_shifts` (noir-lang/noir#9585)
chore(ssa): Simplify shl/shr identity operations (noir-lang/noir#9587)
chore: greenlight `brillig_array_get_and_set` for audits (noir-lang/noir#9540)
chore(ssa): Update comments on `loop_invariant` for audit and some missing unit tests (noir-lang/noir#9574)
chore: Switch to node v22.15.0 (noir-lang/noir#9582)
chore: Update unrolling docs for audit (noir-lang/noir#9572)
chore: greenlight `array_set_optimization` (noir-lang/noir#9560)
fix(acir_gen): Keep range checks before side effects (noir-lang/noir#9340)
END_COMMIT_OVERRIDE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Execution crash: Printing a reference to string

2 participants