Phase 2 of A.6.3f, closing A.6.3 entirely (a/b/c/d/e/f-a/f-b all
landed). With #50 phase 1 (26724fe) populating TY_TAGGED.params,
nullableptrtag retires its AST-keyed predecessor: walk ti.params,
strip TY_NAMED via .under on each variant, return idx of first
TY_PTR.
Mirrors cstage cmd/w6c/cgen.c:404-416 line-for-line. Source-order
semantics preserved by phase 1's append-tail head/tail
construction (head = first n.list variant). For a `(*T | void)`,
*T-first → returns 0; void-first → walks past void (vt.kind==
TY_VOID, no match), *T at idx 1 → returns 1.
Two paths the new tinfo-keyed body handles that the AST walk
missed (cstage parity, dead-in-bootstrap today, parallel to #49
TY_TUPLE / #51b typed-float corrections):
- Aliased *T variant: `type ip = *int; let x: (ip | void);` —
cstage cgen.c:415 strips TY_NAMED via .under; new wwstage
body mirrors. Bootstrap has zero aliased-ptr-variant
callsites today (`grep "^type [a-z]+ = \*"` yields only an
out-of-union test/uses.ww case).
- void-first ordering: cstage walks past TY_VOID and finds *T
at idx 1. Same in wwstage. No test/wcc fixture exercises
void-first today.
Outer-type TY_NAMED strip (cstage cgen.c:409 `if t->kind==TY_NAMED
t = t->under`) intentionally skipped: tinfofornode for N_TNAME
already resolves the alias and returns the underlying tinfo
unwrapped — `typenamed` is declared at lib/ww/typ.ww:238 but has
zero producers in selfhost today. The strip would be a no-op
under current invariants. Tracked as part of #13 (TTAGGED
normalization parity) for when wwstage starts producing
TY_NAMED.
Net +30 LOC across cgenutil.ww + two .combined.ww bundler regens
(new function body is ~14 lines vs ~4; new WHY comment shorter
than the pre-graduation note's 7).
Byte-identity (994/995) is the gate; full make test green at
133/133 confirms.