Skip to content

fix(parser): const X = packed struct {} was emitted as parameter = packed - #3384

Merged
gHashTag merged 2 commits into
masterfrom
fix/packed-struct-const
Sep 6, 2026
Merged

fix(parser): const X = packed struct {} was emitted as parameter = packed#3384
gHashTag merged 2 commits into
masterfrom
fix/packed-struct-const

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 6, 2026

Copy link
Copy Markdown
Owner

There is no KwPacked in this lexer — packed arrives as an Ident, exactly
like union does. parse_const_decl handled = struct { and = union(enum) {
but not = packed struct {, so the declaration fell through to the generic
expression path and became a ConstDecl whose initializer started at the token
packed.

The Verilog backend then rendered it as a scalar parameter:

parameter [31:0] Greeting = packed;

packed is not a value.

Scope, stated plainly

No spec in this repository declares one todaygrep -rn '= packed struct' --include='*.t27' returns 0. That is why nothing caught it, and it also means
this fix changes no corpus number in either direction. I am not claiming one.

The damage is in the corpus snapshot the website vendors, which carries
specs/demos/hello_world.t27 — the spec the Spec Explorer opens on, described as
showing "every part of the language" — with that line live under its Verilog tab
on t27.ai.

A packed struct is the declaration that matters most to a hardware backend: it
is a bit layout. Losing it to a keyword token is the worst-placed gap in the
emitter.

What this deliberately does not do

Packedness itself is not recorded on the node. Nothing downstream reads it,
and inventing a field no emitter consumes would look like support that does not
exist. The declaration now reaches the backends as a struct, which is the part
that was missing.

Verification

Because no spec exercises this construct, the two regression tests are the only
thing standing between it and a silent return — so they were checked against the
defect, not just run:

new branch disabled packed_struct_const_parses_as_a_struct_declaration FAILS, plain-struct test still passes
new branch enabled both pass
full suite 1694 passed, 0 failed

FROZEN_HASH resealed per FROZEN.md §5 / CANON.md M5. The digest was computed
with shasum and written by the shell — never transcribed by hand.

Closes #3383

… packed`

There is no KwPacked in this lexer -- `packed` arrives as an Ident, exactly like
`union` does. parse_const_decl handled `= struct {` and `= union(enum) {` but
not `= packed struct {`, so the declaration fell through to the generic
expression path and became a ConstDecl whose initializer started at the token
`packed`. The Verilog backend then rendered it as a scalar parameter:

    parameter [31:0] Greeting = packed;

`packed` is not a value.

No spec in this repository declares one today -- `grep -rn '= packed struct'
--include='*.t27'` returns 0 -- which is why nothing caught it. That also means
this fix changes no corpus number in either direction, and I am not claiming one.
The damage is in the corpus snapshot the website vendors, which carries
specs/demos/hello_world.t27: the spec the Spec Explorer opens on, described as
showing "every part of the language", with that line live under its Verilog tab
on t27.ai.

A packed struct is the declaration that matters most to a hardware backend -- it
IS a bit layout -- so losing it to a keyword token is the worst-placed gap in
the emitter.

Packedness itself is deliberately NOT recorded on the node. Nothing downstream
reads it, and inventing a field no emitter consumes would look like support that
does not exist. The declaration now reaches the backends as a struct, which is
the part that was missing.

Because no spec exercises this, the two regression tests are the only thing
standing between the construct and a silent return. Verified by negative
control: with the new parser branch disabled, packed_struct_const_parses_as_a_
struct_declaration FAILS and the plain-struct test still passes. Full suite
1694 passed, 0 failed.

FROZEN_HASH resealed per FROZEN.md §5 / CANON.md M5; the digest was computed
with shasum and written by the shell, never transcribed.

Closes #3383
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

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

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-06 12:10:37 UTC

Summary

Status Count
Total Open PRs 15
PRs with Failing Checks 10
PRs with All Checks Green 5
READY 4
FAILING 10
PENDING 0
NO CHECKS YET 0

These columns do not partition: 4 + 10 + 0 + 0 = 14, and there are 15 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=745cf51f9c7e != 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).

gHashTag added a commit to gHashTag/trinity-fpga that referenced this pull request Sep 6, 2026
…the defect

A47. Competitor research, done through the browser after WebSearch failed in
two consecutive iterations. It placed the Chip tab BEHIND prior art: netlistsvg,
DigitalJS, d3-hwschematic and FEntwumS all render an elaborated netlist, ours
renders declared widths -- because our Verilog is module shells. On the other
axis, searching for published silicon-level format conformance returns our own
paper as the top hit. Behind on visualisation, alone on evidence.

Following that into the emitter: hello_world -- the spec the Spec Explorer
opens on -- emits `parameter [31:0] Greeting = packed;`. There is no KwPacked
in the lexer, so `packed` arrives as an Ident; parse_const_decl handles
`= struct {` and `= union(enum) {` but not `= packed struct {`.
gHashTag/t27#3384, closing #3383.

I first wrote that this was likely a major cause of the 315 yosys rejections,
then counted: zero specs on master declare it. The fix changes no corpus number
in either direction, and the commit and PR say so instead of leaving the
earlier framing standing.

Because nothing exercises the construct, the two regression tests are the whole
guard -- so they were checked against the defect: branch disabled, the packed
test fails and the plain one still passes. Suite 1694 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both NOW gates refused the PR: every change to master must ADD a docs/now/
entry, and the local pre-commit hook only checks that an EXISTING entry is
fresh, so it passed while CI did not. Two gates disagreeing about the same
requirement is exactly what the second one exists to report, and it did.

Closes #3383
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

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

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-06 12:13:49 UTC

Summary

Status Count
Total Open PRs 15
PRs with Failing Checks 10
PRs with All Checks Green 5
READY 4
FAILING 10
PENDING 0
NO CHECKS YET 0

These columns do not partition: 4 + 10 + 0 + 0 = 14, and there are 15 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=745cf51f9c7e != 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).

@gHashTag
gHashTag merged commit f5d8564 into master Sep 6, 2026
42 checks passed
@gHashTag
gHashTag deleted the fix/packed-struct-const branch September 6, 2026 12:31
gHashTag added a commit to gHashTag/trinity-fpga that referenced this pull request Sep 6, 2026
gHashTag/t27#3384 merged as f5d8564ab, closing #3383. 38 checks pass, 0 fail,
0 pending -- including the Corpus ratchet, which agrees with the zero-specs
finding rather than merely tolerating it.

Two NOW gates refused the first push: every change to master must ADD a
docs/now/ entry, while the local pre-commit hook only checks that an EXISTING
entry is fresh. The hook passed and CI did not, and the second gate exists to
report exactly that disagreement.

t27.ai still shows the old line under hello_world's Verilog tab, because the
website vendors an older corpus snapshot. Re-vendoring is the follow-up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gHashTag added a commit that referenced this pull request Sep 6, 2026
#3384) (#3385)

There is no KwPacked in this lexer -- `packed` arrives as an Ident, exactly like
`union` does. parse_const_decl handled `= struct {` and `= union(enum) {` but not
`= packed struct {`, so the declaration fell through to the generic expression
path and became a ConstDecl whose initializer started at that token.

Cherry-picked from master (f5d8564). Master's corpus contains no `packed
struct` at all, so master could not test the fix. THIS branch is where
specs/demos/hello_world.t27 lives -- the spec the Spec Explorer opens on -- so
it is the first place the fix could be verified end to end. The conflict was
real and resolved by keeping both: the branch's `union(enum)` case occupies the
slot the fix used on master, and both now sit side by side ahead of KwStruct.

Measured on hello_world, same branch, same spec, only the parser differing.

    before:  parameter [31:0] Greeting = packed;
             // struct
             reg [7:0] _length;         // .length
             reg signed [7:0] _trit;    // .trit

    after:   // struct Greeting
             reg [7:0] greeting_length;        // Greeting.length
             reg signed [7:0] greeting_trit;   // Greeting.trit

So the struct was already being emitted -- anonymously, because the declaration
was a ConstDecl. The fix removes the bogus parameter and restores the name.

WHAT IT DOES NOT DO, measured rather than assumed. yosys 0.63 synthesises both
versions to 0 cells, 4 wires, 4 ports. It never rejected the old output either:
it printed

    Lexer warning: The SystemVerilog keyword `packed' is not recognized
    unless read_verilog is called with -sv!

and carried on. So this removes a warning and restores a lost name; it does not
change synthesis, and it was never a cause of the corpus's yosys failures. I had
speculated otherwise two iterations ago and am recording the refutation next to
the fix rather than leaving the guess in a commit message. The module-shell /
0-LUT condition has a different root cause -- no hardware boundary -- which the
`entry-points` service already measures at 387 specs.

Suite green on this branch: 889 plus the per-crate suites, 0 failed. FROZEN_HASH
resealed with a shasum-computed digest, never transcribed. Cargo.lock was left
alone: `cargo build -p t27c` prunes ~1000 lines from it, which is a build
artifact and not a change.

Closes #3383
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.

parser: const X = packed struct {} becomes a ConstDecl, and Verilog emits = packed;

2 participants