diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index e80cb000..7207319a 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -7785,6 +7785,40 @@ fn astsize(c: *checker, t: *node) i64 = { return 0i64; }; +// matchyieldtype — port of cstage cmd/wcc/check.c:110-135. Walks a +// match arm body for the first `yield expr;` and returns its operand +// type. Returns nil if no yield is reachable from `body`. Doesn't +// descend into a nested N_MATCH — each match opens its own yield +// scope. exprtype is idempotent on already-stamped nodes (tinfocache +// path at L467) so re-entering it on the yield operand here is safe. +fn matchyieldtype(c: *checker, body: *node) *node = { + if (body == nil) { return nil; }; + let k: nkind = body.kind; + if (k == nkind.N_YIELD) { + if (body.lhs == nil) { return nil; }; + return exprtype(c, body.lhs, nil); + }; + if (k == nkind.N_MATCH) { return nil; }; + if (k == nkind.N_BLOCK) { + let s: *node = body.list; + for (s != nil) { + let t: *node = matchyieldtype(c, s); + if (t != nil) { return t; }; + s = s.next; + }; + return nil; + }; + if (k == nkind.N_IF) { + let t: *node = matchyieldtype(c, body.body); + if (t != nil) { return t; }; + return matchyieldtype(c, body.els); + }; + if (k == nkind.N_FOR || k == nkind.N_FORRANGE) { + return matchyieldtype(c, body.body); + }; + return nil; +}; + // astoffset — byte offset of `dot.str` inside the struct type of // `dot.lhs`. Mirrors cstage cmd/wcc/check.c:932-961: peel one N_TPTR // (for `p.field` where p is *Struct), require N_TSTRUCT, walk fields @@ -8897,6 +8931,27 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = { if (t != nil) { e.type_ = tinfofornode(c, t): *void; }; return t; }; + if (k == nkind.N_MATCH) { + // A.6.2.0g — port of cstage cmd/wcc/check.c:1316-1330 match-as- + // expression stamp. The match's type is the first arm's yield + // operand type; void if no arm yields. Wwstage skips cstage's + // arm-yield-unification check (L1322-1327) — that's a checker + // concern, this arm only stamps. Closes the consumer half of + // the match-as-expression contract that A.6.2.0f opened on the + // producer side (N_YIELD). + let yt: *node = nil; + let cs: *node = e.list; + for (cs != nil) { + let t: *node = matchyieldtype(c, cs.body); + if (t != nil) { yt = t; break; }; + cs = cs.next; + }; + if (yt == nil) { + yt = mktname(c, "void"); + }; + e.type_ = tinfofornode(c, yt): *void; + return yt; + }; if (k == nkind.N_YIELD) { // A.6.2.0f — pass-through stamp; cstage check.c:1708 does NOT // stamp N_YIELD (statement-shaped). Wwstage's A.6.2 invariant diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 0b226e51..00221e72 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -821,6 +821,40 @@ fn astsize(c: *checker, t: *node) i64 = { return 0i64; }; +// matchyieldtype — port of cstage cmd/wcc/check.c:110-135. Walks a +// match arm body for the first `yield expr;` and returns its operand +// type. Returns nil if no yield is reachable from `body`. Doesn't +// descend into a nested N_MATCH — each match opens its own yield +// scope. exprtype is idempotent on already-stamped nodes (tinfocache +// path at L467) so re-entering it on the yield operand here is safe. +fn matchyieldtype(c: *checker, body: *node) *node = { + if (body == nil) { return nil; }; + let k: nkind = body.kind; + if (k == nkind.N_YIELD) { + if (body.lhs == nil) { return nil; }; + return exprtype(c, body.lhs, nil); + }; + if (k == nkind.N_MATCH) { return nil; }; + if (k == nkind.N_BLOCK) { + let s: *node = body.list; + for (s != nil) { + let t: *node = matchyieldtype(c, s); + if (t != nil) { return t; }; + s = s.next; + }; + return nil; + }; + if (k == nkind.N_IF) { + let t: *node = matchyieldtype(c, body.body); + if (t != nil) { return t; }; + return matchyieldtype(c, body.els); + }; + if (k == nkind.N_FOR || k == nkind.N_FORRANGE) { + return matchyieldtype(c, body.body); + }; + return nil; +}; + // astoffset — byte offset of `dot.str` inside the struct type of // `dot.lhs`. Mirrors cstage cmd/wcc/check.c:932-961: peel one N_TPTR // (for `p.field` where p is *Struct), require N_TSTRUCT, walk fields @@ -1933,6 +1967,27 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = { if (t != nil) { e.type_ = tinfofornode(c, t): *void; }; return t; }; + if (k == nkind.N_MATCH) { + // A.6.2.0g — port of cstage cmd/wcc/check.c:1316-1330 match-as- + // expression stamp. The match's type is the first arm's yield + // operand type; void if no arm yields. Wwstage skips cstage's + // arm-yield-unification check (L1322-1327) — that's a checker + // concern, this arm only stamps. Closes the consumer half of + // the match-as-expression contract that A.6.2.0f opened on the + // producer side (N_YIELD). + let yt: *node = nil; + let cs: *node = e.list; + for (cs != nil) { + let t: *node = matchyieldtype(c, cs.body); + if (t != nil) { yt = t; break; }; + cs = cs.next; + }; + if (yt == nil) { + yt = mktname(c, "void"); + }; + e.type_ = tinfofornode(c, yt): *void; + return yt; + }; if (k == nkind.N_YIELD) { // A.6.2.0f — pass-through stamp; cstage check.c:1708 does NOT // stamp N_YIELD (statement-shaped). Wwstage's A.6.2 invariant diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index b12d889b..6eb2071d 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -7785,6 +7785,40 @@ fn astsize(c: *checker, t: *node) i64 = { return 0i64; }; +// matchyieldtype — port of cstage cmd/wcc/check.c:110-135. Walks a +// match arm body for the first `yield expr;` and returns its operand +// type. Returns nil if no yield is reachable from `body`. Doesn't +// descend into a nested N_MATCH — each match opens its own yield +// scope. exprtype is idempotent on already-stamped nodes (tinfocache +// path at L467) so re-entering it on the yield operand here is safe. +fn matchyieldtype(c: *checker, body: *node) *node = { + if (body == nil) { return nil; }; + let k: nkind = body.kind; + if (k == nkind.N_YIELD) { + if (body.lhs == nil) { return nil; }; + return exprtype(c, body.lhs, nil); + }; + if (k == nkind.N_MATCH) { return nil; }; + if (k == nkind.N_BLOCK) { + let s: *node = body.list; + for (s != nil) { + let t: *node = matchyieldtype(c, s); + if (t != nil) { return t; }; + s = s.next; + }; + return nil; + }; + if (k == nkind.N_IF) { + let t: *node = matchyieldtype(c, body.body); + if (t != nil) { return t; }; + return matchyieldtype(c, body.els); + }; + if (k == nkind.N_FOR || k == nkind.N_FORRANGE) { + return matchyieldtype(c, body.body); + }; + return nil; +}; + // astoffset — byte offset of `dot.str` inside the struct type of // `dot.lhs`. Mirrors cstage cmd/wcc/check.c:932-961: peel one N_TPTR // (for `p.field` where p is *Struct), require N_TSTRUCT, walk fields @@ -8897,6 +8931,27 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = { if (t != nil) { e.type_ = tinfofornode(c, t): *void; }; return t; }; + if (k == nkind.N_MATCH) { + // A.6.2.0g — port of cstage cmd/wcc/check.c:1316-1330 match-as- + // expression stamp. The match's type is the first arm's yield + // operand type; void if no arm yields. Wwstage skips cstage's + // arm-yield-unification check (L1322-1327) — that's a checker + // concern, this arm only stamps. Closes the consumer half of + // the match-as-expression contract that A.6.2.0f opened on the + // producer side (N_YIELD). + let yt: *node = nil; + let cs: *node = e.list; + for (cs != nil) { + let t: *node = matchyieldtype(c, cs.body); + if (t != nil) { yt = t; break; }; + cs = cs.next; + }; + if (yt == nil) { + yt = mktname(c, "void"); + }; + e.type_ = tinfofornode(c, yt): *void; + return yt; + }; if (k == nkind.N_YIELD) { // A.6.2.0f — pass-through stamp; cstage check.c:1708 does NOT // stamp N_YIELD (statement-shaped). Wwstage's A.6.2 invariant