Skip to content

t27o: three converter defects -- static mut name, text-field str_chain, 64-bit unsigned int_ok - #11064

Merged
gHashTag merged 3 commits into
masterfrom
t27o/defects-a3
Oct 19, 2026
Merged

gHashTag merged 3 commits into
masterfrom
t27o/defects-a3

Conversation

@gHashTag

@gHashTag gHashTag commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Closes #10829
Closes #10834
Closes #10864

Three open defects of the Rust-to-.t27 converter (specs/tools/t27o/rust_convert.t27), one commit each. In each commit the spec is the rule: the test that pinned or exposed the defect was written first and failed on the merge-base code under t27b test, then the rule was fixed, t27c gen-rust regenerated gen/rust/tools/t27o/rust_convert.rs, and the seal was re-saved. No hand-written Rust and no glue changed.

The three defects

Issue Failing test first (on merge-base code) Fix in the spec
#10829 static mut recorded as mut a_static_mut_is_named_by_its_name_not_by_the_mut_before_it (assert failed) and the const mut row of const_item_struct_item_and_enum_item_write_the_declarations; the old known_defect_a_static_mut_... pin was replaced const_item steps over mut before it reads the name; item_named skips a mut after const / static (as const_hit already did); index_decls enters only the name. The refusal now names COUNT, sum (a let mut) converts, and fn cnt() { COUNT } is "uses refused COUNT", not "undeclared"
#10834 str_chain never opens with a text field str_chain_opens_with_a_text_field_of_the_head_names_struct (14 chains plus the end-to-end repro of the issue; assert failed); the old known_defect_str_chain_... pin was replaced the field branch asks for no ( after the field (as recv_class does), not for one. let t = n.value.trim(); t.starts_with("a") now converts to the same call as the inline chain
#10864 int_ok(64, unsigned) overflows in bit(64) int_type_width_int_type_signed_and_int_ok_say_what_an_integer_type_holds (new 64-bit unsigned asserts) and a_64_bit_unsigned_parameter_is_sampled_from_its_body_like_the_narrower_ones (u32, i64, u64, usize, u128, i128, isize): both failed with integer overflow at line 3991 (* on u64) an unsigned type of 63 bits or more holds every non-negative sample, so int_ok answers true without computing bit(w); a negative sample is still no sample of an unsigned type, narrower widths and signed types are unchanged. Oracle of fn g(x: usize) -> bool { x == 5 } (and u64, u128): was 0 1 2 7 10 100 18446744073709551615 only, now 4 5 6 follow, as for u32

Dogfood, before and after

