gen-c: name the element type of a bare literal list - #3461
Merged
Conversation
Refs #3459 var x = [1, 2, 3]; -> __auto_type x = { 1, 2, 3 }; error: cannot use '__auto_type' with initializer list in C 1401 of these remained after the previous pass, all one shape. Rust writes `let mut x = [1, 2, 3]` and Zig `var x = .{ 1, 2, 3 }`; only C has no inference for a brace list, so the type has to be named. The choice is not arbitrary: it matches what a SCALAR literal already gets -- `var x = 1` emits `uint32_t x = 1` here and `var x: u32 = 1` in Zig -- and any fractional element makes the list `double`, which is C's own promotion. MEASURED, whole corpus, -ferror-limit=0: errors 14738 -> 14276 __auto_type with initializer list 1401 -> 837 files better 31 THE CORPUS REFUTED THE FIRST VERSION. A string literal is an `ExprLiteral` whose `value` is the text WITHOUT its quotes, so `["12", "34"]` passed the digit test and the list was typed `uint32_t`: incompatible pointer to integer conversion initializing 'uint32_t' with an expression of type 'char *' four of them in one file. `extra_kind == "string"` is the tag, and the `#define` path upstream already knew it. The per-file check is the only reason this was seen; the aggregate improved either way. Two files still rise and both are unmasking, checked one at a time: an unnamed tuple struct losing `f0`, and `member reference base type 'int'`. A third, a `uint16_t` field initialised from a `uint32_t[4]`, was wrong before and had no type to be wrong against. A SPECULATION WAS REVERTED WITH ITS REASON RECORDED. A one-element literal `[7]` arrives as an `ExprArrayLiteral` with ZERO children -- confirmed by instrumenting the branch -- while the emitted C still reads `{ 7 }`, so the element is kept somewhere this code does not read. `lit.value` was the obvious candidate, was tried, and is empty too. 334 of the remaining 837 are `{ 0 }`. A mutant survived on a guard that is SUBSUMED rather than unreachable: deleting `kind != ExprLiteral` changes nothing, because an identifier, a call and a binary expression all reach the value checks with nothing numeric to offer -- probed, all three. It stays, and the difference from the `has_init` parameter deleted last pass is written into the comment: that one no caller could reach, this one is reachable and dropping it would make correctness depend on an accident. Three other mutants died. 47 seals refreshed in the same commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gHashTag
enabled auto-merge (squash)
September 8, 2026 04:11
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #3459
1401 of these remained after the previous pass, all one shape. Rust writes
let mut x = [1, 2, 3]and Zigvar x = .{ 1, 2, 3 }; only C has no inferencefor a brace list, so the type has to be named.
The choice is not arbitrary — it matches what a scalar literal already
gets:
var x = 1emitsuint32_t x = 1here andvar x: u32 = 1in Zig. Afractional element makes the list
double, which is C's own promotion.Measured —
-ferror-limit=0__auto_typewith initializer listigla/race/ternary_gemm437 → 348 ·igla/race/rtl268 → 197 ·igla/race/bram_weights279 → 228.The corpus refuted my first version
A string literal is an
ExprLiteralwhosevalueis the text without itsquotes, so
["12", "34"]passed a digit test and the list was typeduint32_t:four of them in one file. The per-file check is the only reason it was seen —
the aggregate improved either way.
Two files still rise and both are unmasking, checked one at a time.
A speculation reverted, with its reason recorded
A one-element literal
[7]arrives as anExprArrayLiteralwith zerochildren — confirmed by instrumenting the branch — while the emitted C still
reads
{ 7 }, so the element is kept somewhere this code does not read.lit.valuewas the obvious candidate, was tried, and is empty too. 334 of theremaining 837 are
{ 0 }and every one takes that path. Filed rather thanguessed at.
Mutation
u32kind != ExprLiteralremovedThe last one differs from the
has_initparameter deleted last pass: that oneno caller could reach; this one is reachable, and dropping it would make
correctness depend on the accident that non-literal nodes carry no numeric
text. Probed with an identifier, a call and a binary expression — all three
still refused.
Full suite: 2639 passed, 0 failed. 47 seals refreshed in the same commit.