Repository navigation
Conversation
gen-c wrote `defer S;` as `/* unsupported: StmtExpr */;` and exited 0, so the deferred statement never ran. It is now recorded on the innermost open block and written at each exit of that block, in Zig's order: at the fall-through end, before `return` (the returned value goes into a temp first, so a defer that changes what it reads does not change it), before `break`/`continue` of the loop whose body declared it; innermost block first, each block in reverse order. `errdefer`, and a `defer` with no open block, are refused by name and line instead of dropped. Regression test: bootstrap/tests/genc_defer.rs compiles and runs the C. Part of #5980. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gen_hash_c moves for c_api_contract, json, compress, filesystem, url, config and text (and each twin); no other hash moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # bootstrap/stage0/FROZEN_HASH
contrib/backend/zig/legacy/main_zig_handwritten.t27 has 27 defers, so its C moves; only gen_hash_c, built_by and sealed_at change. The lab's seal-currency gate named it (current=2eabfadaa47c). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Parked: this PR is over the foreign-line budget that #7399 (owner rule 2026-10-07, #7371) put on master while it waited for CI. It is now a draft, with auto-merge off.
No exception and no label lifts this limit. Splitting the same Rust into 80-line PRs would get around the rule rather than follow it, so I have not done that. The fix itself is still correct and tested on the t27c lab: the regression tests fail on master's compiler and pass with the fix, and |
Closes #7352
Part of #5980
What was wrong
gen-c wrote
defer S;as the comment/* unsupported: StmtExpr */;and exited 0. The deferred statement never ran: a test that depended on it trapped at run time, and one that did not passed while the cleanup it claimed never happened.What gen-c does now
The deferred statement is written at every exit of the block that declared it, the way Zig runs it:
return,breakorcontinue);return, after the returned value is read:{ T __t27_ret = <value>; <defers>; return __t27_ret; }(__auto_typewhere the fn's return type is not known);breakorcontinue, for the blocks up to and including the loop body that declared it;Blocks covered: fn bodies, invariant and bench bodies, test bodies,
if/elsebranches,whileandforbodies.errdeferis not lowered: gen-c now refuses it by name and line (gen-c: \errdefer` at line N is not lowered to C (#7352), non-zero exit) instead of dropping it. Adefer` outside any block is refused the same way.Tests
bootstrap/tests/genc_defer.rs, 9 tests. Each one compiles the generated C with-DT27_TEST_MAINand runs it, because the defect compiled cleanly and only running shows the call happened: the issue repro, reverse order, inner-before-outer onreturn, earlyreturn, value read before the defer runs,breakandcontinuerunning the loop body's defer, a loop defer not running again on the fn's return, and theerrdeferrefusal.Lab (t27c-lab),
cargo test --test genc_defer: 9 passed on this branch; withcompiler.rsandFROZEN_HASHreverted to master, 9 failed.C output over the corpus
gen-c over all 1527 tracked specs, master binary vs this branch: 13 outputs change, all of them specs that use
defer:Every diff is the same three moves and nothing else: the
/* unsupported: StmtExpr */;lines are gone, the deferred calls are written at block ends, and returns in a fn with a pending defer are wrapped as above. (read_user1 gets one unreachablempsse_closeafter an if/else whose branches both return; harmless.)cc -fsyntax-onlyerror counts are identical before and after for 12 of the 13. compress.t27 goes 36 -> 37: a return of the Zig-onlyerror.OutOfMemorynow lands in an__auto_typetemporary; the file did not compile before either. scope_exit.t27 (the t27b conformance spec for defer) now compiles and runs: "All 2 tests passed."Seals
7 of the 13 have seals; each was fresh on master and stale with the new C, and is resealed here with its twin (14 files): c_api_contract, json, compress, filesystem, url, config, text. Only
gen_hash_c(plussealed_at,built_by,tests) moves; spec, Rust, Verilog and Zig hashes are unchanged.t27c seal --verifyexits 0 for all 7 on the lab. The other 6 have no seal on master either. One more seal sits outsidespecs/:contrib/backend/zig/legacy/main_zig_handwritten.t27(27defers), which the lab's seal-currency gate named on 5ffb207 (gen_hash_csealed 88c4ba2b8d2f, current 2eabfadaa47c). It is resealed in d8e71dd; again onlygen_hash_c,built_byandsealed_atmove. A gen-c comparison of all 28 sealed specs outsidespecs/(master 18689bc vs this fix) shows no other change, so no other seal goes stale.Foreign code
This edits Rust (
bootstrap/src/compiler.rs+ itsFROZEN_HASHseal, and a new test file), so it carriesowner-approved-foreignand atools/policy/foreign-exceptions.txtentry naming #7352.🤖 Generated with Claude Code