Skip to content

feat(frontend): admit every integer width from 2 to 16384 and dump the monomorphized program before the field guard - #19

Open
BornPsych wants to merge 7 commits into
provekit-v2from
ys/goldilocks-stage-5d
Open

BornPsych wants to merge 7 commits into
provekit-v2from
ys/goldilocks-stage-5d

Conversation

@BornPsych

@BornPsych BornPsych commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Stage 5d of the Goldilocks port, stacked on #17. It changes the width rule that #16 introduced: an integer type may now have any width from 2 through 16384 bits, odd or even, for both signednesses, where before it was 8, 16 and the even widths from 32 to 65536. The cap is the one the Mavros compiler accepts (MAX_SUPPORTED_INT_BITS = 1 << 14), so every width the frontend admits is one the consumer of the monomorphized output takes; which operations it can lower at a given width is its own question. u2, u3, u10, i24 and u33 are types like any other; u65536 is refused. On bn254 nothing changes on the circuit path: the backend still lowers u8 through u128 and i8 through i64 and refuses every other width at the same place as before.

Three things I'd point a reviewer at.

Width 1 is refused in every spelling and is never an alias of bool. u1 and i1 already said "u1 has been removed, use bool instead"; now u<1>, i<1> and a generic width that binds to 1 at monomorphization print that same sentence as the note under "u1 is not a supported integer type", so a user reads one instruction whichever way they wrote it. An alias would have made u<N> at N = 1 the same type as bool and put every impl on bool in coherence with the u<N> impls. u0 and i0 are now read as integer type names too (a lone 0 was not a width before), so they get the width diagnostic instead of an unresolved name. The same diagnostic is raised in an expression path, u16385::max_value(), where the name is looked up as a primitive type rather than resolved as a type and used to come back as "Could not resolve". Its three renderers, at resolution, in path resolution and at monomorphization, share one function.

compile_no_check runs ensure_field_is_linked after monomorphization instead of before it. Monomorphization is front-half work and runs under any field, so the hidden --show-monomorphized now prints the program, and a monomorphization error is reported as such, when the requested field is not the one the compiler was built with. The guard still sits ahead of the cache read, so a cached artifact for the built field is never returned for another. Nothing changes when the field matches.

The standard library gains no #[test] for the small widths. Its test runner compiles every stdlib test to a circuit in two of its three lanes, and the backend cannot lower u2 or u3, so the u2/i2/u3/i3 limits are pinned in the driver's stdlib_elaboration.rs, which evaluates them at compile time under every configured field. The generic max_value/min_value impls needed no change: i<N>::max_value shifts by N - 2, which is 0 at N = 2.

Elsewhere: the lexer's coarse literal ceiling follows the constant and is now 2^16384 - 1; the widest width in the frontend's width tests moves from 65536 to 16384 and the widths 2, 3 and 33 join the literal-bound sweep; design/field-genericity.md states the new rule, cites the cap, records why width 1 is refused rather than aliased, and notes that the ECDSA foreigns carry no field at any plane while keccakf1600 has no Mavros replacement under any field.

Tests: width_contract.rs gains every_spelling_of_width_1_says_use_bool_and_width_0_is_refused, which pins the rendered message and note for u1, u<1>, i<1>, u0 and a generic bound to 1, and a_width_the_language_does_not_have_is_refused_in_an_expression_path for u16385::max_value(), i0::max_value() and u1::max_value(). field_selection.rs gains monomorphization_reports_before_the_linked_field_guard, red before the guard moved (the error was UnsupportedField) and green after (MonomorphizationError). The driver's width boundary test adds u3, and the small-width limits sit next to the u34/i128 ones in stdlib_elaboration.rs.

Gates on the tip: frontend 2301 and driver 33 tests, the stdlib runner's 9 matrix cells, the Goldilocks lane's test command 2384 tests, clippy at -Dwarnings across the workspace and rustfmt clean, and nargo by hand on u3 (passes check, stops at the backend), u<1>, i<1>, u0, u16385, the small-width limits under both fields, and --show-monomorphized --field goldilocks. The execute snapshot suite was not rerun; no snapshot carries the rule text.

Known follow-ups, not here: the monomorphizer reports a generic width bound past the rule at the call site rather than at the signature; the ast-interpreter companion regenerates its width fixtures at the new rule.

@github-actions

Copy link
Copy Markdown

Thank you for your contribution to the Noir language.

Please do not force push to this branch after the Noir team have started review of this PR. Doing so will only delay us merging your PR as we will need to start the review process from scratch.

Thanks for your understanding.

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 21, 2026
@BornPsych
BornPsych force-pushed the ys/goldilocks-stage-5d branch from 0396301 to ba5582e Compare September 22, 2026 16:45
@BornPsych
BornPsych changed the base branch from ys/goldilocks-stage-5b to provekit-v2 September 22, 2026 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant