Files
ww/test/wcc/792_spread_variant_match.c
Hojun-Cho bbd2ad390a test: retarget Pattern-A combined-feed gates to sep .sepwork layout (M4 E3, #94)
94a of the C-mig2 split (rob rule-11): 13 Pattern-A gates that fed a
.combined.ww to w6c/w6c_ww now drive two --sep builds (ww / ww_ww) and
byte-diff the concatenated per-package .sepwork/*.s, mirroring #93's
convention. Lands pre-flip while combined.ww still exists as the
reversible safety net. Test-only; all 5 binary pins HOLD.

989_enumcap_run deferred to 94b (its decisive assertion is a combined.ww
content diff, not a .s diff).
2026-06-18 16:44:41 +09:00

160 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 cdrv[2100], wdrv[2100];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
if (access(wdrv, X_OK) != 0) {
fprintf(stderr, "792: ww_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], 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);
/* #94: sep layout. The cstage driver's --sep build emits the root
* unit's asm to <stem>.sepwork/__root.s via its internal w6c; pin
* WW_PKGCACHE under the scratch dir so out/.pkgcache is untouched. */
snprintf(cs_s, sizeof cs_s, "%s/main.sepwork/__root.s", dir);
snprintf(ws_s, sizeof ws_s, "%s/mainww.sepwork/__root.s", dir);
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww",
dir, dir, cdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "792: cstage sep build failed\n");
rc = 1; goto done;
}
/* The #209 discriminator: the wwstage driver's --sep build runs the
* wwstage checker over the same root unit. Pre-fix it REJECTED the
* spread-union match arms (build fails); post-fix it accepts AND its
* w6c_ww-emitted __root.s is byte-id with the cstage __root.s. */
snprintf(cmd, sizeof cmd,
"cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww",
dir, dir, wdrv, dir, dir, dir);
if (runwait(cmd) != 0) {
fprintf(stderr, "792: wwstage sep build 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;
}