Skip to content

A statement may stand where a clause head is expected - #2779

Merged
gHashTag merged 1 commit into
masterfrom
w699-fallback-why
Aug 29, 2026
Merged

A statement may stand where a clause head is expected#2779
gHashTag merged 1 commit into
masterfrom
w699-fallback-why

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Refs #2754, #2778

The instrument first: parse-complete --fallbacks

bdd-block-fallback is 93% of everything this parser discards — one bucket, no direction. It is eight call sites sharing one name. Recording which one fired, and on which clause, split it in a single pass:

events specs  why                            clause
   594    35  quantified invariant (forall)  forall
   269    29  unmodelled clause head         forall
    69    21  stopped mid-clause             for
    41    14  stopped mid-clause             while

863 of 1 034 events are forall in two spellings — one owner decision, #2774. The 110 for/while events were the only actionable row, and they are this PR.

Rung 9

for and while are keywords, so at clause position they are neither a clause head nor a block boundary, and the loop fell through to the whole-block fallback — taking every assertion after them.

before after
discarded tokens 30 408 25 093 (−5 315, the largest rung so far)
specs discarding 86 77

Two backend defects the recovered code exposed, both older than this change

  • gen_c_invariant emits at module scope — right for the _Static_assert it is built around, invalid for anything else. while loop outside of a function, and vals redefined once per invariant. A body with statements now goes in a function, as gen_c_test already does.
  • @as(usize, x) printed its type argument as an expression(usize)(x), use of undeclared identifier 'usize'. Every other site in that backend maps through type_to_c; this one printed the identifier it found.
cc accepts it     158 -> 163
ALL FOUR           63 ->  66

And one assertion that fails on its own merits

#2778. specs/base/ternary_encoding.t27 asserts trits_to_bits(bits_to_trits(i)) == i over 0..16. It does not hold. Zig evaluates it at comptime now and stops.

Every backend reported that spec as fine for as long as the block was discarded. Nine rungs in, this is the first recovered assertion that is simply false — which is what the whole line was for.

Zig 217 → 214: those three specs are the ones whose assertions now run. One is #2778; the other two are shape complaints (value of type 'usize' ignored, use of undefined value) and are named there as follow-ups. Read the column falling only after opening each spec — five other specs went the other way once the backend defects they exposed were fixed.

A correction

I first labelled one fallback arm "clause head not an identifier". The comment directly above it says the opposite — it fires for an identifier that is not a clause — so the table read as though forall lexed as something other than an Ident. Renamed to "unmodelled clause head". A mislabelled bucket is worse than no bucket: it invents an explanation.

parse-conform               22/22, new case load-bearing
cargo test --no-fail-fast   2432 passed, 0 failed
suite --ratchet             RATCHET: CLEAN, ledger 177 -> 168
check_elab_ratchet.py       176 = baseline, exit 0
check_specs_generate.py     exit 0
check_seal_coverage.py      exit 0 — 168 re-sealed, 172 twins synced

🤖 Generated with Claude Code

W699 rung 9, and the instrument that found it.

INSTRUMENT: `parse-complete --fallbacks`. `bdd-block-fallback` is 93% of
everything this parser discards, and it is EIGHT call sites sharing one name.
Recording which one fired, and on which clause, split it in one pass:

    events specs  why                            clause
       594    35  quantified invariant (forall)  forall
       269    29  unmodelled clause head         forall
        69    21  stopped mid-clause             for
        41    14  stopped mid-clause             while

863 of 1 034 events are `forall` in two spellings -- one owner decision, #2774.
The 110 `for`/`while` events were the only actionable row, and they are this
commit.

RUNG 9: `for` and `while` are keywords, so at clause position they are neither a
clause head nor a block boundary, and the loop fell through to the whole-block
fallback -- taking every assertion after them.

    discarded tokens  30408 -> 25093   (-5 315, the largest rung so far)
    specs discarding     86 -> 77

TWO BACKEND DEFECTS THE RECOVERED CODE EXPOSED, both older than this change:

  * `gen_c_invariant` emits at MODULE SCOPE, which is right for the
    `_Static_assert` it is built around and invalid for anything else. A body
    with statements now goes in a function, as `gen_c_test` already does.
    ("while loop outside of a function", and `vals` redefined per invariant.)
  * `@as(usize, x)` printed its TYPE argument as an expression -- `(usize)(x)`,
    "use of undeclared identifier 'usize'". Every other site maps through
    `type_to_c`; this one printed the identifier it found.

    cc accepts it     158 -> 163
    ALL FOUR           63 -> 66

AND ONE ASSERTION THAT FAILS ON ITS OWN MERITS, filed as #2778.
`specs/base/ternary_encoding.t27` asserts a round trip over 0..16 that does not
hold. Zig evaluates it at comptime now and stops. Every backend reported that
spec as fine for as long as the block was discarded. Nine rungs in, this is the
first recovered assertion that is simply FALSE -- which is what the line was for.

Zig 217 -> 214: those three specs are ones whose assertions now RUN. One is
#2778; the other two are shape complaints (`value of type 'usize' ignored`,
`use of undefined value`) and are named there as follow-ups.

A CORRECTION. I first labelled one fallback arm "clause head not an identifier".
The comment above it says the opposite -- it fires for an identifier that is not
a CLAUSE -- so the table read as though `forall` lexed as something other than an
Ident. Renamed to "unmodelled clause head". A mislabelled bucket is worse than no
bucket: it invents an explanation.

    parse-conform              22/22, new case load-bearing
    cargo test --no-fail-fast  2432 passed, 0 failed
    suite --ratchet            RATCHET: CLEAN, ledger 177 -> 168
    check_elab_ratchet.py      176 = baseline, exit 0
    check_specs_generate.py    exit 0
    check_seal_coverage.py     exit 0 (168 re-sealed, 172 twins synced)

Skill 170-172.

Refs #2754, #2778

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@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 04:14:10 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)=03cdabb2b243 != 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 d4c849c into master Aug 29, 2026
34 checks passed
@gHashTag
gHashTag deleted the w699-fallback-why branch August 29, 2026 04:24
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