From 908875682a25283ed7a732952e4e71cd7a057927 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 23 May 2026 02:21:53 +0900 Subject: [PATCH] selfhost/cmd/wcc/cgenutil: collapse nullableptrtag onto tinfo.params (#50 phase 2, A.6.3f-b) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- selfhost/cmd/w6c/main.combined.ww | 32 ++++++++++++++++++---------- selfhost/cmd/wcc/cgenutil.ww | 32 ++++++++++++++++++---------- selfhost/cmd/wwdump/main.combined.ww | 32 ++++++++++++++++++---------- 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 20b1fc08..dfa07613 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -12588,19 +12588,29 @@ export fn isnullabletype(t: *node) bool = { }; // nullableptrtag — 0-based index of the *T variant in a nullable -// union. The void variant takes the other slot (0 or 1). AST-keyed -// for now: tinfofornode doesn't populate TY_TAGGED.params yet -// (check.ww:1287-1337 sets size/align/nullable but not the variant -// chain), so the tinfo equivalent of cstage cgen.c:405 -// `nullable_ptr_tag` can't read params today. Graduates to a pure -// tinfo helper in A.6.3f (#50) alongside the variant-index work and -// the tparam-population glue. +// union. Mirror of cstage cgen.c:404-416 `nullable_ptr_tag`: linear +// scan ti.params, strip TY_NAMED on each variant, return idx of first +// TY_PTR. Phase 1 (26724fe) populated the chain in tinfofornode's +// TTAGGED arm so this walk could retire the AST-keyed predecessor. export fn nullableptrtag(t: *node) i32 = { if (t == nil) { return 0; }; - if (t.kind != nkind.N_TTAGGED) { return 0; }; - let a: *node = t.list; - if (a != nil) { if (a.kind == nkind.N_TPTR) { return 0; }; }; - return 1; + let ti: *tinfo = t.type_: *tinfo; + if (ti == nil) { return 0; }; + if (ti.kind != tykind.TY_TAGGED) { return 0; }; + let p: *tparam = ti.params; + let i: i32 = 0; + for (p != nil) { + let vt: *tinfo = p.type_; + if (vt != nil) { + if (vt.kind == tykind.TY_NAMED) { vt = vt.under; }; + if (vt != nil) { + if (vt.kind == tykind.TY_PTR) { return i; }; + }; + }; + p = p.tnext; + i += 1; + }; + return 0; }; // voidvariantindex — find the 0-based index of the `void` variant in a diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index c0cf2187..49e4b60b 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -2204,19 +2204,29 @@ export fn isnullabletype(t: *node) bool = { }; // nullableptrtag — 0-based index of the *T variant in a nullable -// union. The void variant takes the other slot (0 or 1). AST-keyed -// for now: tinfofornode doesn't populate TY_TAGGED.params yet -// (check.ww:1287-1337 sets size/align/nullable but not the variant -// chain), so the tinfo equivalent of cstage cgen.c:405 -// `nullable_ptr_tag` can't read params today. Graduates to a pure -// tinfo helper in A.6.3f (#50) alongside the variant-index work and -// the tparam-population glue. +// union. Mirror of cstage cgen.c:404-416 `nullable_ptr_tag`: linear +// scan ti.params, strip TY_NAMED on each variant, return idx of first +// TY_PTR. Phase 1 (26724fe) populated the chain in tinfofornode's +// TTAGGED arm so this walk could retire the AST-keyed predecessor. export fn nullableptrtag(t: *node) i32 = { if (t == nil) { return 0; }; - if (t.kind != nkind.N_TTAGGED) { return 0; }; - let a: *node = t.list; - if (a != nil) { if (a.kind == nkind.N_TPTR) { return 0; }; }; - return 1; + let ti: *tinfo = t.type_: *tinfo; + if (ti == nil) { return 0; }; + if (ti.kind != tykind.TY_TAGGED) { return 0; }; + let p: *tparam = ti.params; + let i: i32 = 0; + for (p != nil) { + let vt: *tinfo = p.type_; + if (vt != nil) { + if (vt.kind == tykind.TY_NAMED) { vt = vt.under; }; + if (vt != nil) { + if (vt.kind == tykind.TY_PTR) { return i; }; + }; + }; + p = p.tnext; + i += 1; + }; + return 0; }; // voidvariantindex — find the 0-based index of the `void` variant in a diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 5ac78ace..8ca8484f 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -12588,19 +12588,29 @@ export fn isnullabletype(t: *node) bool = { }; // nullableptrtag — 0-based index of the *T variant in a nullable -// union. The void variant takes the other slot (0 or 1). AST-keyed -// for now: tinfofornode doesn't populate TY_TAGGED.params yet -// (check.ww:1287-1337 sets size/align/nullable but not the variant -// chain), so the tinfo equivalent of cstage cgen.c:405 -// `nullable_ptr_tag` can't read params today. Graduates to a pure -// tinfo helper in A.6.3f (#50) alongside the variant-index work and -// the tparam-population glue. +// union. Mirror of cstage cgen.c:404-416 `nullable_ptr_tag`: linear +// scan ti.params, strip TY_NAMED on each variant, return idx of first +// TY_PTR. Phase 1 (26724fe) populated the chain in tinfofornode's +// TTAGGED arm so this walk could retire the AST-keyed predecessor. export fn nullableptrtag(t: *node) i32 = { if (t == nil) { return 0; }; - if (t.kind != nkind.N_TTAGGED) { return 0; }; - let a: *node = t.list; - if (a != nil) { if (a.kind == nkind.N_TPTR) { return 0; }; }; - return 1; + let ti: *tinfo = t.type_: *tinfo; + if (ti == nil) { return 0; }; + if (ti.kind != tykind.TY_TAGGED) { return 0; }; + let p: *tparam = ti.params; + let i: i32 = 0; + for (p != nil) { + let vt: *tinfo = p.type_; + if (vt != nil) { + if (vt.kind == tykind.TY_NAMED) { vt = vt.under; }; + if (vt != nil) { + if (vt.kind == tykind.TY_PTR) { return i; }; + }; + }; + p = p.tnext; + i += 1; + }; + return 0; }; // voidvariantindex — find the 0-based index of the `void` variant in a