Closes STATUS latent #1: @tagscr shared 24B reservation across the four tagged-scratch sites (cgreturn, pushargsrev, cgindex tagged-elem, pointer-rooted struct-field tagged write). Any fn that needed >24B (e.g. slice-in-tagged-field 32B) silently overflowed into the neighbor frame slot. Surfaced concretely as getopttest's errortable wwstage exit 16 after #37 fixed the upstream gaps. c.tagscrsz: i32 on the cgen struct is the single source of truth. tagscrbump(c, need) in scanlocals raises the max across all 4 reservation sites and returns the frame delta. All emit sites (cgreturn / pushargsrev / cgindex / cgwidentaggedstore pointer- rooted) read c.tagscrsz instead of hardcoded 24. Mirrors the existing cgwidentaggedstore precedent; @tagbase keeps its 8B scanseenmark dedup (always 8B, correct). Unmasked latent bug (now fixed): scanlocals's pointer-rooted struct- field tagged-write detection uses localfindnode(c, base.str) to resolve the *struct base. For `fn fill(h: *holder)`, h's scan-time stub from scanseenmark had tnode=nil, so the @tagscr reservation never fired. Pre-#38 the hardcoded 24B masked this; #38's correctly- sized slot exposed it. cgfn's param scan loop now sets c.locals.tnode = scanp.lhs after scanseenmark so localfindnode resolves param types at scan time. Test 714 (tagged_return_scratch): 4 rows × 2 stages = 8 fixtures. Direct adjacency repro; match-arm field-by-field read; **mixed- sizes-one-fn** (16B pushargsrev widen + 32B cgreturn widen in the same body — pins the lockstep invariant that a sibling site can't undersize the shared slot); call-site struct-payload widen. Row 3 specifically would regress if a future refactor ever forgets to route an emit site through c.tagscrsz. 982 getopt_run green through both stages (was the original surface); 995 self_rebuild byte-id holds.
172 KiB
172 KiB