t27c corpus puts C last of the four backends — 157 of 559 accepted (24.2%), against Zig 217, rustc 214 and Verilog 378. The largest systematic cause is not a long tail.
The measurement
specs/vsa/ops.t27 opens with
use base::types;
use base::ops;
and Trit is declared in specs/base/types.t27. The generated C:
$ t27c gen-c specs/vsa/ops.t27 | grep -c Trit
141
$ t27c gen-c specs/vsa/ops.t27 | grep -cE '^typedef|^enum'
0
141 uses, zero declarations. The prelude emits only <stdint.h> <stdbool.h> <stddef.h> <assert.h> and nothing from the imported spec. cc reports unknown type name 'Trit' in 12 specs measured across the whole corpus.
What this is not
It is not a missing typedef Trit in the prelude. Trit is not a language built-in — it is declared in a spec, and other specs import it. Adding a typedef would paper over the actual gap and invent a representation the language never fixed.
The gap is that gen-c does not resolve use: it emits a standalone translation unit per spec and never emits, includes, or forward-declares anything the spec imported.
Rust has the same shape, C just fails louder
gen-rust on the same spec emits pub fn bind(a: Vec<Trit>, …) with no Trit in scope either. Zig drops the affected code entirely, so its number does not show the cost. Three backends, three different silences over one missing mechanism.
Why this is worth doing
Every other lever in the Rust column has now been pulled — the ruler (-o /dev/null, #2760), unconditional serde derives (#2760), and Vec<const T> — taking it from a false 0 to 214. C has had none of that, and this is the one cause that is systematic rather than per-spec: the first-error histogram is otherwise a long tail of user types appearing once each.
Effort is days, not minutes: it needs a decision about what an imported declaration becomes in C — a generated header per spec plus #include, or inlined declarations in dependency order — and the same decision then applies to Rust.
t27c corpusputs C last of the four backends — 157 of 559 accepted (24.2%), against Zig 217, rustc 214 and Verilog 378. The largest systematic cause is not a long tail.The measurement
specs/vsa/ops.t27opens withand
Tritis declared inspecs/base/types.t27. The generated C:141 uses, zero declarations. The prelude emits only
<stdint.h> <stdbool.h> <stddef.h> <assert.h>and nothing from the imported spec.ccreportsunknown type name 'Trit'in 12 specs measured across the whole corpus.What this is not
It is not a missing
typedef Tritin the prelude.Tritis not a language built-in — it is declared in a spec, and other specs import it. Adding a typedef would paper over the actual gap and invent a representation the language never fixed.The gap is that
gen-cdoes not resolveuse: it emits a standalone translation unit per spec and never emits, includes, or forward-declares anything the spec imported.Rust has the same shape, C just fails louder
gen-ruston the same spec emitspub fn bind(a: Vec<Trit>, …)with noTritin scope either. Zig drops the affected code entirely, so its number does not show the cost. Three backends, three different silences over one missing mechanism.Why this is worth doing
Every other lever in the Rust column has now been pulled — the ruler (
-o /dev/null, #2760), unconditional serde derives (#2760), andVec<const T>— taking it from a false 0 to 214. C has had none of that, and this is the one cause that is systematic rather than per-spec: the first-error histogram is otherwise a long tail of user types appearing once each.Effort is days, not minutes: it needs a decision about what an imported declaration becomes in C — a generated header per spec plus
#include, or inlined declarations in dependency order — and the same decision then applies to Rust.