syntax+w6c: multi-type match patterns in wwstage (align up to cstage)
`case T1 | T2 =>` (binding-less; cstage parse.c:635-650 chains extra types through cs->list, bindings stay single-type by design). Three wwstage layers, no AST change (node.list already exists): - parser (expr.ww): pipe loop after the non-let arm's first parsetype; - checker (check.ww): resolvewalk N_MCASE now walks n.list so each alt gets its type_ stamp (exhaustiveness/casecovers were already alt-aware, built ahead of the parser); - cgen (cgenexpr.ww): the single-pattern want-computation moves VERBATIM into matcharmwant; a cs.list arm emits CMPQ/JE per alt funneling into one match_body label, mirroring cgen.c:11118-11141 incl. label mint order and the tag<0 clamp. Nullable arms keep ignoring alts (both stages). Graduates the held e2e row (pin 1732/1158/3464); byte-identical on the repro and the single-pattern control.
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
package wwfixture;
|
package wwfixture;
|
||||||
|
|
||||||
def protocolversion: i32 = 1;
|
def protocolversion: i32 = 1;
|
||||||
def corpuscount: i32 = 1731;
|
def corpuscount: i32 = 1732;
|
||||||
def errorcount: i32 = 343;
|
def errorcount: i32 = 343;
|
||||||
def compilecount: i32 = 22;
|
def compilecount: i32 = 22;
|
||||||
def runcount: i32 = 209;
|
def runcount: i32 = 209;
|
||||||
def runexitcount: i32 = 1157;
|
def runexitcount: i32 = 1158;
|
||||||
def nativecount: i32 = 3462;
|
def nativecount: i32 = 3464;
|
||||||
def corpushash: str = "6786916caba20c07c47eaaa0507dd2707fd162b708904b6e72187afb02233bf9";
|
def corpushash: str = "df3862c647aae744b549089ab4a180b36371fe898f5996c13099f52da203265e";
|
||||||
|
|
||||||
type directive = enum i32 {
|
type directive = enum i32 {
|
||||||
ERROR = 0,
|
ERROR = 0,
|
||||||
|
|||||||
@@ -205,6 +205,17 @@ fn parseprimary(p: *parser) *node = {
|
|||||||
mc.lhs = parsetype(p);
|
mc.lhs = parsetype(p);
|
||||||
} else { if (p.curkind != tkind.TK_FATARROW) {
|
} else { if (p.curkind != tkind.TK_FATARROW) {
|
||||||
mc.lhs = parsetype(p);
|
mc.lhs = parsetype(p);
|
||||||
|
// `case T1 | T2 | ... =>`: extra types chain
|
||||||
|
// through mc.list. Binding arms stay single-type
|
||||||
|
// (a binding over a multi-pattern would need the
|
||||||
|
// union type) — mirrors cmd/wcc/parse.c:643.
|
||||||
|
let alttail: *node = nil;
|
||||||
|
for (accepttok(p, tkind.TK_PIPE)) {
|
||||||
|
let more: *node = parsetype(p);
|
||||||
|
if (mc.list == nil) { mc.list = more; }
|
||||||
|
else { alttail.next = more; };
|
||||||
|
alttail = more;
|
||||||
|
};
|
||||||
};};
|
};};
|
||||||
expecttok(p, tkind.TK_FATARROW, "expected '=>' in match arm");
|
expecttok(p, tkind.TK_FATARROW, "expected '=>' in match arm");
|
||||||
mc.body = parsestmt(p);
|
mc.body = parsestmt(p);
|
||||||
|
|||||||
@@ -3090,6 +3090,41 @@ fn cgslice(c: *cgen, n: *syntax.node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// matcharmwant — variant tag a non-nullable match pattern selects on
|
||||||
|
// the scrutinee's tagged union; 0 when unresolvable (cstage's tag<0
|
||||||
|
// clamp, cmd/w6c/cgen.c:11124). Serves the single-pattern arm and each
|
||||||
|
// `case T1 | T2` alt identically.
|
||||||
|
fn matcharmwant(c: *cgen, scrutt: *syntax.node, pat: *syntax.node) i32 = {
|
||||||
|
let want: i32 = 0;
|
||||||
|
if (scrutt != nil) {
|
||||||
|
// #67: gate on the stamped tinfo, not the node kind
|
||||||
|
// — matchscrutt now returns the scrutinee node itself
|
||||||
|
// for an N_DOT field (its .type_ is the tagged tinfo)
|
||||||
|
// rather than the resolved N_TTAGGED node.
|
||||||
|
if (istaggedtype(c, scrutt)) {
|
||||||
|
let r: i32 = -1;
|
||||||
|
let pattype: *syntax.tinfo = pat.type_: *syntax.tinfo;
|
||||||
|
if (pattype != nil) {
|
||||||
|
// #179: kind-agnostic dispatch on the resolved
|
||||||
|
// tinfo. Cstage cg_tag_for_variant works on
|
||||||
|
// Type, so N_TPTR / N_TFN / N_TPTR(N_TFN) case-
|
||||||
|
// patterns all reach typeeq; the prior pat.kind
|
||||||
|
// gate dropped them to r=-1 → tag 0 collapse.
|
||||||
|
// Slice arm still goes through flatslicevariantidx
|
||||||
|
// (task #19 untyped-elem fallback when typeeq
|
||||||
|
// can't match a (scalar | []T) shape).
|
||||||
|
if (syntax.typeisslice(pattype)) {
|
||||||
|
r = flatslicevariantidx(c, scrutt, pat.lhs);
|
||||||
|
} else {
|
||||||
|
r = flatvariantidxt(scrutt.type_: *syntax.tinfo, pattype, false);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (r >= 0) { want = r; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return want;
|
||||||
|
};
|
||||||
|
|
||||||
fn cgmatch(c: *cgen, n: *syntax.node) void = {
|
fn cgmatch(c: *cgen, n: *syntax.node) void = {
|
||||||
// match (e) { case let v: T => stmt; ... }
|
// match (e) { case let v: T => stmt; ... }
|
||||||
//
|
//
|
||||||
@@ -3326,42 +3361,44 @@ fn cgmatch(c: *cgen, n: *syntax.node) void = {
|
|||||||
emitline(nxt);
|
emitline(nxt);
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
} else {
|
} else {
|
||||||
let want: i32 = 0;
|
let want: i32 = matcharmwant(c, scrutt, pat);
|
||||||
if (scrutt != nil) {
|
|
||||||
// #67: gate on the stamped tinfo, not the node kind
|
|
||||||
// — matchscrutt now returns the scrutinee node itself
|
|
||||||
// for an N_DOT field (its .type_ is the tagged tinfo)
|
|
||||||
// rather than the resolved N_TTAGGED node.
|
|
||||||
if (istaggedtype(c, scrutt)) {
|
|
||||||
let r: i32 = -1;
|
|
||||||
let pattype: *syntax.tinfo = pat.type_: *syntax.tinfo;
|
|
||||||
if (pattype != nil) {
|
|
||||||
// #179: kind-agnostic dispatch on the resolved
|
|
||||||
// tinfo. Cstage cg_tag_for_variant works on
|
|
||||||
// Type, so N_TPTR / N_TFN / N_TPTR(N_TFN) case-
|
|
||||||
// patterns all reach typeeq; the prior pat.kind
|
|
||||||
// gate dropped them to r=-1 → tag 0 collapse.
|
|
||||||
// Slice arm still goes through flatslicevariantidx
|
|
||||||
// (task #19 untyped-elem fallback when typeeq
|
|
||||||
// can't match a (scalar | []T) shape).
|
|
||||||
if (syntax.typeisslice(pattype)) {
|
|
||||||
r = flatslicevariantidx(c, scrutt, pat.lhs);
|
|
||||||
} else {
|
|
||||||
r = flatvariantidxt(scrutt.type_: *syntax.tinfo, pattype, false);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
if (r >= 0) { want = r; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(scrutoff: i64);
|
emitoff(scrutoff: i64);
|
||||||
emitline("(BP), AX\n");
|
emitline("(BP), AX\n");
|
||||||
emitline("\tCMPQ\t$");
|
if (cs.list != nil) {
|
||||||
emitint(want: i64);
|
// Multi-pattern `case T1 | T2 =>`: any
|
||||||
emitline(", AX\n");
|
// matching alt funnels into one body
|
||||||
emitline("\tJNE\t");
|
// label. Mirrors cmd/w6c/cgen.c:11118.
|
||||||
emitline(nxt);
|
let bodyl: str = mklabel(c, "match_body");
|
||||||
emitline("\n");
|
emitline("\tCMPQ\t$");
|
||||||
|
emitint(want: i64);
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tJE\t");
|
||||||
|
emitline(bodyl);
|
||||||
|
emitline("\n");
|
||||||
|
let alt: *syntax.node = cs.list;
|
||||||
|
for (alt != nil) {
|
||||||
|
let awant: i32 = matcharmwant(c, scrutt, alt);
|
||||||
|
emitline("\tCMPQ\t$");
|
||||||
|
emitint(awant: i64);
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tJE\t");
|
||||||
|
emitline(bodyl);
|
||||||
|
emitline("\n");
|
||||||
|
alt = alt.next;
|
||||||
|
};
|
||||||
|
emitline("\tJMP\t");
|
||||||
|
emitline(nxt);
|
||||||
|
emitline("\n");
|
||||||
|
emitlabel(bodyl);
|
||||||
|
} else {
|
||||||
|
emitline("\tCMPQ\t$");
|
||||||
|
emitint(want: i64);
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tJNE\t");
|
||||||
|
emitline(nxt);
|
||||||
|
emitline("\n");
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// Bind `let v: T` from the slot, if requested.
|
// Bind `let v: T` from the slot, if requested.
|
||||||
|
|||||||
@@ -671,6 +671,14 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
|||||||
// Mirrors cmd/wcc/check.c's newscope/saved-restore around cstmt.
|
// Mirrors cmd/wcc/check.c's newscope/saved-restore around cstmt.
|
||||||
if (k == syntax.nkind.N_MCASE) {
|
if (k == syntax.nkind.N_MCASE) {
|
||||||
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
||||||
|
// `case T1 | T2` alts ride n.list; each needs its type_
|
||||||
|
// stamp or cgmatch's tag lookup collapses to 0. Mirrors
|
||||||
|
// cmd/wcc/check.c:2140 (per-alt resolve).
|
||||||
|
let alt: *syntax.node = n.list;
|
||||||
|
for (alt != nil) {
|
||||||
|
resolvewalk(c, alt);
|
||||||
|
alt = alt.next;
|
||||||
|
};
|
||||||
let outer: *syntax.scope = c.cur;
|
let outer: *syntax.scope = c.cur;
|
||||||
c.cur = syntax.newscope(outer);
|
c.cur = syntax.newscope(outer);
|
||||||
let nm: str = n.str;
|
let nm: str = n.str;
|
||||||
|
|||||||
27
test/wcc/data/r700_match_multi_pattern/case.ww
Normal file
27
test/wcc/data/r700_match_multi_pattern/case.ww
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//ww:run-exit 18
|
||||||
|
// Migrated from 700_e2e row 72.
|
||||||
|
package main;
|
||||||
|
fn pick(n: i64) (i64 | i32 | u32) = {
|
||||||
|
if (n < 0) { return 1: i32; };
|
||||||
|
if (n == 0) { return 2: u32; };
|
||||||
|
return n;
|
||||||
|
};
|
||||||
|
fn main() i32 = {
|
||||||
|
let r1: (i64 | i32 | u32) = pick(0);
|
||||||
|
let r2: (i64 | i32 | u32) = pick(-1);
|
||||||
|
let r3: (i64 | i32 | u32) = pick(7);
|
||||||
|
let acc: i32 = 0;
|
||||||
|
match (r1) {
|
||||||
|
case let v: i64 => acc += 100;
|
||||||
|
case i32 | u32 => acc += 1;
|
||||||
|
};
|
||||||
|
match (r2) {
|
||||||
|
case let v: i64 => acc += 100;
|
||||||
|
case i32 | u32 => acc += 10;
|
||||||
|
};
|
||||||
|
match (r3) {
|
||||||
|
case let v: i64 => acc += v: i32;
|
||||||
|
case i32 | u32 => acc += 100;
|
||||||
|
};
|
||||||
|
return acc;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user