Files
BFM-decomp/cookbook/C0417.md
T

2.5 KiB

§376 ★★★ — A STANDALONE match_one CLOSENESS OF 0 IS A CLAIM ABOUT THE BODY, NEVER ABOUT THE TU (P31 S69; measured 0/28)

The claim. S68 closed with "32 FREE BANKS ARE WAITING", 10 of them personally verified with match_one at closeness 0 (NOCOMPILE-UNDECLARED-FIXED), the other 22 classified INTEG-STANDALONE-MATCH — "they already match standalone and were misfiled as failures".

The measurement. 4 had banked since the checkpoint was written. The remaining 28 gated 0/28, and not one failure was a codegen miss:

CC1-FAIL : ov_SC01_080_jr_8017AE2C.c:4365: too few arguments to function `func_8017D72C'
PLUMBING : ov_SC04_010_jr_8017BEBC.c:3714: conflicting types for `func_8017D6CC'
PLUMBING : ov_SC07_000_jr_8017BEBC.c:4406: conflicting types for `func_8012AD44'

The mechanism. match_one compiles the draft ALONE. The TU it must actually live in already carries a forward declaration written for a call site — extern void func_8017D6CC(void); — while the draft's true signature is void func_8017D6CC(void *a0). C rejects the pair. The body was right the whole time; the TU would not accept the signature.

The autodecl trap. The NOCOMPILE-UNDECLARED-FIXED tier adds an extern derived from the target's own .s so the STANDALONE probe compiles. In the real TU that extern is a SECOND conflicting declaration, so the patched arm is strictly WORSE in-tree than the raw draft. Gate the raw draft, never the autodecl arm.

The recipe (byte-neutral: a declaration emits no code, so the gate stays the sole arbiter):

tools/fix_arity_callers.py --funcs <FN[,FN…]> --binary <B> --any-proto --apply \
    --drafts <dir> --journal .run/<id>/<B>.json      # no-protos every conflicting caller decl
<gate>                                               # then, and only then, the byte gate
tools/fix_arity_callers.py --undo-journal … --keep <banked>   # revert what did not buy a match

S69: 32 caller decls no-proto'd across 10 binaries, plus 12 in src/shared/engine_core.h — that second set makes the edit fleet-tier, so R22 is mandatory, not optional (§65a).

The law. An oracle that compiles a function in isolation cannot see the declarations that surround it. Every "already matches, just bank it" claim must state WHICH compilation it survived. This is the silently-narrowed-tool-scope class (accelerators #15) applied to a banking claim: a TRUE number — closeness 0 — about a NARROWER world than the reader believes.