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.