Skip to content

Two hardcoded lists, each missing a case - #2875

Merged
gHashTag merged 2 commits into
masterfrom
w727
Aug 29, 2026
Merged

Two hardcoded lists, each missing a case#2875
gHashTag merged 2 commits into
masterfrom
w727

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

The Rust backend had never been audited. Both findings are the shape #2844 already had: a fixed list of node kinds that omits one.

1. A function whose body is assignments only was emitted as a stub

has_body matches statements against ExprReturn | StmtExpr | StmtLocal | StmtIf | StmtWhile | StmtFor | StmtForRange. StmtAssign is absent.

spec      fn gate_domain() {
              power_gate_en = true;
              clock_gate_en = true;
              domain_active = false;
          }

Rust      pub fn gate_domain() -> () { unimplemented!() }
Zig       fn gate_domain() void { power_gate_en = true; ... }
C         void gate_domain(void) { power_gate_en = true; ... }

unimplemented!() type-checks in any return position, so rustc takes it with no diagnostic and the function panics at run time instead of performing its stores.

53 functions across 35 specs, and they are not incidental: the clock tick() of the FPGA testbenches, uart_reset, six state setters in top_level, both on_clock hardware steps — the pure-mutation functions a state machine is made of.

2. A bool struct field in a condition was treated as an integer

expr_is_bool has no arm for ExprFieldAccess:

spec      if (!debouncer.enabled) {
Rust      if ((debouncer.enabled) == 0) {     <- E0308, rustc rejects the file
Zig/C     if (!debouncer.enabled) {

The emitter prints pub enabled: bool, into the same file, two dozen lines earlier, and never consulted it. It now remembers which field names this file declared as bool.

Keyed by name rather than by (struct, field), on a measurement: zero of the 650 specs declare one field name as bool in one struct and as something else in another, so within a generated file the name is unambiguous. 35 names collide across files — which is why the set is cleared per file and never shared.

Measured over the 559 specs that generate

before after
unimplemented!() stubs 870 817
integer zero-test on a field 25 2
rustc accepts 214 216
suite 2455 / 0 2455 / 0

This is #2844 one level up: that fixed the content of the assignment arms; this is the gate that stopped them from being reached at all.

bootstrap/stage0/FROZEN_HASH resealed.

Refs #2844

The Rust backend had never been audited. Both findings are the shape
#2844 already had: a fixed list of node kinds that omits one.

FIRST. `has_body` decides whether a function has a body at all by
matching its statements against
`ExprReturn | StmtExpr | StmtLocal | StmtIf | StmtWhile | StmtFor |
StmtForRange`. `StmtAssign` is absent, so a function whose body is
assignments ONLY tested as bodiless and was emitted as

    pub fn gate_domain() -> () { unimplemented!() }

`unimplemented!()` type-checks in any return position, so rustc takes it
with no diagnostic and the function panics at run time instead of
performing its stores. Zig and C lower all three assignments.

53 functions across 35 specs, and they are not incidental: the clock
`tick()` of the FPGA testbenches, `uart_reset`, six state setters in
top_level, both `on_clock` hardware steps -- the pure-mutation functions
a state machine is made of.

SECOND. `expr_is_bool` has no arm for `ExprFieldAccess`, so a condition
on a bool field fell to the integer default:

    spec      if (!debouncer.enabled) {
    emitted   if ((debouncer.enabled) == 0) {

which rustc rejects with E0308. The emitter prints `pub enabled: bool,`
into the same file two dozen lines earlier and never consulted it. It
now remembers which field names this FILE declared as `bool`.

Keyed by name rather than by (struct, field) on a measurement: zero of
the 650 specs declare one field name as `bool` in one struct and as
something else in another, so within a generated file the name is
unambiguous. 35 names collide ACROSS files, which is why the set is
cleared per file and never shared.

Measured over the 559 specs that generate:

    `unimplemented!()` stubs         870 -> 817
    integer zero-test on a field      25 ->   2
    rustc accepts                    214 -> 216

FROZEN_HASH resealed. Suite 2455 passed, 0 failed.

Refs #2844
@gHashTag
gHashTag enabled auto-merge (squash) August 29, 2026 19:49
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-08-29 19:49:58 UTC

Summary

Status Count
Total Open PRs 8
PRs with Failing Checks 7
PRs with All Checks Green 1
READY 0
FAILING 7
PENDING 0

Seal Status

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

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

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

1 similar comment
@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-29 19:50:26 UTC

Summary

Status Count
Total Open PRs 8
PRs with Failing Checks 7
PRs with All Checks Green 1
READY 0
FAILING 7
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=95f7168a9594 != 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 0f5e873 into master Aug 29, 2026
27 of 28 checks passed
gHashTag added a commit that referenced this pull request Aug 29, 2026
§317 ended with "That is a five-line script, not an audit -- and it
would have found all four." No such script existed when I wrote that.

Built and measured:

  naive diff of constructed variants against each list
      -> 10 lists of 10 flagged. Almost every list is a legitimate
         subset, so it finds everything, which is finding nothing.

  the same grouped into families (control flow / binding / exit),
  flagging a list that covers PART of a family
      -> 3 of 10, and on the commit before #2875 it points at the exact
         `has_body` line. But all three of its hits on a clean tree are
         correct code: `StmtLocal | StmtAssign` collects NAMED bindings
         and `StmtExpr` has no name; the loop arm at 10979 excludes
         `StmtIf` on purpose.

  two of the four are not NodeKind lists at all: `compound_binop` maps
  operator strings and `expr_is_bool` is a match whose missing arm
  nobody enumerated.

So: 1 of 4, three false positives. `tri kinds drift` also finds 1 of 4
and costs zero false positives, which is why that is the one that
shipped. §317 now carries these numbers instead of the prediction.

319 is the general form. A sentence in the conditional tense, inside a
document whose purpose is to hold measurements, takes authority from the
numbers around it -- and unlike a wrong claim about existing code, it
cannot be checked until somebody builds the thing. Either build it and
write the number, or write "untested" beside it.

Refs #2876
gHashTag added a commit that referenced this pull request Aug 29, 2026
The Rust header exists to name what this backend drops:

    // NOT LOWERED BY THIS BACKEND: 7 test(s), 2 invariant(s).
    // This backend emits declarations only.

Its own comment says why: "Emitting library code without tests is a
defensible policy; emitting it silently is the defect."

It said nothing about the third category. 817 functions across the
corpus are emitted as `unimplemented!()`, and a file holding two dozen
of them read as complete -- the banner accounted for the tests and the
invariants and left the stubs out of its own accounting.

The banner is written before the body, so the count needs a pre-pass.
The `has_body` predicate was inline in `gen_fn`; it is now a function
both call, because two copies of that list is exactly how `StmtAssign`
went missing from one of them (#2875).

Cross-checked over all 559 specs that generate: banner total 817,
`unimplemented!()` emitted 817, zero files where the two disagree.

FROZEN_HASH resealed. Suite unchanged.

Refs #2875
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.

1 participant