fix(specs): make 36 specs compile under the current t27 compiler - #99
Merged
gHashTag merged 1 commit intoSep 24, 2026
Merged
Conversation
These .t27 files failed to parse, so the t27 compiler produced no target for them and the Queen board (t27.ai/queen, Specs) listed them as Broken. The syntax is translated to t27 (Zig/Rust idioms, old test style, typos such as 'expect(a;);', stray braces); declarations, tests, invariants, benches, assertions and constants are kept. Every file now passes all seven targets (c, js, rust, ts, verilog, verilog_hir, zig) of the compiler built from gHashTag/t27 master a103752, and t27c gen / gen-c / gen-rust / gen-verilog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
These 36
.t27files did not parse, so the t27 compiler produced no target and the Queen board (t27.ai/queen → Specs) counted them as Broken. Most carried Zig or Rust idioms (try std.testing.expect,for (xs) |x|,@as/@intCast,let mut), the oldgiven … when …test style, or typos such asexpect(a;);and stray braces.This PR translates the syntax to t27 and keeps the meaning: every declaration, test, invariant, bench, assertion and constant stays. Where t27 has no direct form, the spec uses a construct the passing corpus already uses and says so in a comment (listed below).
Verification (every file):
a103752— the same analysis the Queen board runs — with 0 discarded and 0 swallowed items;t27c gen,gen-c,gen-rust,gen-verilogsucceed;specs/fpga/avs_controller_48.t27specs/fpga/avs_controller_96.t27specs/fpga/avs_reconf.t27specs/fpga/dfs_gate.t27not x->!x(2 places). Test and invariant rewrites as in fp8_e4m3. Removed the stray}before the invariants. The typoexpect(result.child_count == 4 // No change);(the);sat inside the comment) is fixed. Braces added to invariant dfs_stack_depth…specs/fpga/drowsy_ret.t27specs/fpga/fbb_active_path.t27return a and b and c;, with a comment.for (0..N) |i|becamefor i in 0..N, and the @as/@truncate casts becameas. In …specs/fpga/fp8_e4m3.t27given x = e->const x = e;,try std.testing.expect(x = e);(a binding written as a boolean) ->const x = e;,try std.testing.expect(e);->assert(e);. Braceless invariant clauses:try std.testing.expect(x = e);->and x = e, `tr…specs/fpga/fp8_e5m2.t27for i in 0..N { const v = xs[i]; ... }. The @as/@intCast/@truncate/@floatFromInt/@floatCast/@intFromFloat casts became t27x as T; FP8E5M2 is spelled u8, with a comment.@as(i16, -EXP_BIAS)became-(EXP_BIAS as i16). `…specs/fpga/gf128.t27ascast has no u128 target, sox as u128is rewritten as@as(u128, x)in extract_mantissa_128_low, extract_mantissa_128_high and mant_to_f64_128 (4 casts), with a comment at each. That spelling parses, and C emits(unsigned __int128)(x)while R…specs/fpga/gf16_to_fp16.t27const, expect ->assert(...). Four//comments inside expect calls swallowed the closing);; each comment now sits after its statement, with the text intact. Removed the stray}before the invariants. mantissa_masks_correct: i…specs/fpga/gf16_to_posit16.t27x <<= nandx >>= nbecamex = x << nandx = x >> n, and the integer casts becameas.for (0..@as(usize, @intCast(n))) |_|becamefor _ in 0..(n as usize). Brace tests became const/assert, and 3 expects whose);had been swallowed b…specs/fpga/gf256.t27@as(u32, 1), which narrowed the shifts to 32 bits, and it produced Rust type errors (implicit u64->u128 a…specs/fpga/gf32_to_fp32.t27specs/fpga/holo_mux_x4.t27not Xbecame!X(2 in fns, 1 in theassert !any_valid or count > 0invariant). The five Zig capture loopsfor (inputs, 0..) |input, i|/for (inputs) |input|becamefor i in 0..NUM_INPUTS { const input = inputs[i]; ... }. In holo_opposite_layer…specs/fpga/int4.t27let raw = clamped as u4; ... raw = (16 + clamped) as u4;becamevar raw : u4 = clamped; ... raw = 16 + clamped;. Round 2, from the review: (1) MAJOR fixed. My round-1 claim that the quantified tests 'lower to real loops' was wrong. The …specs/fpga/int8.t27specs/fpga/lane_l_precheck.t27packed struct(u8)->packed struct, with a comment. Zig for-loops becamefor i in 0..LANE_COUNTandfor result in results, and the indexed form becamefor i in 0..LANE_COUNT { const result = results[i]; ....not config.enabled->!config.enabled.…specs/fpga/lut_npu_81_entry.t27specs/fpga/nf4.t27if (x < 0.0) { -x } else { x };(a Rust tail expression), becomesif (x < 0.0) { return -x; } else { return x; }. Braces added to 1 test and 3 invariants whose clause body usedfor (i in 0..N) then expr; the clause form cannot hold a …specs/fpga/null_pe.t27not x->!x(2 places). 5 Zig loopsfor (configs, 0..) |config, i|(andpe_array.configs), whose open range0..does not parse, becomefor (0..PE_COUNT) |i| { const config = <arr>[i]; ... }; the arrays are typed [PE_COUNT]PeConfig, so the itera…specs/fpga/posit16.t27for i in 0..Nandfor val in vec, with range bounds kept exactly.not x->!xtwice. posit16_dot_product:for (a, 0..) |val_a, i|->for i in 0..a.len { const val_a = a[i]; if (i >= b.len) { break; } ... }. Tests: given/expe…specs/fpga/purkinje_thermal_gate.t27if (not ptg_temp_valid(temp_c))becameif (!ptg_temp_valid(temp_c)). Removed the stray}before the invariants. Braced tests:given x = Ebecameconst x = E;(29), expect-bindings becameconst x = E;(8), andtry std.testing.expect(E);became `as…specs/fpga/sparse_mask.t27return switch (op) {...};.for (0..N) |i|andfor (xs, 0..) |v, i|becamefor i in 0..N, and the @as/@truncate casts becameas. The// 7/16 ~ 43%comment that had swall…specs/fpga/sparse_skip.t27not xbecame!x. Indexed loops becamefor i in 0..VECTOR_SIZE { const v = xs[i]; ... }, and the @as/@truncate casts becameas. Brace tests got typed[16]i8literals, and[_]i8{v} ** 16became[v; 16]. The stray module-level}was remov…specs/fpga/spec_exit.t27if (not spec_state_can_speculate(...))now uses!. In test spec_state_is_terminal_true, the call tospec_is_terminal(...), which does not exist, is nowspec_state_is_terminal(...). Removed the stray}before the invariants. Tests: given to const (16)…specs/fpga/stoch_round.t27specs/fpga/subth_clk.t27if (not config.enable_subth)now uses!.for (domains) |domain| { if (not clk_domain_is_idle(...)) ...}becamefor i in 0..NUM_CLK_DOMAINS { const domain = domains[i]; if (!clk_domain_is_idle(...)) ...}. Removed the stray}before the invariants. T…specs/numeric/formats.t27specs/numeric/gf128.t27given->const ...;,try std.testing.expect(x = e);->const x = e;,try std.testing.expect(e);->assert(e);. Removed the stray}before the invariants. The Zig-style functions (u128 GF128, @as/@truncate, std.math.*…specs/numeric/gf256.t27const, expect ->assert(...). In gf256_from_components_roundtrip,expect(mant.parts[0] = ...)becamevar mant = gf256_zero(); mant.parts[0] = 0x8000000000000000;. Removed the stray}before the invariants. gf256_neg_tog…specs/numeric/gf64.t27given x = eandtry std.testing.expect(x = e);becameconst x = e;, and other expects becameassert(...). The stray module-level}was removed. The builtin casts @as+@intcast, @floatFromInt and @intFromFloat becamex as T; …specs/numeric/goldenfloat_family.t27specs/numeric/int4.t27specs/numeric/int8.t27}before the invariants section. In bracedtest "..." {}blocks,given x = E/given x: Int8 = Ebecameconst x = E;/const x: Int8 = E;(83),try std.testing.expect(x = E);bindings (including typedb: Int8 = 30) became `con…specs/numeric/nf4.t27specs/numeric/tri_net_formats.t27for (const TRI_NET_FORMATS) |fmt|->for (TRI_NET_FORMATS) |fmt|(9 places). Gave braces to 7 braceless test/invariant blocks whose body is a braced for loop, because the clause form has nofor. The helperfn @floor(an @-builtin name, emitted inva…Approximations, please check:
specs/fpga/avs_controller_48.t27: Named struct Avs48Cmd replaces the anonymous return struct of decode_avs_48_cmd; fields and types are the samespecs/fpga/avs_controller_96.t27: Named struct Avs96Cmd replaces the anonymous return struct of decode_avs_96_cmd; fields and types are the samespecs/fpga/avs_reconf.t27: The and-chain in avs_reconf_config_valid is inferred from the corruption pattern: the leftover ';);' and the same 'and X' to 'try std.testing.expect(X);' rewrite seen in the tests. No uncorrupted original exists anywhere I checked; local website snapshots have the same broken text. fbb_active_path.t27 (not my file) has the identical break in fbb_path_config_valid.specs/fpga/dfs_gate.t27: invariant dfs_node_children_bound: the outermostdfs_node_add_child(had no second argument and no closing paren, so the expression could not parse. It now reads..., 22), 23).child_count <= 4, giving it child 23: the file's own 4-child pattern (20..23, as in test dfs_node_add_child_overflow), with a comment. The other possible repair, dropping one call, would give 3 children; either way the asserted bound <= 4 holds.specs/fpga/dfs_gate.t27:[_]DfsNode{} ** Npadding is kept as written; t27 lowers it oddly (e.g..{ _ } ** 252in Zig), but that is a compiler quirk, not a change here.specs/fpga/drowsy_ret.t27: Invariant drowsy_config_enable_both_or_neither: in the base, a trailing lineor (config2.enable_light and not config2.enable_deep)came after the closedexpect(...);. It could not parse there. It is now a second assert beside the unchanged config1 check, with a comment saying so. Both assertions hold with the current function bodies.specs/fpga/drowsy_ret.t27: drowsy_wake_cycles: the statement switch (DrowsyState.light => return X,...) is now an equivalent if-chain, because gen-verilog lowers short enum names in a switch to bare, unbound identifiers.specs/fpga/fbb_active_path.t27: fbb_path_config_valid is now the conjunction of fbb_path_index_valid, fbb_latency_valid and fbb_bandwidth_valid. The reviewer judged this sound.specs/fpga/fbb_active_path.t27: One Zig error remains: the enum varianterroris a Zig keyword, and the backend emits.errorinstead of@"error". The name comes from base text, and the missing escape is a compiler gap.specs/fpga/fp8_e5m2.t27: @bitcast(abs_value) (f32 -> u32) is kept, becauseashas no bit-reinterpret form. The canonical specs/numeric/gf16.t27 uses the same intrinsic. A comment is added.specs/fpga/fp8_e5m2.t27: @as(u5, @intcast(subnormal_shift)) is kept, becauseashas no u5 form. A comment is added.specs/fpga/fp8_e5m2.t27: std.math.* calls are kept as written, the same as canonical gf16.t27.specs/fpga/fp8_e5m2.t27: Zig check (zig 0.15.2, test -fno-emit-bin on the gen output): the reviewer's @intCast-on-float and truncation-trap errors are gone. What remains comes from base text. First, fp8_from_f32 and fp8_magnitude are undefined. Second, the literalssign_bitandsignare untyped (comptime_int). Third, 5 invariants fail at comptime: add/sub zero identity, div one identity, lerp one end and scale roundtrip. The cause is that upstream fp8_encode_f32 usesf32_mant >> 22, which keeps 1 mantissa bit instead of 2 (>> 21 would be right). So 50.0 encodes as 40.0, and 40.0 re-encodes as 32.0. I did not change this; it is an upstream logic bug.specs/fpga/gf128.t27: Only the Zig backend passes@as(u128, <f64 expr>)through verbatim; real Zig would want @intFromFloat. C and Rust get the exact original cast.specs/fpga/gf16_to_posit16.t27: gf16_from_bits keeps the base Zig @truncate intrinsics, with a comment. This follows the same precedent as the fp8 u5 cast.specs/fpga/gf16_to_posit16.t27: COMPILER BUG (Zig and Verilog miscompile, not in the spec): in posit16_decode, common_subexpr_elim (bootstrap/src/compiler.rs, near line 23824) takes the top-levelbits = bits << 1;as a seed. It then replaces the later loop-bodybits = bits << 1;in the exponent and mantissa loops with_csetemps. Those temps are hoisted abovevar bitsand never invalidated when bits is reassigned. Zig reports 'undeclared identifier bits', and gen-verilog emits the same 6_cselines. C and Rust are correct. It is triggered becausebits <<= 1has no t27 form. I did not work around it, because the spec is valid t27 and the fix belongs in the compiler.specs/fpga/gf16_to_posit16.t27: Upstream logic, kept: invariant gf16_to_posit16_infinity_to_naR fails at comptime. For +inf, gf16_to_posit16_raw returnssign << 15, which is 0 (zero), not NaR 0x8000.var regime = 0;is untyped, as reported before.specs/fpga/gf256.t27: 128-bit casts are written as@as(u128, e)/@as(i128, e)because t27asdoes not accept 128-bit types. In Rust and C these lower to realas/ C casts, the same conversion semantics as the originalas.specs/fpga/gf256.t27: Zig only: the backend passes@asthrough verbatim, whereasasgets type-aware lowering. So the u128->i128 casts (line 75 of the generated Zig), f64->u128 (mant) and f64->i128 (xi) would need@intCast/@intFromFloatin Zig. I tried@as(i128, @intFromFloat(x)): it breaks gen-c, which emits@intFromFloatverbatim, and gen-rust, which emits(x as _) as i128, so I rejected it. This is a backend gap, not a spec change.specs/fpga/gf256.t27: Upstream content defects are left as they are, because fixing them would invent behaviour, and they alone already stop the generated Zig from compiling:BITS : u8 = 256(u8 overflow, also the onet27c typecheckerror),1 << 158in u128 (Zig u7 shift-amount error), the externalsacred_physics/goldenfloat_family::get_format_by_name/Option, and theletreassignments that typecheck warns about. t27 master has since rewritten specs/numeric/gf256.t27 entirely.specs/fpga/gf32_to_fp32.t27: The second line of invariant mantissa_masks_correct was 'try std.testing.expect(B);' after 'assert A'. It is now a second 'assert B;', which is equivalent to 'assert A and B'.specs/fpga/holo_mux_x4.t27: Switch arms of the form=> return Xdo not parse, so the function now returns a switch expression with identical arms, includingelse => layer.specs/fpga/holo_mux_x4.t27: The capture-for form parses, but gen-c emits its body once instead of looping. The indexed range loop over NUM_INPUTS (the declared array length) iterates the same way in every backend.specs/fpga/holo_mux_x4.t27: Generated Zig still does not compile because of backend defects outside this file: const-ident switch patterns are emitted as.HOLO_LAYER_NORTH, and[_]HoloInput{...}loses its element type.specs/fpga/int4.t27: encode:var raw : u4 = clamped;is a value-level approximation ofas u4, as described in the comment. The Zig intrinsic route does not work here. @truncate cannot go from i8 to u4 (it needs the same signedness), and @intcast traps on the negative first assignment.specs/fpga/int4.t27: The domain of invariant int4_abs_is_non_negative is -8..7 (min_value()..max_value()). The original never boundvals; the reviewer confirmed the domain holds.specs/fpga/int4.t27: The remaining Zig errors come from the u4 narrowing: encode, plusInt4{ raw = v }with v: i8 in the two decode tests. There is also a base-text error: test int4_memory_ratio_vs_fp32 calls abs() on a float, but the spec defines its own abs(Int4).specs/fpga/int8.t27: fn and / fn or renamed to and_bits / or_bits (keyword collision; the only way to declare them)specs/fpga/int8.t27: The BDD set clauses 'then P(v) for v in {...}' are written as typed-array index loops. Element types are chosen from use: i32 for encode() inputs, u8 for raw bytes and shift amountsspecs/fpga/int8.t27: int8_decode_negative_values: explicit '(e as i32) - 256' widening of the u8 raw byte (same value as C integer promotion)specs/fpga/int8.t27: '{-128..127}' written as the exclusive t27 range -128..128, which covers every Int8 value including 127 (commented)specs/fpga/lane_l_precheck.t27: PrecheckFlags:packed struct(u8)cannot be written in t27, so it is nowpacked struct, with a comment recording the u8 backing. Field widths are unchanged (u1 x4 + u4 = 8 bits). As the reviewer noted, the current compiler emits it as a plain Zigstruct, sopackedsurvives only in the source.specs/fpga/lut_npu_81_entry.t27: Zig index-capture loops became index ranges over the fixed array length 9.specs/fpga/lut_npu_81_entry.t27: The switch statement became a switch expression assigned to op, with the same arm values.specs/fpga/purkinje_thermal_gate.t27: As in int8,giveninside braced tests is written as aconstlocal.specs/fpga/sparse_mask.t27: mask_generate: the statement-arm switch became an if/else-if chain, with a comment.specs/fpga/sparse_mask.t27:[_]i8{1, 2, 3, 4} ** 4is written out as a 16-element literal, with a comment.specs/fpga/sparse_mask.t27: The remaining Zig errors are all base text.set_bits * 100anddensity * MASK_SIZEoverflow u8. mask_invert shifts a u16 by 16. Invariant mask_apply_preserves_unmasked expects values[7] == 8, which was known. Invariant mask_apply_masks_zero expects values[8] == 0 under mask 0xFF00, but bit 8 is set, so the value is kept.specs/fpga/sparse_skip.t27: The remaining Zig error is known upstream: invariant processed_plus_skipped_equals_size passes an 8-element vector to a [16]i8 parameter.specs/fpga/spec_exit.t27:giveninside braced tests is written as aconstlocal.specs/fpga/spec_exit.t27: The enum memberSpecState.erroris kept, because renaming it would change the API. Zig rejectserroras a member name, so generated Zig will not compile until the backend escapes it as@"error".specs/fpga/stoch_round.t27: switch with block arms written as an equivalent if/else-if chainspecs/fpga/stoch_round.t27: for (values, 0..) |val, i| written as the index loop for (0..8) |i| { const val = values[i]; ... }, since the parameter is [8]i16specs/fpga/stoch_round.t27: 4 named structs (RngStep, StochRoundStep, VectorStochStep, StochRoundFields) replace the anonymous return structs; fields and types are the samespecs/fpga/stoch_round.t27: try std.testing.expect(name = expr) read as the binding 'const name = expr', because it cannot be an assertionspecs/fpga/subth_clk.t27: The capture-for is now an indexed range loop over NUM_CLK_DOMAINS (the parameter's declared length), for the same gen-c reason as holo_mux_x4.specs/numeric/formats.t27: The body-less prototype became an empty body '{ }', following the t27 master formats.t27 convention. No behaviour was invented, but the Zig/C output is an empty fn.specs/numeric/formats.t27: In invariant ternary_to_f32_is_inverse, 'const values = [_]f32{...}; for (values) |v|' became '[5]f32{...}; for i in 0..5 { const v = values[i]; ...}', as in master.specs/numeric/formats.t27: In the benches, 'for (0..1000) |_|' became 'for _ in 0..1000'.specs/numeric/gf64.t27: @bitcast(abs_value) (f64 -> u64) is kept, the same as canonical gf16.t27. A comment is added.specs/numeric/gf64.t27: std.math.*, optional returns and.?are kept.specs/numeric/gf64.t27: The remaining Zig errors are all base text: the u16/i16 constant overflows (EXP_MAX, SPECIAL_EXP, BIAS), the untypedconst sign = if (value < 0.0) -1 else 0;, and an i16 test value of 131072.specs/numeric/int4.t27: The misused 'try std.testing.expect(name = expr)' is really a variable binding. It became a const/when binding, not an assertion. The same applies in the other tt-trinity-euler files.specs/numeric/int8.t27:givenis not accepted inside a braced test body, so those bindings are written asconstlocals with the same type annotation. The semantics are otherwise unchanged.specs/numeric/nf4.t27: Left as they are because they parse and lower on all targets: Zig std/builtin calls (std.math.isNan/isInf/nan/inf, @abs, @floatcast, @floatFromInt, @intFromFloat, @round), the 'var level: u3' local, and the undefined abs() in two roundtrip tests. The C/Rust output copies them verbatim.specs/numeric/tri_net_formats.t27: @floor -> floor rename: in t27 the call already bound to the file's own helper, so meaning is unchanged. In C, the new namefloorsits next to libm's floor, as the file's existingpow/abshelpers already do.🤖 Generated with Claude Code