The converter was compiled from the generated Rust of the merge base and of this branch (same #[path] file cli/tri mounts, same buffers as cmd_t27o) and run per file with a 60 s cap. "Before" is the merge base.

Measure Before After
$TMPDIR/t27o-loop/sample-files.txt, 29 files: fns seen / converted 573 / 40 573 / 40
...output specs for which t27b test is not a pass 2 (rc 2, "unsupported", t27c test-report would judge; capflags.rs, firmware.rs) the same 2, outputs byte-identical
...outputs that differ from before - 0 of 29
every tracked .rs of t27 (489 files): fns seen / converted 12,898 / 3,692 12,898 / 3,692, 0 outputs differ
.rs of trios, trinity, trinity-fpga (839 files): fns seen / converted 8,685 / 987 8,685 / 987, 0 outputs differ
oracle harness (--oracle), 1,326 files: sample calls 28,706 37,244 (+8,538; 91 files differ)
those 91 files run end to end (harness, rustc, run, t27b test of spec + samples): files that pass 67 67
...oracle tests that pass / fail 1,018 / 0 1,021 / 0
...files that went from pass to not pass - 0

The convert counts do not move by design: the shapes of the three defects (static mut next to a let mut, a let over a text field, a usize body-derived sample) change refusals and samples in rare places, none in these files. What moves is the oracle: usize / u64 / u128 parameters now get their body-derived samples (+8.5 k calls, +3 passing oracle tests, no failing one). The other verdicts of those 91 files are identical before and after: 15 stop at a t27b parse or typecheck error of the converted spec (an existing converter output defect, unrelated), 5 at "unsupported", 4 at a harness that does not compile alone; a few rows only moved a line number because more assert lines precede the failing one. Three harnesses (verilog_emit, verilog_plan, rust_convert) hit my 90 s rustc cap in the first "after" run while two jobs ran at once; re-run alone they compile in 5 to 51 s and give the same verdict as before (counted in the 15 and 5 above).

Checks

  • t27b test specs/tools/t27o/rust_convert.t27: 333 passed (332 before; the two pins became two tests and one test grew). t27c test-report: 333 / 333, vacuous passes 0 of 333. t27c coverage: 395 / 395 fns (100 %). rust_std.t27: 51 / 51, vacuous 0, coverage 149 / 149.
  • t27c gen-rust of rust_convert.t27 is byte-identical to the committed gen/rust/tools/t27o/rust_convert.rs; zig_convert.t27 and spice_convert.t27 generate byte-identical Rust to what is committed.
  • t27c seal --verify: rust_convert (re-saved, tests 333 / 333 under Zig), rust_std, zig_convert, spice_convert, zig_std, ledger, golden, install, service all MATCH. No dependent seal needed a re-save and spice_convert.t27 is untouched.
  • python3 -I tools/dupe_scan.py: 738 of 15,471 bodies in 218 groups at the merge base and at the head; the 15 findings are the same set, none is new (no helper was added). python3 -I tools/check_devhome_paths.py: OK. The spec has 0 non-ASCII bytes.
  • Own-language gate, compiled from the merge base (gen/c/policy/own_language.c, git diff --numstat --no-renames BASE...HEAD): 179 added / 58 deleted lines in three files (specs/tools/t27o/rust_convert.t27 153 / 39, generated gen/rust/tools/t27o/rust_convert.rs 17 / 10, .trinity/seals/t27o_T27oRust.json 9 / 9); hand-written foreign lines added: 0; the gate prints nothing and exits 0.
  • No type was declared, so tri types ratchet has nothing to ratchet.

Not run

  • cargo build -p tri and the real tri t27o / tri t27o --check binary: the disk was short, and nothing in cli/ changed. I compiled the same generated file with rustc and replayed the steps of --check (oracle harness, rustc, run, t27b test of spec plus samples) by hand on the 91 files whose harness changes; the files that need #[path] neighbours (4 of them) did not compile alone, before or after.
  • tri t27o-sweep with its cache, t27c suite, ./scripts/tri test, and the CI workflows beyond the gates listed above; docs/reports/t27b_expectations.json and every ledger were left to their owners.
  • t27c test-report of the two dogfood outputs that t27b calls unsupported: their text is byte-identical before and after.
  • The commit hook printed "no tri binary is built" in this worktree, so its gates did not run locally; each commit message carries its closing line and the co-author line.
  • The seal's built_by field now names the t27c I used (b258b9007), an earlier build than the one that sealed master.

Not set to auto-merge.

🤖 Generated with Claude Code

claude and others added 3 commits October 19, 2026 20:58
…oses #10829)

const_item read the item name from the token it stopped at, which for a
static mut is the keyword mut: the refused item went into the name table
as mut, and every later fn that spells mut (a let mut, a &mut) was
refused with it. item_named had the same blind spot for declared_type
(a static mut S declared mut, not S), and index_decls added both tokens.

const_item now steps over the mut before it reads the name, item_named
skips a mut after const and static the way const_hit already did, and
index_decls enters only the name. The refusal names COUNT, a fn that
spells mut converts, and a fn that reads COUNT is refused as a user of a
refused name, not as one that reads an undeclared name.

The test that pinned the defect (known_defect_a_static_mut_is_named_mut_...)
now states the fixed behaviour; it failed on master before the change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… let t = n.value.trim() binds a text (Closes #10834)

The field branch of str_chain asked for a ( right after the field name,
i.e. node.value( -- a method call, not a field -- so for
node.value.trim() the token after value is a dot, the branch was never
taken, base stayed false and the binding was no text. recv_class reads
the same step with !tok_is(.., "("); str_chain now does too.

let t = n.value.trim(); t.starts_with("a") converts like the inline
n.value.trim().starts_with("a"): t is a text and takes the text entry
of the std table. A field of another type, a field the struct does not
have and a method call (x.value(), x.trim()) are still no text chain.

The test that pinned the defect (known_defect_str_chain_...) now states
the fixed behaviour with fourteen heads and the end-to-end repro of the
issue; it failed on master before the change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…computing 2^64, so usize / u64 / u128 parameters are sampled from the body (Closes #10864)

int_ok asked an unsigned type for m < bit(w), and bit(64) is 2^64: one
doubling past what a usize holds. Under t27b and t27c test-report the
call trapped (integer overflow, * on u64); in the compiled converter it
wrapped to 0, so m < 0 was false for every sample. Either way a usize,
u64 or u128 parameter got none of the body-derived samples of #9102
(the signed 64-bit types and every narrower type were fine).

Every sample num_value returns is below 2^62 and the samples beside it
are at most 2^62, so an unsigned type of 63 bits or more holds them all;
int_ok now says so without computing bit(w). A negative sample is still
no sample of an unsigned type, and the narrower widths and the signed
types are unchanged.

Oracle of fn g(x: usize) -> bool { x == 5 }, and u64 and u128 likewise:
before, 0 1 2 7 10 100 18446744073709551615 only; now 4 5 6 follow, as
they do for u32. The int_ok test holds the 64-bit unsigned cases (a
direct call trapped on master), and a new test walks derived_at and
dcount for u32, i64, u64, usize, u128, i128 and isize.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-19 14:09:25 UTC

Summary

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

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

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=57b91bb96334 != 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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment