Repository navigation
gen-c: a repeat [v] ** N holds v in every element, or is refused (Closes #11181) - #11192
Merged
Merged
Conversation
#11181) t27c gen-c wrote `uint64_t a[4] = /* repeat: { 7 } ** 4 */ {0};` for `var a : [4]u64 = [7] ** 4;`: four zeros, exit 0, so the generated C computed other values than the spec and t27b. Cause: the C emitter's repeat arm (#6050) takes the GNU range only when the sole element reaches it as a CHILD of the array literal. A bare literal or name between the brackets (`[7]`, `[K]`) reads as an array dimension, so the parser keeps only its text (extra_size) and the arm saw no element; a module-level literal keeps text for every shape. Elements that were calls (`[@as(u8, 5)]`, `[f()]`), expressions or nested repeats were excluded by an allow-list and also fell to `{0}`. Measured on a 408-cell probe matrix: 137 cells wrong on master, 0 after. Fix, in bootstrap/src/compiler.rs, hand Rust net -2 lines (+40/-42): c_repeat_list reads the elements of either parser shape (children, or the kept text parsed again); the arm writes `{0}` for a zero and the GNU range for one other value; zero now looks through @as, a struct of zeros and a repeat of zeros, so no zero output changes (2788 of 2857 tracked specs byte-identical); a nested repeat drops the outer source comment instead of nesting `/* */`; a repeat of a LIST (`[a, b] ** n`), which a designated range cannot hold, is refused like a switch without else (#7308). Three specs hold one and are ledgered in docs/reports/suite_expectations.json (66 -> 69, phase gen-c). Test, written first and red on master: specs/compiler/c_array_repeat_wrapper.t27 gains the repeat operator in every scalar type, hex, named, @as, parameter, module constant, struct field, 4096 elements and nested; its existing Rust harness compiles and runs it (cc -std=gnu11 -Werror), and t27c test-report and t27b test both pass it. The refusal and the zero-list control are core_selfhost fixtures (both_refuse/, genc/), so no Rust test grew. FROZEN_HASH moved with compiler.rs; 47 specs whose gen_hash_c moved are re-saved (76 seal files, all verify MATCH). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…11181) The corpus ratchet's type-name step (tri types ratchet) failed on the first push of this branch: "+ Row NEW conflict". The struct Row added to specs/compiler/c_array_repeat_wrapper.t27 shares its name with the Row of specs/port/browseros/trios/agent-server/apps/server/src/api/routes/queen-registry.t27, and a type name with two definitions is a new conflict. RepeatCells is defined once in the tree. Run locally after the rename: tri types ratchet is CLEAN (ledger 107, observed 107), tri types classified is OK, the spec still passes t27c test-report (6 of 6, 0 vacuous), t27b test (6 passed) and `cargo test -p t27c --test c_array_repeat_wrapper`; the seal is re-saved (verify: all hashes MATCH, validate-seals ok). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contributor
…-guards found, and record gen_hash_c=none for the 3 specs gen-c now refuses (Closes #11181) The first CI run of this branch failed `coverage` (tools/check_seal_coverage.py: 20 seals "gen-drift") and `spec-guards` (tools/check_seal_currency.py: 16 stale gen_hash_c). Cause: the 13 specs I had set aside as "already stale on master" were not. I had decided that by hashing the output of `t27c gen-c <absolute path>`; for a spec that resolves a `use`, that is not what `t27c seal` hashes, and `t27c seal --verify` on the master binary says "all hashes MATCH" for all 13. With this branch's compiler 11 of them (analog/linear_zoh, depin/prove, effects/capability, effects/fs_cap, effects/replay_oracle, hosting/sybil, ml/optimizer/lamb, ml/recurrent/bilstm, ml/rl/ppo_critic, tri/io/fs, verified/corpus_merkle) mismatch on gen_hash_c only; lab/lab_t27b_cache and tools/t27o/spice_convert still match. Re-saved with `t27c seal --save` (11 specs, 16 seal files incl. twins; only gen_hash_c, built_by, sealed_at and the test runner digest move). The 3 specs gen-c now refuses (ternary/hybrid_arithmetic, tri/t27b/conformance/array_repeat_text, tri/t27b/conformance/big_repeat_global) are re-saved with `--force`, which records gen_hash_c=none and leaves the zig, rust and verilog hashes and the test results as they were; that is what the compiler produces for them now, and the same state 4 other seals already record. Run locally with the branch compiler after this: tools/check_seal_coverage.py ends "OK: 2939 seals, 2826 hold, 113 known-broken", tools/check_seal_currency.py reports 0 stale generated-code hashes (2837 current, 8 sealed with gen_hash=none by its count). `t27c suite --ratchet --corpus-only` was already CLEAN. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contributor
PR DashboardGenerated at: 2026-10-20 01:10:02 UTC
Summary
Seal Status
|
gHashTag
marked this pull request as ready for review
October 20, 2026 01:24
This was referenced Oct 20, 2026
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.
Closes #11181
t27c gen-cwroteuint64_t a[4] = /* repeat: { 7 } ** 4 */ {0};forvar a : [4]u64 = [7] ** 4;: four zeros, exit 0, so the generated C computed other values than the spec,t27c test-reportandt27b. It is not u64-specific. On a 408-cell probe matrix 137 cells were wrong on master and none are now (4 cells still do not compile, loudly: a repeat in argument or return position, a string element type, and a call element on clang; see below).Probe matrix
Master e427d38 (
compiler.rsis byte-identical to 16c7dcf, where the issue was reproduced). Every cell is a spec with a test,t27c gen-c,cc -std=gnu11 -w -DT27_TEST_MAIN, run. Types u8, u16, u32, u64, i32, f64, bool; values non-zero (5, 300, 70000, 7, -3, 1.5, true) and zero; N = 1, 4, 64, 4096. Each row is 28 cells per value kind.var a : [N]T = [v] ** N;const a : [N]T = [v] ** N;pub const A : [N]T = [v] ** N;S{ .a = [v] ** N }[@as(T, v)] ** N[_]T{v} ** N(control)[v; N](control)The 4 non-wrong cells in three rows are i32
-3: a leading minus makes the element an expression, which the parser keeps as a child, so master had it right there but not in a module constant. Eight more shapes, one cell each:[0xFF],[1_000], a named element[K], a parameter[x], an expression[D + 2], a call[two()], a named count, an expression count(D * D)were all wrong on master; so was a non-zero struct element (one of 11 extra edge cells,[_]P{P{ .x = 7, .y = 1 }} ** 4), which now builds and runs. Nested[[1] ** 3] ** 2(local,@as, module constant) did not compile on master (a/* */comment nested in a/* */one) and is correct now. Not changed, and loud on master and here: a repeat in argument or return position is still bare braces (a C error); a numeric powera ** bis written as a repeat (see #7427, not touched).Cause
The repeat arm of the C emitter (#6050, "any other single value takes the GNU range") only ever looked at
lhs.children.len() == 1. The parser gives the sole element of[v]two shapes. Whenvis not a literal or a bare name, it is a child of the array literal. A bare literal or name between the brackets cannot be told from an array dimension ([4]u8), so the parser leaves it as text inextra_sizeand the node has no child: the arm saw no element and wrote{0}. A module-level literal keeps text for every shape. On top of that an allow-list (c_repeat_value_lowers) sent calls,@as, expressions and nested repeats to{0}as well.gen-rustalready reads the text shape ("A sole literal or name element arrives as bracket TEXT"); the C arm never did. This is a bug, not a documented trade-off: #6050 chose the range on purpose and the branch works whenever the element is a child.Fix (
bootstrap/src/compiler.rs, hand Rust net -2 lines)c_repeat_listreturns the values of a repeat for either shape: the children, or the kept text parsed again (whole, then comma by comma).{0}for a zero and the GNU designated range{ [0 ... (n) - 1] = v }for one other value;vgoes throughgen_c_expr, so_digit separators, names, casts and calls print as everywhere else.{0}and now looks through@as(T, 0), a struct of zeros and a repeat of zeros, so no zero output changed (below)./* */: the outer source comment is dropped when it would contain another.[a, b] ** nrepeats a list, which a designated range cannot hold. It was{0};compile_cnow refuses it like the switch withoutelse(self-host: gen-c lowers a switch with no else arm to 0 for the missing case #7308):a repeat of several values ([a, b] ** n) has no C initializer (#11181), exit non-zero, unless every element is zero. The module-level spelling (text only) is caught too.There is no spec for the emitter's repeat rule, so the rule stays in Rust; the diff is +40/-42 in
compiler.rsand the own-language gate passes (below).FROZEN_HASHcarries the new digest.Test, red first
specs/compiler/c_array_repeat_wrapper.t27gains the repeat operator in every scalar type, hex, named element and count,@as, parameter, module constant, struct field, 4096 elements, nested, and a zero control (3 tests, 12 + 9 + 2 asserts). Its existing Rust harness (bootstrap/tests/c_array_repeat_wrapper.rs) already compiles the spec's own tests withcc -std=gnu11 -Werrorand runs them, so no Rust test was added. On master the spec's C does not compile (nested comment); with the nested function removed it compiles and the process traps on the first assertion (exit 133). The issue's probe on master:Assertion failed: ((third() == 7)), exit 134; on this branchAll 1 tests passed., exit 0, and the line isuint64_t a[4] = /* repeat: { 7 } ** 4 */ { [0 ... (4) - 1] = 7 };. The same spec passest27c test-report(6 of 6, 30 runtime asserts, 0 vacuous) andt27b test(6 passed), so spec, Zig, t27b and C now agree.Negative controls are
core_selfhostdata fixtures, no Rust:both_refuse/list_repeat.t27andlist_repeat_bare.t27(gen-c and the core must both refuse; on master gen-c wrote{0}), andgenc/list_repeat_zero.t27(a list of zeros still builds and runs). Value 0 stays{0}: the diff ofgen-coutput over all 2857 tracked.t27files, master binary against this one, has 2788 byte-identical, and in the other 65 changed files every changed hunk is{0}to a range (164) or a dropped nested comment (10).Corpus effect (t27c gen-c over the 2857 tracked .t27 files)
gen_hash_cmoved and are re-saved witht27c seal --save; the other 2 (lab/lab_t27b_cache, tools/t27o/spice_convert) still verify. The three refused specs are re-saved with--force, which recordsgen_hash_c=none, the state 4 other seals already hold. In all, 96 seal files in 61 specs (twins included), andt27c seal --verifysaysall hashes MATCHfor each re-saved one. My first selection of seals was wrong: I had set 13 specs aside as "already stale on master" because theirt27c gen-c <absolute path>hash differed from the seal, butt27c sealhashes ause-resolving spec differently, andt27c seal --verifyon master says MATCH for all 13. CI'scoverageandspec-guardscaught it (20 seals); the second commit (b347c92) re-saves them.ccbefore or after for other reasons, 6 compile both ways, 1 (a fixture) now compiles. One regression of the loud kind:specs/port/bootstrap/src/compiler/HirIpCatalog_new.t27has[empty_core()] ** 32; master wrote zeros (its own native tests aborted), this branch writes the range, which gcc builds and clang refuses (cannot compile this GNU array range designator extension yet: a range over a call). The existing[v; N]lowering has the same limit. Pure expressions (names, casts, arithmetic) build on clang.specs/ternary/hybrid_arithmetic.t27,specs/tri/t27b/conformance/array_repeat_text.t27,specs/tri/t27b/conformance/big_repeat_global.t27. They are indocs/reports/suite_expectations.json(phase gen-c, expires 2026-11-30, 66 to 69 entries, a hand edit with the reason in_why_entries_rose, as corpus: ledger the six port-wave codegen refusals, max_entries 75 -> 83 #9303 did), so the corpus ratchet should not call them new failures. That is the one edit outside the compiler that needs a reviewer's eye.git grep -l "/\* repeat:" -- genprints 0 files before and after. No committedgen/cfile belongs to a changed spec.What each re-seal states (old gen-c against new, per spec, hunks counted)
Re-sealing is a statement that the new output is the one wanted. For the 11 specs below every changed hunk is one of two things and nothing else (0 other): a
{0}initializer replaced by the GNU range holding the value the spec writes, or an outer/* repeat: ... */comment dropped because it nested another (the old C did not compile there; the new one is{0}, the same all-zero value). The 3 refused specs now failgen-cwitha repeat of several values ([a, b] ** n) has no C initializer (#11181), and their seals recordgen_hash_c=none.float h_out[3]= 9.0 in all 3m[100],v[100]= 9.0out[3]= 9.0const char* out[8]and[3]= "" (were NULL)ZRC_NV[1]= 818234 (the constant the spec writes)uint8_t weights/result[16][16]={0}(was a nested comment)wide[600]= 65wide[65]= 66,full[64]= 67got[3]= 9true_tier[4]= TIER_PUBLICb[1]= 9;d[5][32]={0}[_]i8{1, 0, -1, 1} ** 8as an argument[1, 2] ** 3and[SEED, minus_two()] ** 2[_]u16{ 1, 2 } ** PAIRSt27c --versionof the compiler used: 0.5.2, built from this branch'scompiler.rs(digest inFROZEN_HASH).Gates and results
git diff --numstat --no-renames BASE...HEAD | budget(compiled from the merge base'sgen/c/policy/own_language.c, as the workflow does): exit 0.compiler.rs+40/-42,FROZEN_HASH+1/-1; the per-file limit is 40 added. The path gate (check_all, exceptions from BASE) exit 0.FROZEN_HASH=08d882e79f8da0cfb8abc0f98e95de792580dc88d5dd1cb9bae8bd6385e86b72=shasum -a 256 bootstrap/src/compiler.rs.cargo check -p t27c --all-targets: exit 0.cargo build --release -p t27c: ok.cargo test -p t27c --test Xfor c_array_repeat_wrapper (6 passed), core_selfhost (1 passed: gen-c builds the core, fixpoint, fixtures, refusals, corpus differential), genc_digit_separators (4), c_module_array_consts (5), c_local_array_types (6), c_struct_array_fields (7), c_auto_type_array_literal (4), c_array_literal_arguments (7).tri types ratchet"+ Row NEW conflict" (the test's structRowshared its name withRowinqueen-registry.t27; renamedRepeatCells, 2494746);coverage(tools/check_seal_coverage.py) andspec-guards(tools/check_seal_currency.py) on the seals above (b347c92). Now, run locally:tri types ratchetCLEAN (ledger 107, observed 107),tri types classifiedOK,tools/check_seal_coverage.pyOK (2939 seals, 2826 hold, 113 known-broken),tools/check_seal_currency.py0 stale generated-code hashes,validate-seals --pr-filesok.Verilog widths (absurd-width ledger)also failed on that run: it namesspecs/widgets/bit_diff.t27andspecs/widgets/lut_treemap.t27(on master, gen-verilog widths of 2097151 bits), which this diff does not touch. On the final head b347c92 the only red checks are that one andt27b-native-ratchet(UNLISTEDspecs/widgets/vcd_wrapped.t27andvcd_wrapped_logic.t27, STALEspecs/ml/train_hslm.t27: ledger vs master drift, none of them in this diff);coverage,spec-guards,Corpus ratchet,own-language,test-ratchet,build-and-testand the rest are green.python3 -I tools/dupe_scan.py: master 1632 of 17570 bodies in 632 groups, branch 1632 of 17571 in 632 groups, "ok: no new duplicate body" on both.tools/check_devhome_paths.pyok; added lines are ASCII; no local paths.tri hooks pre-commitandl1-checkpass.Not run, and what stays open
cargo test -p t27c(only the C back-end and self-host targets listed above), and the CI jobs themselves, which were still queued when this was written.t27c suite --repo-root . --ratchet --corpus-onlyendsRATCHET: CLEAN, ledger 69 of 69 cap, 0 unexpected failures, 0 unexpected passes, 0 expired, 0 discard drift, 0 gate drift; Gen C 2478 passed and 3 failed (the three ledgered), Fixed Point 0 divergences. Seal Verify (200 failed) is reporting-only in the ratchet and was not compared with master.tri seals sync-twinsreports as "did not produce five hashes" are unrelated.t27core.t27emit_arraydescribe gen-c before this PR; I did not edit the core. A numeric powera ** bis still emitted as a repeat (see gen-c: a numeric a ** b is lowered as an array repeat, /* repeat: a ** b */ {0} #7427).🤖 Generated with Claude Code