mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-29 07:10:32 -04:00
feat(phase-29): func_8014D820 banked (41,952 ins) + conform_decls scalar-narrowing guard
WAVE 2 (9 never-drafted exemplars, ultracode): 9/9 returned, 5 MATCH / 4 near, 2.25M tokens. BANKED: func_8014D820 (304 ins ×138) — and its agent ROOT-CAUSED the failure I left undiagnosed. It was never an assembler problem: cc1 exit 33, `conflicting types for 'Ent'` vs engine_types.h:434, surfaced by the recipe's `set -o pipefail` and MISATTRIBUTED to `as` because `as` is the last stage in the pipe (Makefile:560). Fixed by moving V4/Desc/Ent to BLOCK scope — byte-neutral and collision-proof across all 138 member TUs. Vindicates flagging it to the agent as UNVERIFIED rather than passing my own guess forward as fact (§88e). R22 clean-fleet 140/140. NEW GUARD — SCALAR NARROWING IS NOT CALLER-NEUTRAL (byte-proven, and it cost 3 gate cycles): conform_decls treated all decl type changes alike. A POINTER change is caller-neutral (func_80179B74 conformed 1,600 sites s16*/short* -> u16* and stayed byte-identical fleet-wide). A SCALAR WIDTH change is NOT: narrowing `s32 a0` -> `u16 param_1` changes argument promotion at every call site. MEASURED on func_80175DA8: decls reverted -> gate says PLUMBING; conform applied -> gate says DIFF. The conform did not fix the draft, it changed the CALLERS. Now warned explicitly (not refused — the draft's sig is still byte-truth for the callee and the gate arbitrates), with the instruction that a DIFF after this conform means examine the callers (§17a-1 pair), not the body. Verified the guard discriminates: fires on func_80175DA8 (s32->u16), silent on func_80179B74. STILL OPEN from wave 2: func_80176218 + func_80175AB8 (DATA-symbol conflicts, D_80078EB4 / D_8011F7BC -> reconcile_decls) · func_80175DA8 + func_80135EB0 (need the §17a-1 caller pair, not a bare conform) · 4 near-misses with precise residuals recorded (func_80176734 129 length-drift, func_80140958 49 inverted-hoist, func_80177B5C 19 sched tie, func_8017C974 83 -> permuter).
This commit is contained in:
+1679
-1680
File diff suppressed because it is too large
Load Diff
@@ -4,16 +4,16 @@
|
||||
# cross-binary collapsible-byte leverage: docs/duplicates.cross.md.
|
||||
|
||||
# THREE progress metrics (all matter — see the labels):
|
||||
FLEET fn-count byte-ident: 317487 / 353720 = 89.76% (REAL+LINKED+empties; FUNCTION-count, ×134-inflated — one crack counts per overlay)
|
||||
FLEET instr-weighted : 10841039 / 13141652 = 82.5% (shipped .text across main + resident + 138 overlays; the decomp.dev-DISPLAY number)
|
||||
FLEET distinct-code(uniq): 3979994 / 5634875 = 70.6% (65725/87459 unique fns; the DISTINCT-RE number)
|
||||
FLEET fn-count byte-ident: 317488 / 353720 = 89.76% (REAL+LINKED+empties; FUNCTION-count, ×134-inflated — one crack counts per overlay)
|
||||
FLEET instr-weighted : 10841343 / 13141652 = 82.5% (shipped .text across main + resident + 138 overlays; the decomp.dev-DISPLAY number)
|
||||
FLEET distinct-code(uniq): 3980298 / 5634875 = 70.6% (65726/87459 unique fns; the DISTINCT-RE number)
|
||||
MAIN game-code weighted : 436 / 60201 = 0.7% (INCLUDED in the fleet numbers above since 2026-07-22 — roadmap §1 metrics contract; LINKED-excluding Ghidra sig dated 2026-06-14; caveat is R34: no independent second oracle for a PS-X EXE, NOT drift)
|
||||
(fleet EXCLUDING main, for continuity with pre-2026-07-22 readings: 10840603 / 13081451 = 82.9%)
|
||||
(fleet EXCLUDING main, for continuity with pre-2026-07-22 readings: 10840907 / 13081451 = 82.9%)
|
||||
|
||||
FLEET REAL substantive : 315632 (of which dedup-shared 239530 via 1886 groups / 239604 instances)
|
||||
FLEET REAL substantive : 315633 (of which dedup-shared 239530 via 1886 groups / 239604 instances)
|
||||
FLEET LINKED PsyQ objs : 959
|
||||
FLEET NON_MATCHING : 7 (0 in any default build — G4)
|
||||
FLEET INCLUDE_ASM stubs : 36226
|
||||
FLEET INCLUDE_ASM stubs : 36225
|
||||
FLEET matchable : 353720
|
||||
|
||||
| binary | REAL | shared | LINKED | byte-ident | matchable | byte-ident % |
|
||||
@@ -28,7 +28,7 @@ FLEET matchable : 353720
|
||||
| ov_SC01_008 | 2256 | 1734 | 0 | 2258 | 2426 | 93.1% |
|
||||
| ov_SC01_009 | 2282 | 1735 | 0 | 2283 | 2507 | 91.1% |
|
||||
| ov_SC01_074 | 2260 | 1735 | 0 | 2262 | 2425 | 93.3% |
|
||||
| ov_SC01_077 | 2440 | 1702 | 0 | 2442 | 2585 | 94.5% |
|
||||
| ov_SC01_077 | 2441 | 1702 | 0 | 2443 | 2585 | 94.5% |
|
||||
| ov_SC01_080 | 2298 | 1738 | 0 | 2298 | 2512 | 91.5% |
|
||||
| ov_SC01_084 | 2305 | 1738 | 0 | 2310 | 2579 | 89.6% |
|
||||
| ov_SC02_000 | 2358 | 1773 | 0 | 2358 | 2683 | 87.9% |
|
||||
|
||||
@@ -1809,7 +1809,329 @@ void func_8014D738(void)
|
||||
|
||||
DEFINE_func_8014D790() /* dedup: shared engine-core @0x8014D790 (src/shared) */
|
||||
|
||||
INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077_after", func_8014D820);
|
||||
// @class: schedule
|
||||
// @stuck: none — MATCH (304/304), symcheck SYMS-OK 12/12, and the whole-TU cc1 stage now compiles CLEAN
|
||||
//
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// SESSION-22 RE-DERIVATION + THE ROOT-CAUSE FIX FOR THE "ASSEMBLER-STAGE" FAILURE
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// The SESSION-21 draft did reach match_one MATCH (re-verified this session, 304/304)
|
||||
// and symcheck SYMS-OK — but the whole-binary build died with an opaque `Error 33`
|
||||
// that was logged as "an ASSEMBLER-stage failure I did not finish diagnosing".
|
||||
//
|
||||
// IT IS NOT AN ASSEMBLER FAILURE. Stage-by-stage isolation of the real pipeline
|
||||
// (cpp | cc1 | maspsx | as) over a scratch copy of src/ov_SC01_077/ov_SC01_077_after.c
|
||||
// with this function spliced in place of its INCLUDE_ASM gives:
|
||||
// cpp rc=0
|
||||
// cc1 rc=33 <-- ".../tu.c:1901: conflicting types for `Ent'"
|
||||
// "src/shared/engine_types.h:434: previous declaration of `Ent'"
|
||||
// maspsx rc=0
|
||||
// as rc=0 (only benign "used $at without .set noat" warnings)
|
||||
// `33` is CC1's exit code, surfaced by the recipe's `set -o pipefail` (Makefile:560).
|
||||
// Because `as` is the LAST stage in that pipe, make attributes the failure to the
|
||||
// assembler — the same misread that made func_8012AAAC's "opaque Error 33" look
|
||||
// assembler-shaped until it was isolated stage by stage.
|
||||
//
|
||||
// ROOT CAUSE: the draft declared `typedef struct Ent { ... } Ent;` at FILE scope.
|
||||
// `Ent` is already a shared engine type (src/shared/engine_types.h:434,
|
||||
// typedef struct { u16 guard; u16 pad; u16 field; u8 rest[14]; } Ent;
|
||||
// a 20-byte struct), pulled into every overlay TU through
|
||||
// src/ov_SC01_077/../shared/engine_core.h. gcc-2.7.2 rejects the redefinition
|
||||
// outright (an ERROR, not the "type mismatch with previous external decl"
|
||||
// WARNING that the rest of this TU is full of), so cc1 aborts.
|
||||
//
|
||||
// THE FIX, and why this shape: all three helper types are now declared at BLOCK
|
||||
// scope inside func_8014D820 itself. That is strictly better than renaming to
|
||||
// `Ent_8014D820` at file scope, because this function is an h_seq family EXEMPLAR
|
||||
// with 138 members: a file-scope type name has to stay unique against 138 distinct
|
||||
// overlay TUs *and* against whatever engine_types.h grows next, while a block-scope
|
||||
// typedef can never collide with anything, in any TU, ever. It also matches the
|
||||
// fleet convention — src/shared/engine_core.h contains ZERO `typedef struct`
|
||||
// (all 0 of them); every DEFINE_ macro body uses engine_types.h types or local ones.
|
||||
// Typedefs emit no code and create no allocno, so the move is byte-neutral:
|
||||
// re-verified MATCH (304 ins) after the change, and the local DECLARATION ORDER
|
||||
// below is untouched (§76 — declaration order is the dominant regalloc lever here).
|
||||
//
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// THE BODY (SESSION-21 derivation, each lever re-verified this session)
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// func_8014D820 (304 ins, ov_SC01_077, h_seq family exemplar, 138 members).
|
||||
// The whole residual off the SESSION-18 close=9 seed was a sched1 PERMUTATION
|
||||
// inside ONE basic block (idx 79..101, the pos/desc set-up between the `slti 0x400`
|
||||
// guard and the `currentLocationId` test): identical multiset of insns, identical
|
||||
// register assignment, wrong positions. §66d-5 exactly — classed WIDTH [structural]
|
||||
// at every waypoint and not structural at all.
|
||||
//
|
||||
// The lever (reusable): that block is 12 statements over a small dependency poset
|
||||
// (load x0/y0/z0 -> add -> store pos.* / desc.*), so source ORDER is the only
|
||||
// control and the space of linear extensions is enumerable. A dependency-respecting
|
||||
// sweep found MATCH at
|
||||
// x0; pos.x; y0; pos.y; z0; tx; pos.z; tz; desc.x; desc.z; ty; desc.y
|
||||
// i.e. all three loads and both pos stores first, then the two adds, then the desc
|
||||
// stores, with the y accumulator (`ty`) computed LAST. No pin, no barrier, no
|
||||
// permuter run. Eleven hand attempts on this window (SESSION-18) were inert or
|
||||
// worse — hand-reading a 12-statement schedule permutation is the wrong tool;
|
||||
// brute-forcing the linear extensions is the right one.
|
||||
//
|
||||
// Load-bearing levers (each re-verified by removal this session):
|
||||
// * the `a0v` launder (`__asm__("" : "=r"(a0v) : "0"(a0))`) — drop => 9 mismatched
|
||||
// * the `a2` $7 pin — drop => 5
|
||||
// * the `a2x -> a2` launder (vs a plain copy) — drop => 1
|
||||
// * `new_var3` (the s16 stash of dx feeding ratan2) — drop => 2
|
||||
// * `new_var2` (the desc.y + 0x10 accumulator) — drop => 2
|
||||
// The `a2` pin's live range ENDS before the first `jal` (last use is
|
||||
// `desc.z = a2[2] - a1[2]`), so it is §42e-CORRECTION / §74-safe to propagate.
|
||||
//
|
||||
// SYMBOLS (§58 rule 1/2): all five data externs use the canonical splat spelling and
|
||||
// the engine_core.h type — `u8 D_801202A0[]`, `u8 D_801152A8[]`, `s16 D_801152AC`,
|
||||
// `u8 D_8018876C[]`, `s16 currentLocationId`. symcheck: SYMS-OK, 12 symbols agree.
|
||||
//
|
||||
// The one deliberate divergence (§27 step 2 / rule 3): func_80135A4C's canonical
|
||||
// extern is 4-arg but this call site sets only $a0-$a2, so the canonical decl is
|
||||
// kept verbatim (it is also the TU's own spelling at ov_SC01_077_after.c:1651/1709)
|
||||
// and the ARITY is cast at the call site.
|
||||
//
|
||||
// BANKING NOTE for the orchestrator (carried forward from SESSION-18, still true):
|
||||
// 1 `self_decl_hdr` axis remains — DEFINE_func_8014D790's body declares
|
||||
// func_8014D820, so that macro in engine_core.h needs the §85 return-axis widen
|
||||
// (already applied). With the `Ent` collision gone the TU's cc1 stage is clean.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern s16 currentLocationId;
|
||||
extern u8 D_801202A0[];
|
||||
extern u8 D_8018876C[];
|
||||
extern u8 D_801152A8[];
|
||||
extern s16 D_801152AC;
|
||||
extern s32 ratan2(s32 a0, s32 a1);
|
||||
extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3);
|
||||
extern s32 func_80012A60(s32 a0, s32 a1);
|
||||
extern void func_800139C8(s32 a0, void *a1, void *a2);
|
||||
extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3);
|
||||
extern s32 func_8014DCE0(s32 a0, s32 a1, s32 a2);
|
||||
extern s32 func_80133784(s32 a0, void *a1, s32 a2);
|
||||
s32 func_8014D820(s32 a0, u16 *a1, u16 *a2x)
|
||||
{
|
||||
/* BLOCK-SCOPE types — see the header note. `Ent` at file scope collides with
|
||||
src/shared/engine_types.h:434 and kills the TU's cc1 stage (exit 33). */
|
||||
typedef struct
|
||||
{
|
||||
u16 x;
|
||||
u16 y;
|
||||
u16 z;
|
||||
u16 w;
|
||||
} V4;
|
||||
typedef struct
|
||||
{
|
||||
u16 x;
|
||||
u16 y;
|
||||
u16 z;
|
||||
u16 w;
|
||||
s32 pad[4];
|
||||
s32 f18;
|
||||
s32 f1C;
|
||||
s32 f20;
|
||||
} Desc;
|
||||
typedef struct EntD820
|
||||
{
|
||||
u16 f0;
|
||||
u16 f2;
|
||||
u16 f4;
|
||||
u16 x;
|
||||
u16 f8;
|
||||
u16 y;
|
||||
u16 fC;
|
||||
u16 z;
|
||||
u8 p10[0x10];
|
||||
s32 f20;
|
||||
u8 p24[0x34];
|
||||
s32 f58;
|
||||
u16 f5C;
|
||||
u16 f5E;
|
||||
u8 p60[0x10C - 0x60];
|
||||
} Ent;
|
||||
int new_var2;
|
||||
s16 new_var3;
|
||||
register u16 *a2 __asm__("$7");
|
||||
V4 out[3];
|
||||
V4 pos;
|
||||
Desc desc;
|
||||
Ent *ent;
|
||||
Ent *p;
|
||||
s32 dx;
|
||||
s32 dz;
|
||||
s32 ex;
|
||||
s32 ez;
|
||||
s32 r1;
|
||||
s32 r2;
|
||||
s32 r3;
|
||||
s32 ang;
|
||||
s32 x0;
|
||||
s32 y0;
|
||||
s32 z0;
|
||||
s32 tx;
|
||||
s32 tz;
|
||||
s32 ty;
|
||||
s32 r;
|
||||
s32 t;
|
||||
s32 u;
|
||||
s32 a0v;
|
||||
__asm__ __volatile__("" : "=r"(a2) : "0"(a2x));
|
||||
t = a2[0];
|
||||
u = a1[0];
|
||||
dx = t - u;
|
||||
__asm__ __volatile__("" : "=r"(a0v) : "0"(a0));
|
||||
new_var3 = (s16) dx;
|
||||
t = a2[2];
|
||||
u = a1[2];
|
||||
dz = t - u;
|
||||
if (((s16) (dx | dz)) == 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
desc.x = dx;
|
||||
desc.y = a2[1] - a1[1];
|
||||
desc.z = a2[2] - a1[2];
|
||||
ent = *((Ent **) (a0v + 0x170));
|
||||
if (ent->f0 == 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
if ((ent->f5C & 0x1000) == 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
if (ent->f58 == 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
if (((s32 (*)(s32, s32, s32)) func_80135A4C)(ent->f20, ent->f58, (s32) a1) == 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
ex = ent->x - (*((u16 *) (a0v + 6)));
|
||||
ez = ent->z - (*((u16 *) (a0v + 0xE)));
|
||||
r1 = (s16) ratan2((s16) dz, new_var3);
|
||||
r2 = (s16) ratan2((s16) ez, (s16) ex);
|
||||
r3 = (s16) func_80012A60(r1, r2);
|
||||
if (0x400 <= r3)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
desc.f20 = 0;
|
||||
desc.f1C = 0;
|
||||
desc.f18 = 0;
|
||||
x0 = ent->x;
|
||||
pos.x = x0;
|
||||
y0 = ent->y;
|
||||
pos.y = y0;
|
||||
z0 = ent->z;
|
||||
tx = x0 + desc.x;
|
||||
pos.z = z0;
|
||||
tz = z0 + desc.z;
|
||||
desc.x = tx;
|
||||
desc.z = tz;
|
||||
ty = (y0 + desc.y) + 8;
|
||||
desc.y = ty;
|
||||
if (currentLocationId != 0x3008)
|
||||
{
|
||||
for (p = (Ent *) D_801202A0; p < (Ent *) (D_801202A0 + 0x6480); p++)
|
||||
{
|
||||
if (p == ent)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (p->f0 == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (p->f5C == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (p->f58 == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
func_80135888(p->f20, p->f58, (s32) (&pos), (s32) (&desc));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
ang = ratan2(((s16) x0) - ((s16) tx), ((s16) z0) - ((s16) tz)) & 0xFFF;
|
||||
func_800139C8(ang, D_8018876C, &out[0]);
|
||||
func_800139C8((s16) (ang - 0x155), D_8018876C, &out[1]);
|
||||
func_800139C8((s16) (ang + 0x155), D_8018876C, &out[2]);
|
||||
out[0].x += pos.x;
|
||||
out[0].y = pos.y;
|
||||
out[0].z += pos.z;
|
||||
out[1].x += pos.x;
|
||||
out[1].y = pos.y;
|
||||
out[1].z += pos.z;
|
||||
out[2].x += pos.x;
|
||||
out[2].y = pos.y;
|
||||
out[2].z += pos.z;
|
||||
for (p = (Ent *) D_801202A0; p < (Ent *) (D_801202A0 + 0x6480); p++)
|
||||
{
|
||||
if (p == ent)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (p->f0 == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (p->f5C == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (p->f58 == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (func_8014DCE0((s32) p, (s32) (&pos), (s32) (&out[0])) != 0)
|
||||
{
|
||||
__builtin_memcpy((void *) ((s32) (&desc)), (void *) ((s32) (&pos)), 8);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
r = func_80133784(0, &pos, (s32) (&desc));
|
||||
if ((r == 0) || ((r == 0x2000) && (((*(u16 *) D_801152A8) | ((u16) D_801152AC)) == 0)))
|
||||
{
|
||||
ent->x = desc.x - desc.f18;
|
||||
ent->y = desc.y;
|
||||
ent->z = desc.z - desc.f20;
|
||||
desc.x -= pos.x;
|
||||
desc.y -= pos.y;
|
||||
desc.z -= pos.z;
|
||||
*((u16 *) (a0v + 6)) = a1[0] + desc.x;
|
||||
new_var2 = desc.y;
|
||||
new_var2 = new_var2 + 0x10;
|
||||
*((u16 *) (a0v + 0xA)) = a1[1] + new_var2;
|
||||
*((u16 *) (a0v + 0xE)) = a1[2] + desc.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
*((u16 *) (a0v + 6)) = a1[0];
|
||||
*((u16 *) (a0v + 0xA)) = a1[1] + 0x10;
|
||||
*((u16 *) (a0v + 0xE)) = a1[2];
|
||||
}
|
||||
return 1;
|
||||
fail:
|
||||
*((s32 *) (a0v + 0x170)) = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
||||
@@ -155,6 +155,39 @@ def main():
|
||||
+ (" …" if len(set(callers)) > 3 else ""), file=sys.stderr)
|
||||
return 2
|
||||
|
||||
# ---- THE SCALAR-NARROWING WARNING (Phase 29 SESSION-21, byte-proven) ----
|
||||
# A POINTER type change in a decl is caller-neutral (passing a pointer is passing a pointer —
|
||||
# func_80179B74 conformed 1,600 sites s16*/short* -> u16* and stayed byte-identical fleet-wide).
|
||||
# A SCALAR WIDTH change is NOT: narrowing `s32 a0` -> `u16 param_1` changes argument promotion,
|
||||
# so callers emit different code. Measured on func_80175DA8 — with the decls reverted the gate
|
||||
# says PLUMBING (`conflicting types`), with the conform applied it says **DIFF**. The conform
|
||||
# did not fix the draft; it changed the callers.
|
||||
#
|
||||
# This is NOT a refusal: the draft's signature is still byte-truth for the CALLEE, and the
|
||||
# whole-binary gate is the arbiter. It is a warning that the usual "decls are free" intuition
|
||||
# does not hold here, so a DIFF after this conform means re-examine the CALLERS (the §17a-1
|
||||
# pair — keep the caller decl compatible and cast at the call site) rather than the body.
|
||||
NARROW = {"u8": 1, "s8": 1, "char": 1, "u16": 2, "s16": 2, "short": 2}
|
||||
old_shapes = {strip_names(re.search(rf"\(([^;]*)\)\s*;", f).group(1)) for f in forms
|
||||
if re.search(rf"\(([^;]*)\)\s*;", f)}
|
||||
new_shape = strip_names(params)
|
||||
narrowing = []
|
||||
for old in old_shapes:
|
||||
for o, n in zip([x.strip() for x in old.split(",")], [x.strip() for x in new_shape.split(",")]):
|
||||
if "*" in o or "*" in n:
|
||||
continue # pointer change: caller-neutral
|
||||
nb = NARROW.get(n.replace("unsigned ", "u").replace("signed ", "s"))
|
||||
if nb and o not in ("", "void") and o != n:
|
||||
narrowing.append(f"{o} -> {n}")
|
||||
if narrowing:
|
||||
print(f"\n⚠ SCALAR-NARROWING in the parameter list ({', '.join(sorted(set(narrowing)))}). "
|
||||
f"Unlike a pointer-type change this is NOT caller-neutral — argument promotion at "
|
||||
f"every call site changes, so callers emit different code (byte-proven on "
|
||||
f"func_80175DA8: PLUMBING before the conform, DIFF after). Applying is still valid — "
|
||||
f"the draft's signature is byte-truth for the CALLEE and the whole-binary gate "
|
||||
f"arbitrates — but if the gate then reports DIFF, examine the CALLERS (§17a-1: keep "
|
||||
f"their decl compatible and cast at the call site), not the body.")
|
||||
|
||||
# §85 precondition — only matters when the RETURN type changes.
|
||||
ret_changes = any(not re.match(rf"extern\s+{re.escape(ret)}\s", f) for f in forms)
|
||||
if ret_changes:
|
||||
|
||||
Reference in New Issue
Block a user