Skip to content

gen-c: an array literal argument is a compound literal - #3476

Merged
gHashTag merged 1 commit into
masterfrom
c/array-literal-args
Sep 8, 2026
Merged

gen-c: an array literal argument is a compound literal#3476
gHashTag merged 1 commit into
masterfrom
c/array-literal-args

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 8, 2026

Copy link
Copy Markdown
Owner

gen-c: an array literal argument is a compound literal

Closes #3475

A bare { 1, 2 } is an INITIALISER, not an operand: C accepts it after
= in a declaration and nowhere else. Splitting expected expression
-- the corpus's largest class, 885 -- by the character the caret points
at puts 508 of them on {, across 496 distinct lines.

MEASURED, whole corpus, -ferror-limit=0:
errors 12420 -> 12321 (-99)
files better/worse 12 / 0
expected expression 885 -> 757
fpga_testbench_mac_tb 130 -> 86, igla_race_ternary_inference 259 -> 235

THE ELEMENT TYPE IS THE CALLEE'S, NOT THE ELEMENTS'. The cast is derived
from param_type_to_c, so it agrees with the parameter's own
declaration by construction: a parameter written const char** acc
takes (const char*[]). Nothing is inferred from the literal.

THE return { ... } HALF IS LEFT LOUD ON PURPOSE, AND THE REASON IS
MEASURED: all 131 of those sites have a POINTER return type -- 131 of
131, no exceptions. A compound literal in a return is a block-scoped
object, so the obvious widening hands back the address of a local
(#3445), trading a loud syntax error for a silent dangling pointer. A
test pins that limitation so the rule is not widened by someone who has
not read why it is narrow.

MY PRECONDITION WAS MEASURED ON THE WRONG SIDE OF THE LOWERING. I asked
"is the C parameter a pointer?" and counted 170 reachable; the repair
asks "is the t27 parameter a slice?", and 124 were removed. The 70 that
remain reconcile exactly: 45 [N]T, 19 [T], 6 with no declaration in
any spec, 2 not arrays at all, 2 declared in another translation unit.

A MUTANT THAT IGNORES THE ARGUMENT'S POSITION SURVIVED every test,
because every test passed the literal FIRST -- while 63 of the 194
corpus sites pass it later. take(7, [1,2,3]) kills it.

AND THE MUTATION HARNESS REPORTED A FALSE VERDICT: an anchor that
matched twice made the edit fail, and the run that followed was the
UNMUTATED file, printed as *** SURVIVED ***. It now refuses -- ANCHOR FAILED -- NOT A VERDICT -- with a deliberately unmatchable anchor as the
positive control.

One class rose and it is unmasking, checked rather than assumed:
member reference base type 'const char *' +14, all in
igla_race_yosys, all on sv.len() where sv had no type until its
declaration parsed. That file fell 133 -> 128.

Neighbouring finding, filed not folded in: [T] is a THIRD array
spelling, 220 occurrences across 43 specs and 50 element types, which
the C backend reads as [SIZE]Type with a type where the size goes.

Tests: 7 new, full suite 3522 passed 0 failed. 15 stale seals refreshed
(12 specs).

🤖 Generated with Claude Code

Closes #3475

A bare `{ 1, 2 }` is an INITIALISER, not an operand: C accepts it after
`=` in a declaration and nowhere else. Splitting `expected expression`
-- the corpus's largest class, 885 -- by the character the caret points
at puts 508 of them on `{`, across 496 distinct lines.

MEASURED, whole corpus, -ferror-limit=0:
  errors            12420 -> 12321   (-99)
  files better/worse   12 / 0
  `expected expression` 885 -> 757
  fpga_testbench_mac_tb 130 -> 86, igla_race_ternary_inference 259 -> 235

THE ELEMENT TYPE IS THE CALLEE'S, NOT THE ELEMENTS'. The cast is derived
from `param_type_to_c`, so it agrees with the parameter's own
declaration by construction: a parameter written `const char** acc`
takes `(const char*[])`. Nothing is inferred from the literal.

THE `return { ... }` HALF IS LEFT LOUD ON PURPOSE, AND THE REASON IS
MEASURED: all 131 of those sites have a POINTER return type -- 131 of
131, no exceptions. A compound literal in a `return` is a block-scoped
object, so the obvious widening hands back the address of a local
(#3445), trading a loud syntax error for a silent dangling pointer. A
test pins that limitation so the rule is not widened by someone who has
not read why it is narrow.

MY PRECONDITION WAS MEASURED ON THE WRONG SIDE OF THE LOWERING. I asked
"is the C parameter a pointer?" and counted 170 reachable; the repair
asks "is the t27 parameter a slice?", and 124 were removed. The 70 that
remain reconcile exactly: 45 `[N]T`, 19 `[T]`, 6 with no declaration in
any spec, 2 not arrays at all, 2 declared in another translation unit.

A MUTANT THAT IGNORES THE ARGUMENT'S POSITION SURVIVED every test,
because every test passed the literal FIRST -- while 63 of the 194
corpus sites pass it later. `take(7, [1,2,3])` kills it.

AND THE MUTATION HARNESS REPORTED A FALSE VERDICT: an anchor that
matched twice made the edit fail, and the run that followed was the
UNMUTATED file, printed as `*** SURVIVED ***`. It now refuses -- `ANCHOR
FAILED -- NOT A VERDICT` -- with a deliberately unmatchable anchor as the
positive control.

One class rose and it is unmasking, checked rather than assumed:
`member reference base type 'const char *'` +14, all in
igla_race_yosys, all on `sv.len()` where `sv` had no type until its
declaration parsed. That file fell 133 -> 128.

Neighbouring finding, filed not folded in: `[T]` is a THIRD array
spelling, 220 occurrences across 43 specs and 50 element types, which
the C backend reads as `[SIZE]Type` with a type where the size goes.

Tests: 7 new, full suite 3522 passed 0 failed. 15 stale seals refreshed
(12 specs).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gHashTag
gHashTag enabled auto-merge (squash) September 8, 2026 09:32
@github-actions

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

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-08 09:33:00 UTC

Summary

Status Count
Total Open PRs 16
PRs with Failing Checks 13
PRs with All Checks Green 3
READY 2
FAILING 13
PENDING 0
NO CHECKS YET 0

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

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=1ec94cbb7598 != 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 7cc9236 into master Sep 8, 2026
30 of 33 checks passed
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-c: a brace initialiser is not an expression -- 496 lines, and the return half must stay loud

1 participant