Skip to content

fix(verilog): a range is not a bound -- 36 -> 0 in simulation, 5 -> 0 synthesizable - #3005

Merged
gHashTag merged 2 commits into
masterfrom
w43-range-bound
Sep 3, 2026
Merged

gHashTag merged 2 commits into
masterfrom
w43-range-bound

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 3, 2026

Copy link
Copy Markdown
Owner

gen_verilog_for_stmt wrote the whole iterable where the loop's comparison
belongs:

for (i = 0; i < (0 .. 1000); i = i + 1) begin

iverilog answers syntax error. Source is for (0..1000) |_| { … }.

Measured, by regenerating the corpus with each binary

Counting files whose for ( line carries a .. — the exact defect, not a loose
grep for the characters:

stock this
simulation path 36 0
synthesizable 5 0
specs that generate 581 581
iverilog -g2012 accepts 380 380
generated files changed 5 synth / 129 sim

Acceptance does not move, and that is expected: a syntax error in a file that
already failed elaboration for other reasons does not flip it. Seals for the
changed files are re-sealed in this commit, with that reading as the reason.

The repair did not travel, and the trap was already written down

#2849 fixed exactly this in the C emitter and left the reason in its own comment:
the range is an ExprBinary whose extra_op is "..", not the ExprRange
variant, which is declared in NodeKind and constructed nowhere. Rust tests the
same shape. Zig writes the range verbatim and is correct, because Zig has ranges.
Verilog was the one site that never got it.

Two things worth keeping from getting it wrong first

The rename that broke it. My first version also renamed a _ capture to
__t27_i, copying C. C declares its counter in the for header; here the
declaration is hoisted by collect_fn_loop_vars, so the rename produced
register __t27_i' unknown in …`. The probe caught it and the corpus could
not have
— all 36 carriers already failed to elaborate on the very defect
being repaired, so no acceptance number could move in either direction. The
rename is gone; the counter is whatever was declared, and a test now asserts
exactly that.

The guard has three clauses and the first suite reached two. for (data) |x|
iterates an identifier with zero children, so a mutant keeping only
children.len() == 2 left it untouched and survived. for (lo + hi) |x| is the
two-child binary that is not a range; with it, mutation is 4 of 4 —
never-a-range, always-a-range, start/end swapped, start forced to zero.

Honesty

  • 7 tests, including the runtime leg: the probe's three declared tests run under
    t27c icarus-simulate and PASS. Before this change the probe did not
    elaborate at all.
  • The runtime leg asks the OS whether iverilog exists rather than matching a
    phrase in whatever the failure printed (ci-gates 426), and skips loudly.
  • rustfmt --check on compiler.rs: 315 complaints on master and 315 here.
    The new test file is clean.
  • Out of scope, and gen-verilog emits a range literal where a bound belongs: for (i = 0; i < (0 .. 1000); …) -- 38 specs, and 10 in synthesizable RTL #2997 says so: the same literal reaches slice positions —
    expr[(0 .. idx)], line[(SSE_DATA_PREFIX_len .. line_len)] — 15 sites in
    7 files, unchanged
    . A range in an index is a part-select and a different
    repair.
  • Observed, not claimed: a non-range iterable still lowers as
    for (x = 0; x < data; x = x + 1), comparing an index against an array. That
    is pre-existing and this change deliberately does not touch it.

Closes #2997
Refs #2849, #2998

🤖 Generated with Claude Code

`gen_verilog_for_stmt` wrote the whole iterable where the loop's comparison
belongs, so `for (0..1000) |_| { … }` became

    for (i = 0; i < (0 .. 1000); i = i + 1) begin

and iverilog answers `syntax error`. Measured by regenerating the corpus with
the stock compiler and with this one, counting files whose `for (` line carries
a `..`: 36 -> 0 in the simulation path, 5 -> 0 synthesizable. 581 specs generate
either way; `iverilog -g2012` accepts 380 before and 380 after; 5 synthesizable
and 129 simulation files change, and their seals are re-sealed here.

#2849 fixed exactly this in the C emitter and wrote the trap down in its own
comment -- the range is an `ExprBinary` whose `extra_op` is "..", NOT the
`ExprRange` variant, which is declared in NodeKind and constructed nowhere. The
repair did not travel. Rust tests the same shape; Zig writes the range verbatim
and is correct, because Zig has ranges.

Two things worth keeping from getting it wrong first:

* The first version renamed a `_` capture to `__t27_i`, copying C. C declares
  its counter in the `for` header; here the declaration is hoisted by
  `collect_fn_loop_vars`, so the rename produced
  `register `__t27_i' unknown in …`. The probe caught it; the corpus could not
  have, because all 36 carriers already failed to elaborate on the very defect
  being repaired. The rename is gone.
* The guard has three clauses and the first test suite reached two. `for (data)
  |x|` iterates an identifier with zero children, so a mutant keeping only
  `children.len() == 2` left it untouched. `for (lo + hi) |x|` is the two-child
  non-range that separates them; with it, mutation is 4 of 4.

Out of scope and stated in the issue: the same literal reaches SLICE positions,
`expr[(0 .. idx)]`, 15 sites in 7 files, unchanged. A range in an index is a
part-select and a different repair.

Closes #2997
Refs #2849, #2998
@github-actions

github-actions Bot commented Sep 3, 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 3, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-03 07:23:45 UTC

Summary

Status Count
Total Open PRs 11
PRs with Failing Checks 10
PRs with All Checks Green 1
READY 0
FAILING 10
PENDING 0

Seal Status

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

@github-actions

github-actions Bot commented Sep 3, 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 3, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-03 07:30:23 UTC

Summary

Status Count
Total Open PRs 11
PRs with Failing Checks 10
PRs with All Checks Green 1
READY 0
FAILING 10
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=1b52250fc530 != 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 b3d6271 into master Sep 3, 2026
37 checks passed
@gHashTag
gHashTag deleted the w43-range-bound branch September 3, 2026 07:41
gHashTag added a commit that referenced this pull request Sep 3, 2026
…and a verification is dated (#3012)

434. Section 428 named `$?`. The trap fired again through `&&`:

    git apply --check "$patch" | head -3 && echo "    APPLIES"

printed APPLIES for two patches that do not apply, because `head` succeeded. The
real answer -- `error: bootstrap/stage0/FROZEN_HASH: patch does not apply` --
went past on the piped line. `$?` was never the subject: `&&`, `||`, `if`,
`while`, `until` and `set -e` all read the LAST command of a pipeline. A command
whose exit code you care about does not go in a pipeline.

435. I published that two externally-authored patches apply cleanly and their
FROZEN_HASH is valid, hedged correctly that any edit to compiler.rs would end
that, and then ended it myself four hours later by merging #3005 -- which
rewrote the seal from fd842146… to 1b52250f…. A hedge moves the work to a reader
who may never come. Name the commit rather than the branch, and when you merge
something touching the same files, go back and say so: you are the one person
holding both facts at once.

The tell was a review agent reporting that the brief's premise -- "compiler.rs is
byte-identical between X and master" -- had expired during its own run.

`tri skill check`: 399 sections, no number used twice.

Refs #2988, #3005
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 emits a range literal where a bound belongs: for (i = 0; i < (0 .. 1000); …) -- 38 specs, and 10 in synthesizable RTL

1 participant