From 1131e7b68d2edad44671f66ae5385ec76ba30cc0 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Thu, 21 May 2026 21:01:57 +0900 Subject: [PATCH] selfhost/cmd/wcc: stamp e.type_ for N_TUPLE (A.6.2.0b) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port head-only of cstage cmd/wcc/check.c:1437-1451 to exprtype's new N_TUPLE arm. The arm walks e.list, types each element via recursive exprtype, and assembles an N_TTUPLE whose .list chains N_TPARAM wrappers (one per element) so shared element-type ASTs (sym.decl.lhs, struct field's .lhs, another tuple's element) keep their own .next untouched. Foundation: A.6.2.0b-pre (805c841) introduced N_TPARAM as the chain wrapper for N_TTUPLE.list, mirroring cstage's Tparam at the AST layer (cstage keeps it at the Type layer; wwstage has no separate type layer). This commit is the first checker-side synthesizer to use it. One documented divergence: empty list returns nil under the lenient policy (cstage would synthesize empty TY_TUPLE; ww grammar requires >= 2 elements per parse/expr.ww:117, so empty is unreachable either way). Same shape as scruttype L656 / A.6.1.5b N_DOT lenient-on-miss precedent. α scope per Drew (no hint plumbing, no field-level walks, no unify check). Hint param stays unused. A.6.2 step 2 of 8 (γ order). N_ALLOC next (most likely a parser-vestigial kind — A.6.2.0c worker audits first). `make test-unit` green; full `make test` deferred to the batched gate after the A.6.2.1 assertion commit (option B per user). --- selfhost/cmd/w6c/main.combined.ww | 33 ++++++++++++++++++++++++++++ selfhost/cmd/wcc/check.ww | 33 ++++++++++++++++++++++++++++ selfhost/cmd/wwdump/main.combined.ww | 33 ++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 11029e1e..68d45b27 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -8841,6 +8841,39 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = { }; return nil; }; + if (k == nkind.N_TUPLE) { + // A.6.2.0b — head-only stamp of the tuple expression's + // overall type. Mirrors cstage cmd/wcc/check.c:1437-1451 + // N_TUPLE: walk e.list, type each element via exprtype, and + // assemble an N_TTUPLE whose .list chains N_TPARAM wrappers + // (one per element) so shared element-type ASTs (sym.decl.lhs, + // another tuple's element, struct field's .lhs) keep their + // own .next untouched — see lib/ww/ast.ww:101 and the + // A.6.2.0b-pre parser precedent at lib/ww/parse/parse.ww:302. + // Per-element exprtype recursion is tinfocache-idempotent + // (resolvewalk L460-489 already dispatches into N_TUPLE + // children). Lenient on empty list: grammar requires >= 2 + // elements (lib/ww/parse/expr.ww:117 single-elem returns the + // expression), so empty is unreachable and yields nil here + // (matches scruttype L656 / A.6.1.5b N_DOT lenient-on-miss). + if (e.list == nil) { return nil; }; + let head: *node = nil; + let tail: *node = nil; + let it: *node = e.list; + for (it != nil) { + let elemt: *node = exprtype(c, it, nil); + let w: *node = newnode(nkind.N_TPARAM, "", 0, 0); + w.lhs = elemt; + if (head == nil) { head = w; } + else { tail.next = w; }; + tail = w; + it = it.next; + }; + let tt: *node = newnode(nkind.N_TTUPLE, "", 0, 0); + tt.list = head; + e.type_ = tinfofornode(c, tt): *void; + return tt; + }; if (k == nkind.N_TRYPROP) { // success unwrap: the success-variant type of operand's // tagged union. diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 714f4f20..81a305b0 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -1877,6 +1877,39 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = { }; return nil; }; + if (k == nkind.N_TUPLE) { + // A.6.2.0b — head-only stamp of the tuple expression's + // overall type. Mirrors cstage cmd/wcc/check.c:1437-1451 + // N_TUPLE: walk e.list, type each element via exprtype, and + // assemble an N_TTUPLE whose .list chains N_TPARAM wrappers + // (one per element) so shared element-type ASTs (sym.decl.lhs, + // another tuple's element, struct field's .lhs) keep their + // own .next untouched — see lib/ww/ast.ww:101 and the + // A.6.2.0b-pre parser precedent at lib/ww/parse/parse.ww:302. + // Per-element exprtype recursion is tinfocache-idempotent + // (resolvewalk L460-489 already dispatches into N_TUPLE + // children). Lenient on empty list: grammar requires >= 2 + // elements (lib/ww/parse/expr.ww:117 single-elem returns the + // expression), so empty is unreachable and yields nil here + // (matches scruttype L656 / A.6.1.5b N_DOT lenient-on-miss). + if (e.list == nil) { return nil; }; + let head: *node = nil; + let tail: *node = nil; + let it: *node = e.list; + for (it != nil) { + let elemt: *node = exprtype(c, it, nil); + let w: *node = newnode(nkind.N_TPARAM, "", 0, 0); + w.lhs = elemt; + if (head == nil) { head = w; } + else { tail.next = w; }; + tail = w; + it = it.next; + }; + let tt: *node = newnode(nkind.N_TTUPLE, "", 0, 0); + tt.list = head; + e.type_ = tinfofornode(c, tt): *void; + return tt; + }; if (k == nkind.N_TRYPROP) { // success unwrap: the success-variant type of operand's // tagged union. diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 3ff4de33..1871f1ab 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -8841,6 +8841,39 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = { }; return nil; }; + if (k == nkind.N_TUPLE) { + // A.6.2.0b — head-only stamp of the tuple expression's + // overall type. Mirrors cstage cmd/wcc/check.c:1437-1451 + // N_TUPLE: walk e.list, type each element via exprtype, and + // assemble an N_TTUPLE whose .list chains N_TPARAM wrappers + // (one per element) so shared element-type ASTs (sym.decl.lhs, + // another tuple's element, struct field's .lhs) keep their + // own .next untouched — see lib/ww/ast.ww:101 and the + // A.6.2.0b-pre parser precedent at lib/ww/parse/parse.ww:302. + // Per-element exprtype recursion is tinfocache-idempotent + // (resolvewalk L460-489 already dispatches into N_TUPLE + // children). Lenient on empty list: grammar requires >= 2 + // elements (lib/ww/parse/expr.ww:117 single-elem returns the + // expression), so empty is unreachable and yields nil here + // (matches scruttype L656 / A.6.1.5b N_DOT lenient-on-miss). + if (e.list == nil) { return nil; }; + let head: *node = nil; + let tail: *node = nil; + let it: *node = e.list; + for (it != nil) { + let elemt: *node = exprtype(c, it, nil); + let w: *node = newnode(nkind.N_TPARAM, "", 0, 0); + w.lhs = elemt; + if (head == nil) { head = w; } + else { tail.next = w; }; + tail = w; + it = it.next; + }; + let tt: *node = newnode(nkind.N_TTUPLE, "", 0, 0); + tt.list = head; + e.type_ = tinfofornode(c, tt): *void; + return tt; + }; if (k == nkind.N_TRYPROP) { // success unwrap: the success-variant type of operand's // tagged union.