fix(rust-backend): prefix the dereference, and give an out-pointer a safe type - #3423
Merged
Conversation
…safe type Closes #3422 Refs #3420 Zig spells a dereference postfix, `count.*`, and the corpus is written that way. gen-rust emitted it verbatim, so the file did not even tokenise: "error: unexpected token: `*`" -- 70 sites across 6 specs. And a bare `*T` parameter became `*mut T`, whose every dereference needs an `unsafe` block this emitter never writes, so the 81 files carrying such a parameter could not have compiled in any spelling. Zig handles `.*` natively because it is Zig syntax; C renders the parameter `size_t*`. Rust was the only column that could not compile. The repair is narrow, and the narrowing was measured rather than assumed: version rustc accepts coded diags introduced revealed master 433 2937 -- -- every position 435 2724 9 1 parameter only 439 2713 0 1 A struct field cannot take `&mut T` without a lifetime -- `pub fail: &mut ACTrieNode` is E0106 -- so the rewrite applies in parameter position only. The narrow version is better on every axis at once. How this was found matters more than the fix. Ten of the seventeen rings/* crates name a spec that exists, and the new tools/check_ring_spec_drift.py compares them. `ring-099` read DRIFTED on exactly two functions, and the only difference was `count: &mut usize` in the hand-written model against `count: *mut usize` from the spec. The hand-written code was right again -- the second time in two passes. The tool classifies each pair CONVERGED / DRIFTED / UNRELATED and its --self-check requires every verdict to be reachable. It carries the warning it earned: a matching signature is NOT matching behaviour. ring-090 read 16 of 16 identical and still disagreed on 126 of 1190 differential cases (#3420). Today: 2 CONVERGED, 0 DRIFTED, 7 UNRELATED -- where UNRELATED mostly means the doc comment names a spec the crate was never generated from. 135 seals across 68 specs were refreshed by `t27c seal --save`, duplicates included. Two tests, one of which compiles and RUNS the output and asserts the caller observes the write. Mutation-checked: removing the deref fix fails it, and removing the parameter narrowing fails it with E0133. FROZEN_HASH updated in the same commit, as M5 requires. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gHashTag
enabled auto-merge (squash)
September 7, 2026 22:13
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
This was referenced Sep 7, 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 #3422 · Refs #3420
The emitted Rust for any spec using an out-pointer did not tokenise.
Zig spells a dereference postfix; Rust spells it prefix. The corpus is written in the Zig shape — 70 sites across 6 specs — and gen-rust emitted it verbatim.
Underneath it, a second defect: a bare
*Tparameter became*mut T. Every dereference of a raw pointer needs anunsafeblock and this emitter writes none, so the 81 files carrying such a parameter could not have compiled in any spelling. Zig handles.*natively because it is Zig syntax; C renders the parametersize_t*. Rust was the only column that could not compile.The narrowing, measured rather than assumed
&mutin every positionA struct field cannot take
&mut Twithout a lifetime —pub fail: &mut ACTrieNodeisE0106— so the rewrite applies to parameters only. The narrow version is better on every axis at once. Third time this session that narrowing beat widening.How it was found matters more than the fix
Not by reading the compiler. Ten of the seventeen
rings/*crates name a spec that exists, and the newtools/check_ring_spec_drift.pycompares them.ring-099read DRIFTED on exactly two functions, and the sole difference wascount: &mut usizein the hand-written model againstcount: *mut usizefrom the spec.The hand-written code was right again — the second time in two passes (#3420 was the first, where the spec wrapped and the model saturated).
The tool, and the warning it carries
CONVERGED / DRIFTED / UNRELATED, with a
--self-checkthat requires every verdict to be reachable and the trailing-comma normalisation to hold.Today: 2 CONVERGED, 0 DRIFTED, 7 UNRELATED — where UNRELATED mostly means the doc comment names a spec the crate was never generated from.
ring-097shares 7 names withproof_trace.t27and zero signatures: two designs sharing a vocabulary.Everything else guarded itself
check_seal_currency.pynamed 135 stale seals across 68 specs;t27c seal --saverefreshed every duplicate, including the two whose filenames are quoted type strings.Tests, and a correction to my own method
Two new. One compiles and runs the output and asserts the caller observes the write.
Mutation-checked — after I caught my own harness lying: it first reported the parameter mutant as surviving, because a broken anchor made it run
cargo teston an unmutated tree and report that pass. Re-run with the anchor asserted, the mutant dies with the exacterror[E0133]: dereference of raw pointer is unsafe. A mutation run that does not verify the mutation landed measures the same thing twice.bootstrap/stage0/FROZEN_HASHupdated in the same commit, as M5 requires.