The wwstage checker walked a match's raw AST variant list and never expanded a ...inner spread variant, so it rejected fmt's match over field = (...formattable | *mods) ('not a variant of scrutinee'). cstage's resolve_type flattens the spread at type-build. Mirror that in the AST exhaustiveness walk (casevariantin + a recursive checkvariantcovered): when a variant resolves to N_TTAGGED via a spread, recurse into its members. Additive + spread-gated -- typeeqast / casevariantpairmatch (#13) / casecovers untouched, so non-spread matches and 990-997 byte-id are unaffected. Also size a spread N_TTAGGED off each flattened member (mirror cstage check.c), dropping the inner union tag word (field 40B to 32B). Closes the #209 CHECKER reject; full fmt-byte-id still awaits cgen cluster #226 (io.read nominal-remap) + #227 (spread-widen ABI), so fmt tests stay cstage-only with retargeted comments. Adds test 792; regenerates w6c/wwdump combined.ww.
164 lines
5.4 KiB
C
164 lines
5.4 KiB
C
/*
|
|
* 792_spread_variant_match — project #209 close. Pins that the wwstage
|
|
* checker accepts a `match` over a tagged union with a `...inner` SPREAD
|
|
* variant, byte-identically with cstage (rule-10).
|
|
*
|
|
* THE BUG (wwstage-CHECKER-only, cs!=ww): a spread variant
|
|
* `type field = (...inner | str)` flattens its inner union's members
|
|
* into `field` (i64|bool|rune|str). cstage's resolve_type performs the
|
|
* flatten at type-build (cmd/wcc/check.c:651-674), so its match-arm
|
|
* validity + exhaustiveness see the flattened members. wwstage's checker
|
|
* (selfhost/cmd/wcc/check.ww checkmatchexhaust) walked the RAW AST
|
|
* `u.list`, which keeps the spread unexpanded — every member arm tripped
|
|
* "case: not a variant of scrutinee" and the phantom spread node tripped
|
|
* "match: variant not handled". So any module that matches over a spread
|
|
* union (e.g. fmt's `field = (...formattable | *mods)`) was
|
|
* wwstage-uncompilable. cstage compiled it fine. Hare flattens spreads;
|
|
* align UP to cstage (a too-strict checker, NOT a down-align).
|
|
*
|
|
* THE FIX (#209): casevariantin + a checkvariantcovered coverage helper
|
|
* recurse into a spread variant's inner-union members (resolvealias →
|
|
* N_TTAGGED → walk members), mirroring cstage's flatten at the AST
|
|
* layer; the #61a tinfo build additionally sizes the union off the
|
|
* flattened MEMBERS (not the whole inner union) so the slot matches
|
|
* cstage's. cgen already dispatches off the flattened tinfo.params
|
|
* (flatvariantidxt), so no cgen change is needed — checker-only.
|
|
*
|
|
* The PRE-FIX failure mode is a wwstage CHECKER REJECT, so the
|
|
* discriminator is `w6c_ww` on the driver-produced combined.ww
|
|
* succeeding AT ALL — pre-fix it errored out; post-fix it succeeds AND
|
|
* is byte-id with cstage's w6c. The probe stays on the param+match shape
|
|
* (a fn taking the spread union and matching it, exhaustive, no
|
|
* default): that is exactly what #209's checker fix covers and is
|
|
* byte-id. (Spread-union CONSTRUCTION/return-ABI is a separate,
|
|
* pre-existing both-stage cgen path — out of #209 scope — so it is
|
|
* deliberately NOT exercised here.) Self-contained single-file probe.
|
|
*/
|
|
#define _GNU_SOURCE
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
|
|
static int
|
|
runwait(const char *cmd)
|
|
{
|
|
int rc = system(cmd);
|
|
if (rc == -1) return -1;
|
|
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
slurp_eq(const char *a, const char *b)
|
|
{
|
|
FILE *fa = fopen(a, "rb");
|
|
FILE *fb = fopen(b, "rb");
|
|
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
|
int rc = 0;
|
|
for (;;) {
|
|
int ca = fgetc(fa);
|
|
int cb = fgetc(fb);
|
|
if (ca != cb) { rc = -1; break; }
|
|
if (ca == EOF) break;
|
|
}
|
|
fclose(fa); fclose(fb);
|
|
return rc;
|
|
}
|
|
|
|
static const char SRC[] =
|
|
"package main;\n"
|
|
"\n"
|
|
"type inner = (i64 | bool | rune);\n"
|
|
"type field = (...inner | str);\n"
|
|
"\n"
|
|
"fn classify(f: field) i64 = {\n"
|
|
" match (f) {\n"
|
|
" case let n: i64 => return n;\n"
|
|
" case let b: bool => return 1;\n"
|
|
" case let r: rune => return 2;\n"
|
|
" case let s: str => return (s.len: i64);\n"
|
|
" };\n"
|
|
"};\n"
|
|
"\n"
|
|
"export fn main() i32 = { return 0; };\n";
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
const char *bin = getenv("BIN");
|
|
if (!bin) bin = "out/bin";
|
|
char absbin[2048];
|
|
if (bin[0] != '/') {
|
|
char cwd[1024];
|
|
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
|
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
|
bin = absbin;
|
|
}
|
|
|
|
char w6c[2100], w6c_ww[2100];
|
|
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
|
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
|
if (access(w6c_ww, X_OK) != 0) {
|
|
fprintf(stderr, "792: w6c_ww missing — cannot run the cs==ww "
|
|
"byte-id gate (the whole point of this test)\n");
|
|
return 1;
|
|
}
|
|
|
|
char dir[] = "/tmp/ww792_XXXXXX";
|
|
if (mkdtemp(dir) == NULL) { fprintf(stderr, "792: mkdtemp\n"); return 1; }
|
|
|
|
char path[1024], cmd[4096], comb[1024], cs_s[1024], ws_s[1024];
|
|
int rc = 0;
|
|
|
|
snprintf(path, sizeof path, "%s/main.ww", dir);
|
|
FILE *f = fopen(path, "wb");
|
|
if (!f) { fprintf(stderr, "792: write main.ww\n"); rc = 1; goto done; }
|
|
fputs(SRC, f);
|
|
fclose(f);
|
|
|
|
snprintf(comb, sizeof comb, "%s/main.combined.ww", dir);
|
|
|
|
/* cstage driver build: produces the combined.ww + must accept. */
|
|
snprintf(cmd, sizeof cmd, "cd %s && %s/ww build -I %s %s/main.ww",
|
|
dir, bin, dir, dir);
|
|
if (runwait(cmd) != 0) {
|
|
fprintf(stderr, "792: cstage build failed\n");
|
|
rc = 1; goto done;
|
|
}
|
|
if (access(comb, 0) != 0) {
|
|
fprintf(stderr, "792: no combined.ww produced\n");
|
|
rc = 1; goto done;
|
|
}
|
|
|
|
/* The #209 discriminator: raw w6c_ww on the combined. Pre-fix the
|
|
* wwstage checker REJECTED the spread-union match arms (non-zero
|
|
* exit). Post-fix it accepts AND is byte-id with cstage's w6c. */
|
|
snprintf(cs_s, sizeof cs_s, "%s/cs.s", dir);
|
|
snprintf(ws_s, sizeof ws_s, "%s/ww.s", dir);
|
|
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, comb);
|
|
if (runwait(cmd) != 0) {
|
|
fprintf(stderr, "792: w6c on combined failed\n");
|
|
rc = 1; goto done;
|
|
}
|
|
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, comb);
|
|
if (runwait(cmd) != 0) {
|
|
fprintf(stderr, "792: w6c_ww on combined failed "
|
|
"(#209 spread-union match reject?)\n");
|
|
rc = 1; goto done;
|
|
}
|
|
if (slurp_eq(cs_s, ws_s) != 0) {
|
|
fprintf(stderr, "792: cs.s/ww.s DIFFER (rule-10 byte-id "
|
|
"violation)\n");
|
|
rc = 1; goto done;
|
|
}
|
|
|
|
printf("spread_variant_match: ok (w6c_ww accepts spread match + "
|
|
"cs==ww byte-id)\n");
|
|
|
|
done:
|
|
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
|
|
(void)runwait(cmd);
|
|
return rc;
|
|
}
|