Skip to content

fix(verilog): declare the enums a spec imports (Closes #2316) - #2317

Merged
gHashTag merged 1 commit into
masterfrom
fix/verilog-imported-enum-2316
Aug 20, 2026
Merged

fix(verilog): declare the enums a spec imports (Closes #2316)#2317
gHashTag merged 1 commit into
masterfrom
fix/verilog-imported-enum-2316

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Closes #2316

What was wrong

gen_verilog_expr lowers Enum.variant to the flat identifier Enum_variant
whichever spec declared the enum. gen_verilog declared the matching
localparam only for enums a spec declares itself, so an enum arriving
through use produced a reference with nothing declaring it:

gen/mac.v:100: error: Unable to bind wire/reg/memory `Trit_neg' in `ZeroDSP_MAC.extract_trit.extract_trit_body'

specs/fpga/mac.t27 does use base::ops;, and base/ops.t27 declares
Trit = enum(i8) { neg = -1, zero = 0, pos = 1 }.

Enums only, not use_resolve::resolve

resolve() splices whole declarations -- functions, structs, constants -- and
the Verilog backend cannot lower most of them, while 492 of the 650 specs carry
a use line. An enum is the one case where the backend already does everything
except emit the declaration, so use_resolve::imported_enums returns
(enum, [(variant, value)]) and nothing else, and the backend emits it through
the same gen_verilog_enum a same-spec enum goes through.

Two filters keep the blast radius where it belongs:

  • Referenced only -- an enum the module never names emits nothing.
    specs/fpga/uart.t27 imports both modules that declare Trit and never
    mentions it; its output is byte-identical to before. There is a test for
    exactly this.
  • Never shadow -- a name the module already declares is dropped. A
    redeclaration is a compile error, so this pass may only ever ADD a declaration
    that nothing else provides.

Measured

The prebuilt t27c at fb88da234 regenerates mac.v byte-identical to the
fpga-verilog artifact of run 32358697899, so it is a faithful stand-in for the
emitter. With the three localparam lines at the exact position the enum
section occupies and nothing else changed:

iverilog -g2012 -DSIMULATION gen/mac.v
as generated 28 errors
after 22 errors, 0 mentioning Trit_

Across all 650 specs, the ones that carry a use, reference Enum.variant, and
do not declare that enum locally are exactly six, and all six want Trit:
fpga/mac.t27, nn/attention.t27, nn/hslm.t27, vsa/ops.t27, and the two
demos/jones_topology_* -- the last two import only base::types, which is
NOPARSE, so they are unchanged by this.

What this does NOT do

fpga-conformance stays red at 4/32. mac.v still fails on 22 errors, and
26 of the 28 failing modules fail on a defect this PR does not touch: a
struct-typed function parameter is declared as one scalar and then referenced
through <param>_<field> names nothing declares -- word_raw,
mac_units_status, cfg_addr_width, 202 distinct identifiers. The emitter is
internally inconsistent there (packed bit-slices in some places, per-field regs
in others, <param>_<field> matching neither); picking one representation is a
design decision, not a repair. bridge.v (cross-spec calls to functions defined
inside other modules -- illegal in Verilog whatever file set you compile) and
zerodsp_top.v (a structural wrapper the loop compiles without its five
dependencies) are the remaining two.

gen-verilog-for-simulation / icarus-simulate are deliberately left on the
source-only path, so no .trinity/icarus-baselines/ entry moves.

Verification, and its limits

  • iverilog before/after on the real artifact, above. This is the load-bearing
    evidence.
  • bootstrap/tests/verilog_imported_enum.rs asserts the property directly --
    every Enum_variant identifier the output USES is one it DECLARES -- on
    mac.t27, on uart.t27 as the control, and on a spec tree the test owns. Run
    against the current artifact the first and third fail (3 undeclared each) and
    the control passes, which is what a test for this defect should do.
  • That test does not gate anything today. cargo test -p t27c was removed
    from corpus-ratchet in fix(corpus-ratchet): remove the t27c test step; it was measured on the wrong tree #2293 because master has 13 failing tests; per the
    note left there it should return as a ratchet, not a plain gate. Nothing in CI
    currently compiles bootstrap/tests/**.
  • The change was type-checked with rustc --edition 2021 --crate-type lib --emit=metadata over compiler.rs + use_resolve.rs: identical error and
    warning set to master (one unresolved serde import, 8 warnings), and a
    deliberately planted type error in the new code was caught, so the check is
    not vacuous.

`gen_verilog_expr` lowers `Enum.variant` to the flat identifier `Enum_variant`
whichever spec declared the enum; `gen_verilog` declared the matching
`localparam` only for enums a spec declares itself. An enum arriving through
`use` therefore produced a reference with nothing declaring it, which is not
valid Verilog under any standard:

    gen/mac.v:100: error: Unable to bind wire/reg/memory `Trit_neg'
        in `ZeroDSP_MAC.extract_trit.extract_trit_body'

`use_resolve::imported_enums` returns `(enum, [(variant, value)])` for the specs
a file imports -- enums only, no splicing. `resolve()` pulls whole declarations
the Verilog backend cannot lower, and 492 of the 650 specs carry a `use` line;
an enum is the one case where the backend already does everything except emit
the declaration. The backend emits them through the same `gen_verilog_enum` a
same-spec enum goes through.

Two filters. Referenced only: an enum the module never names emits nothing, so
`specs/fpga/uart.t27` -- which imports both modules that declare `Trit` and
never mentions it -- is byte-identical to before. Never shadow: a name the
module already declares is dropped, since a redeclaration is a compile error and
this pass may only ever add a declaration nothing else provides.

Measured with the prebuilt `t27c` at `fb88da234`, which regenerates `mac.v`
byte-identical to the `fpga-verilog` artifact of run 32358697899:
`iverilog -g2012 -DSIMULATION gen/mac.v` goes from 28 errors to 22, none of them
mentioning `Trit_`. Across all 650 specs, exactly six import an enum they name
and do not declare, and all six want `Trit`.

`fpga-conformance` stays red at 4/32. The other 22 errors in `mac.v`, and 26 of
the 28 failing modules, are a different defect: a struct-typed function
parameter is declared as one scalar and then referenced through
`<param>_<field>` names nothing declares (`word_raw`, `mac_units_status`,
`cfg_addr_width` -- 202 distinct identifiers). Choosing one struct
representation is a design decision, not a repair, and it is not made here.

Closes #2316
@github-actions

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-08-20 12:34:58 UTC

Summary

Status Count
Total Open PRs 20
PRs with Failing Checks 5
PRs with All Checks Green 15
READY 7
FAILING 5
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=c3ec9fba947b != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

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.

gen-verilog: an enum reached through use is referenced but never declared

2 participants