Skip to content

fix(rust-backend): prefix the dereference, and give an out-pointer a safe type - #3423

Merged
gHashTag merged 1 commit into
masterfrom
tool/ring-spec-drift
Sep 7, 2026
Merged

fix(rust-backend): prefix the dereference, and give an out-pointer a safe type#3423
gHashTag merged 1 commit into
masterfrom
tool/ring-spec-drift

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Closes #3422 · Refs #3420

The emitted Rust for any spec using an out-pointer did not tokenise.

error: unexpected token: `*`
  --> e2e.rs:35:15
   |
35 |         count.* = (i + 1);

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 *T parameter became *mut T. Every dereference of a raw pointer needs an unsafe block 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 parameter size_t*. Rust was the only column that could not compile.

The narrowing, measured rather than assumed

version rustc accepts coded diagnostics introduced revealed
master 433 2937
&mut in every position 435 2724 9 1
parameter position 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 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 new tools/check_ring_spec_drift.py compares them. ring-099 read DRIFTED on exactly two functions, and the sole 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 (#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-check that requires every verdict to be reachable and the trailing-comma normalisation to hold.

A matching signature is not matching behaviour, and this tool cannot tell you that it is. ring-090 measured CONVERGED at 16 of 16 identical signatures and still disagreed with its spec on 126 of 1190 differential cases. Treat CONVERGED as worth building a differential harness for, never as these agree.

Today: 2 CONVERGED, 0 DRIFTED, 7 UNRELATED — where UNRELATED mostly means the doc comment names a spec the crate was never generated from. ring-097 shares 7 names with proof_trace.t27 and zero signatures: two designs sharing a vocabulary.

Everything else guarded itself

check_seal_currency.py named 135 stale seals across 68 specs; t27c seal --save refreshed 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 test on an unmutated tree and report that pass. Re-run with the anchor asserted, the mutant dies with the exact error[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_HASH updated in the same commit, as M5 requires.

…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
gHashTag enabled auto-merge (squash) September 7, 2026 22:13
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-07 22:13:31 UTC

Summary

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

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

Seal Status

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

@gHashTag
gHashTag merged commit 1cd5d3d into master Sep 7, 2026
30 of 32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gen-rust emits Zig postfix deref verbatim and a raw pointer it can never dereference safely

1 participant