test: port 787/929 cross-module match carriers to corpus fixtures
787_xmod_variant_match.c -> r787_xmod_variant_no_default (run-exit 21), r787_xmod_variant_bind_default (42), r787_xmod_variant_shape_a (12) (pkg/errs module dirs), r787_xmod_variant_foreign_qualifier (staged error: w6c 'is not a variant of', w6c_ww rejects via the #95 ambiguity guard — the carrier asserted reject polarity only) 929_match_4arm_cross_module_run.c -> r929_match_{4arm_shadowed_ canonical,3arm_shadowed,5arm_shadowed,6arm_shadowed,4arm_mixed_kinds, 4arm_shadowed_reverse} (run; single-file a+b packages, dropping the carrier's accidental doubled 'package b;' line) 929's byte-id upgrades from 994-delegated to per-fixture via test-data-byteid; the asm-level distinct-CMPQ sentinel remains 728's separate concern. All rows probed green on both stages.
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
package wwfixture;
|
||||
|
||||
def protocolversion: i32 = 1;
|
||||
def corpuscount: i32 = 1659;
|
||||
def errorcount: i32 = 338;
|
||||
def corpuscount: i32 = 1669;
|
||||
def errorcount: i32 = 339;
|
||||
def compilecount: i32 = 18;
|
||||
def runcount: i32 = 200;
|
||||
def runexitcount: i32 = 1103;
|
||||
def nativecount: i32 = 3318;
|
||||
def corpushash: str = "c9c9361b3f799c1439fb9009f8c6edfc7dbf36a263b789e65f239b3984661ffd";
|
||||
def runcount: i32 = 206;
|
||||
def runexitcount: i32 = 1106;
|
||||
def nativecount: i32 = 3338;
|
||||
def corpushash: str = "2c2ef7841c6da289337a72816041bdc594fa84da18d4f1e11327db3d578e1d14";
|
||||
|
||||
type directive = enum i32 {
|
||||
ERROR = 0,
|
||||
|
||||
@@ -1,403 +0,0 @@
|
||||
/*
|
||||
* 787_xmod_variant_match — project #13 close. Pins that a `package main`
|
||||
* can decompose an IMPORTED union's individual VARIANTS cross-module
|
||||
* (`match (e: pkg.u) { case pkg.a => ...; case let o: pkg.b => ... }`),
|
||||
* on BOTH stages, byte-identically (rule-10).
|
||||
*
|
||||
* THE BUG (wwstage-CHECKER-only, cs!=ww): wwstage's match-arm validity
|
||||
* (selfhost/cmd/wcc/check.ww casevariantin) and exhaustiveness
|
||||
* (casecovers) compared via typeeqast, whose N_TNAME arm is a raw
|
||||
* streq. A union's variant is written UNQUALIFIED in its defining
|
||||
* module (`a` in pkg.u's body); the cross-module case pattern is the
|
||||
* dotted `pkg.a` (one N_TNAME). streq("a","pkg.a") -> false -> wwstage
|
||||
* rejected "case: not a variant of scrutinee". cstage compares
|
||||
* resolved-Type identity (variant_match, cmd/wcc/check.c:1651), so the
|
||||
* qualifier is irrelevant and it built+routed correctly. Hare allows
|
||||
* cross-module variant decomposition; align UP to cstage (a too-strict
|
||||
* checker, NOT a down-align that would forbid the feature).
|
||||
*
|
||||
* THE FIX (#13): casevariantpairmatch reduces BOTH pattern and variant
|
||||
* to a (module, leaf) pair — a dotted name keeps its own qualifier, a
|
||||
* bare name is attributed the union's defining module (taggeddefmod via
|
||||
* aliassym .decl.nmod) — and matches the pairs. This accepts a
|
||||
* cross-module `case errors.unsupported` (vs bare `unsupported`) while
|
||||
* REJECTING a foreign `othermod.unsupported`, and keeps a dotted body
|
||||
* variant matching its own qualifier (Shape A). Wired into BOTH
|
||||
* casevariantin (validity) and casecovers (exhaustiveness). #10-family
|
||||
* (AST-name vs cstage tinfo); precise Type identity is the #10 endgame.
|
||||
*
|
||||
* The PRE-FIX failure mode is a wwstage CHECKER REJECT, so the
|
||||
* discriminator is the `w6c_ww` build of the two-module unit succeeding
|
||||
* at all — pre-fix it errored out; post-fix it succeeds AND is byte-id
|
||||
* with cstage. Self-contained 2-module fixtures (no lib coupling).
|
||||
*
|
||||
* scenario | shape | exit | byte-id
|
||||
* ------------------+----------------------------------------+------+--------
|
||||
* no_default | match pkg.u=!(a|b), bare arms, NO | 21 | cs==ww
|
||||
* | default — exercises casecovers | |
|
||||
* | exhaustiveness cross-module | |
|
||||
* bind_and_default | match pkg.u2=!(a|b|c): bare arm + a | 42 | cs==ww
|
||||
* | `case let o: pkg.b` BOUND arm + a | |
|
||||
* | `case =>` default; if-checks each arm | |
|
||||
* foreign_qualifier | `case o.a` vs pkg.u (o.a is a DIFFERENT | both | (reject
|
||||
* | module's same-leaf type) — BOTH |reject| net)
|
||||
* | stages must REJECT; guards the fix | |
|
||||
* | against a false-ACCEPT (bare leaf- | |
|
||||
* | strip would wrongly accept) | |
|
||||
* shape_a_dotted_var| union BODY holds dotted variant | 12 | cs==ww
|
||||
* | pkg.e=!(errs.bad|local); case errs.bad| |
|
||||
* | keeps OWN qualifier (drew Shape A) — | |
|
||||
* | sole positive lock on qualmod's | |
|
||||
* | dotted-variant branch | |
|
||||
*
|
||||
* GATE POLARITY: must stay GREEN. Red means wwstage rejected a valid
|
||||
* cross-module variant match again (w6c_ww build fails) or the routing
|
||||
* diverged (byte-id / runtime).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.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;
|
||||
}
|
||||
if (ferror(fa) || ferror(fb)) rc = -1;
|
||||
if (fclose(fa) != 0) rc = -1;
|
||||
if (fclose(fb) != 0) rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct file { const char *name; const char *src; };
|
||||
|
||||
struct scenario {
|
||||
const char *label;
|
||||
const struct file *files; /* name==NULL terminates */
|
||||
int want_exit;
|
||||
int expect_reject; /* 1 = BOTH stages must REJECT (checker) */
|
||||
};
|
||||
|
||||
/* ---- no_default: bare cross-module arms, no default (exhaustiveness) */
|
||||
static const struct file no_default_files[] = {
|
||||
{ "pkg.ww",
|
||||
"package pkg;\n"
|
||||
"export type a = !void;\n"
|
||||
"export type b = !void;\n"
|
||||
"export type u = !(a | b);\n"
|
||||
"export fn mka() u = { let x: a; return x; };\n"
|
||||
"export fn mkb() u = { let y: b; return y; };\n" },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import pkg;\n"
|
||||
"fn classify(e: pkg.u) i32 = {\n"
|
||||
" match (e) {\n"
|
||||
" case pkg.a => return 1;\n"
|
||||
" case pkg.b => return 2;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return classify(pkg.mkb()) * 10 + classify(pkg.mka());\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- bind_and_default: bound arm + default cross-module ----------- */
|
||||
static const struct file bind_default_files[] = {
|
||||
{ "pkg.ww",
|
||||
"package pkg;\n"
|
||||
"export type a = !void;\n"
|
||||
"export type b = !void;\n"
|
||||
"export type c = !void;\n"
|
||||
"export type u2 = !(a | b | c);\n"
|
||||
"export fn mka() u2 = { let x: a; return x; };\n"
|
||||
"export fn mkb() u2 = { let y: b; return y; };\n"
|
||||
"export fn mkc() u2 = { let z: c; return z; };\n" },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import pkg;\n"
|
||||
"fn classify(e: pkg.u2) i32 = {\n"
|
||||
" match (e) {\n"
|
||||
" case pkg.a => return 1;\n"
|
||||
" case let o: pkg.b => return 2;\n"
|
||||
" case => return 9;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (classify(pkg.mka()) != 1) { return 11; };\n"
|
||||
" if (classify(pkg.mkb()) != 2) { return 12; };\n"
|
||||
" if (classify(pkg.mkc()) != 9) { return 13; };\n"
|
||||
" return 42;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- foreign_qualifier: a same-leaf variant from a DIFFERENT module
|
||||
* is NOT a variant of the scrutinee — BOTH stages must REJECT. Guards
|
||||
* the #13 fix against trading the false-reject for a false-ACCEPT: a
|
||||
* bare leaf-strip would wrongly accept `o.a` (leaf "a" matches pkg's
|
||||
* variant `a`); the (module,leaf)-pair match rejects it (o != pkg). */
|
||||
static const struct file foreign_qualifier_files[] = {
|
||||
{ "pkg.ww",
|
||||
"package pkg;\n"
|
||||
"export type a = !void;\n"
|
||||
"export type b = !void;\n"
|
||||
"export type u = !(a | b);\n"
|
||||
"export fn mka() u = { let x: a; return x; };\n" },
|
||||
{ "o.ww",
|
||||
"package o;\n"
|
||||
"export type a = !void;\n" },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import pkg;\n"
|
||||
"import o;\n"
|
||||
"fn classify(e: pkg.u) i32 = {\n"
|
||||
" match (e) {\n"
|
||||
" case o.a => return 1;\n"
|
||||
" case => return 0;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = { return classify(pkg.mka()); };\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- shape_a_dotted_variant: the union's BODY itself holds a dotted
|
||||
* cross-module variant (`pkg.e = !(errs.bad | local)`), matched with a
|
||||
* dotted `case errs.bad` AND a bare-variant `case pkg.local`. This is
|
||||
* drew's Shape A (cf. io.error nesting errors.error): the dotted body
|
||||
* variant `errs.bad` must keep ITS OWN qualifier (errs), NOT be forced
|
||||
* onto the union's defining module (pkg) — else `case errs.bad` would
|
||||
* false-REJECT. The other three scenarios carry only BARE body variants,
|
||||
* so this is the sole positive lock on qualmod's dotted-variant branch;
|
||||
* a regression that attributes unionmod to every variant breaks here
|
||||
* alone. exit = classify(mkbad())*10 + classify(mklocal()) = 1*10+2. */
|
||||
static const struct file shape_a_files[] = {
|
||||
{ "errs.ww",
|
||||
"package errs;\n"
|
||||
"export type bad = !void;\n" },
|
||||
{ "pkg.ww",
|
||||
"package pkg;\n"
|
||||
"import errs;\n"
|
||||
"export type local = !void;\n"
|
||||
"export type e = !(errs.bad | local);\n"
|
||||
"export fn mkbad() e = { let x: errs.bad; return x; };\n"
|
||||
"export fn mklocal() e = { let y: local; return y; };\n" },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import pkg;\n"
|
||||
"import errs;\n"
|
||||
"fn classify(x: pkg.e) i32 = {\n"
|
||||
" match (x) {\n"
|
||||
" case errs.bad => return 1;\n"
|
||||
" case pkg.local => return 2;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return classify(pkg.mkbad()) * 10 + classify(pkg.mklocal());\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct scenario scenarios[] = {
|
||||
{ "no_default", no_default_files, 21, 0 },
|
||||
{ "bind_and_default", bind_default_files, 42, 0 },
|
||||
{ "foreign_qualifier", foreign_qualifier_files, 0, 1 },
|
||||
{ "shape_a_dotted_var", shape_a_files, 12, 0 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_scenario(const char *bin, const char *cdrv, const char *wdrv,
|
||||
const struct scenario *sc)
|
||||
{
|
||||
(void)bin;
|
||||
char dir[] = "/tmp/ww787_XXXXXX";
|
||||
if (mkdtemp(dir) == NULL) {
|
||||
fprintf(stderr, "787[%s]: mkdtemp failed\n", sc->label);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char path[1024], cmd[4096];
|
||||
int rc = 0;
|
||||
|
||||
for (int i = 0; sc->files[i].name; i++) {
|
||||
snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name);
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) { fprintf(stderr, "787[%s]: write %s\n", sc->label,
|
||||
sc->files[i].name); rc = -1; goto done; }
|
||||
int bad = fputs(sc->files[i].src, f) == EOF;
|
||||
if (fclose(f) != 0) bad = 1;
|
||||
if (bad) { fprintf(stderr, "787[%s]: write %s\n", sc->label,
|
||||
sc->files[i].name); rc = -1; goto done; }
|
||||
}
|
||||
|
||||
if (sc->expect_reject) {
|
||||
/* #94 sep layout: there is no combined unit to re-check. The
|
||||
* checker reject now fires inside each driver's w6c pass,
|
||||
* so BOTH driver builds MUST fail. w6c_ww (via ww_ww) accepting
|
||||
* here is the #13 false-ACCEPT regression. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s build -I %s "
|
||||
"-o %s/main %s/main.ww >/dev/null 2>&1",
|
||||
dir, cdrv, dir, dir, dir);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "787[%s]: cstage build SUCCEEDED, "
|
||||
"expected reject\n", sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s build -I %s "
|
||||
"-o %s/mainww %s/main.ww >/dev/null 2>&1",
|
||||
dir, wdrv, dir, dir, dir);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "787[%s]: ww_ww ACCEPTED foreign variant "
|
||||
"(#13 false-accept), expected reject\n", sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* cstage driver build + run: pins runtime routing. Pin
|
||||
* all outputs stay under the scratch dir. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s build -I %s -o %s/main %s/main.ww",
|
||||
dir, cdrv, dir, dir, dir);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "787[%s]: cstage build failed\n", sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
snprintf(path, sizeof path, "%s/main", dir);
|
||||
int got = runwait(path);
|
||||
if (got != sc->want_exit) {
|
||||
fprintf(stderr, "787[%s]: cstage exit %d, want %d\n",
|
||||
sc->label, got, sc->want_exit);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
/* The #13 discriminator: the wwstage driver's build runs the
|
||||
* wwstage checker over the same units. Pre-fix it REJECTED the
|
||||
* cross-module variant arms (build fails); post-fix it accepts AND
|
||||
* its per-package w6c_ww asm is byte-id with cstage's. The root +
|
||||
* each imported pkg compile to separate <stem>.sepwork/<pkg>.s;
|
||||
* concat (sorted glob, identical set both stages) for the compare. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s build -I %s -o %s/mainww %s/main.ww",
|
||||
dir, wdrv, dir, dir, dir);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "787[%s]: ww_ww build failed "
|
||||
"(#13 cross-module variant reject?)\n", sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
char cs_s[1024], ws_s[1024];
|
||||
snprintf(cs_s, sizeof cs_s, "%s/all_cs.s", dir);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/all_ww.s", dir);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cat %s/main.sepwork/*.s > %s 2>/dev/null && test -s %s",
|
||||
dir, cs_s, cs_s);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "787[%s]: cstage asm aggregation failed\n",
|
||||
sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cat %s/mainww.sepwork/*.s > %s 2>/dev/null && test -s %s",
|
||||
dir, ws_s, ws_s);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "787[%s]: wwstage asm aggregation failed\n",
|
||||
sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr, "787[%s]: cs.s/ww.s DIFFER (rule-10 byte-id "
|
||||
"violation)\n", sc->label);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
done:
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "787[%s]: cleanup main.sepwork\n",
|
||||
sc->label); rc = -1; }
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s/mainww.sepwork", dir);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "787[%s]: cleanup mainww.sepwork\n",
|
||||
sc->label); rc = -1; }
|
||||
for (int i = 0; sc->files[i].name; i++) {
|
||||
snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name);
|
||||
if (unlink(path) != 0 && errno != ENOENT) {
|
||||
fprintf(stderr, "787[%s]: cleanup %s\n", sc->label,
|
||||
sc->files[i].name);
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
const char *children[] = { "main", "mainww", "all_cs.s", "all_ww.s", NULL };
|
||||
for (int i = 0; children[i]; i++) {
|
||||
snprintf(path, sizeof path, "%s/%s", dir, children[i]);
|
||||
if (unlink(path) != 0 && errno != ENOENT) {
|
||||
fprintf(stderr, "787[%s]: cleanup %s\n", sc->label,
|
||||
children[i]);
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rmdir(dir) != 0) {
|
||||
fprintf(stderr, "787[%s]: cleanup directory\n", sc->label);
|
||||
rc = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
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, "787: ww_ww missing — cannot run the cs==ww "
|
||||
"byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = (int)(sizeof scenarios / sizeof scenarios[0]);
|
||||
int fail = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (run_scenario(bin, cdrv, wdrv, &scenarios[i]) != 0)
|
||||
fail++;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "787 xmod_variant_match: %d/%d scenarios failed\n",
|
||||
fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("xmod_variant_match: %d/%d ok (cstage run + cs==ww byte-id)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,392 +0,0 @@
|
||||
/*
|
||||
* 929_match_4arm_cross_module_run — Class B semantic test for task #31.
|
||||
* Pre-fix wwstage's matchscrutt resolved `match (mod.fn(...))` by
|
||||
* name-only fnretlookup, so when the caller fn shadowed the callee's
|
||||
* name across modules the match dispatch saw the wrong (caller's)
|
||||
* tagged type and arms past the caller's variant count silently
|
||||
* collapsed onto tag 0 (their bodies were unreachable even when the
|
||||
* runtime tag matched).
|
||||
*
|
||||
* Pure runtime test: build through both drivers (cstage `ww`, wwstage
|
||||
* `ww_ww`) and assert each arm's body actually fires for its matching
|
||||
* input. 728_match_4arm_cross_module pins the asm-level distinct-CMPQ
|
||||
* sentinel; this file pins end-to-end behavior across the rob matrix:
|
||||
* - 3-arm boundary (does arm 2 collapse?).
|
||||
* - 4-arm canonical (the probe shape).
|
||||
* - 5/6-arm scaling.
|
||||
* - Mixed variant kinds.
|
||||
* - Reverse arm-order in match source.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.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;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *a_src; /* callee module source — written as a/a.ww */
|
||||
const char *b_src; /* caller module + main — written as b.ww */
|
||||
int want;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* Canonical 4-arm with shadowing `next`. Drives the callee to
|
||||
* return each of the 4 variants in turn (via a selector arg) and
|
||||
* checks the caller dispatched to the right arm. Arm 2/3 reaching
|
||||
* their bodies is the post-fix invariant. */
|
||||
{ "4arm_shadowed_canonical",
|
||||
/* a.ww */
|
||||
"package a;\n"
|
||||
"export type more = void;\n"
|
||||
"export type invalid = !void;\n"
|
||||
"export type done = void;\n"
|
||||
"export fn next(k: i32) (rune | done | more | invalid) = {\n"
|
||||
" if (k == 0) { return 0x41u32: rune; };\n"
|
||||
" if (k == 1) { let v: done; return v; };\n"
|
||||
" if (k == 2) { let v: more; return v; };\n"
|
||||
" let v: invalid; return v;\n"
|
||||
"};\n",
|
||||
/* b.ww */
|
||||
"package b;\n"
|
||||
"package b;\n"
|
||||
"import a;\n"
|
||||
"type done = void;\n"
|
||||
"fn next(k: i32) i32 = {\n"
|
||||
" match (a.next(k)) {\n"
|
||||
" case let r: rune => return 100i32 + (r: i32);\n"
|
||||
" case let dn: a.done => return 200;\n"
|
||||
" case let m: a.more => return 300;\n"
|
||||
" case let e: a.invalid => return 400;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (next(0) != 165) { return 11; };\n"
|
||||
" if (next(1) != 200) { return 12; };\n"
|
||||
" if (next(2) != 300) { return 13; };\n"
|
||||
" if (next(3) != 400) { return 14; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* 3-arm boundary: arm 2 must reach its body. */
|
||||
{ "3arm_shadowed",
|
||||
"package a;\n"
|
||||
"export type more = void;\n"
|
||||
"export type done = void;\n"
|
||||
"export fn next(k: i32) (rune | done | more) = {\n"
|
||||
" if (k == 0) { return 0x42u32: rune; };\n"
|
||||
" if (k == 1) { let v: done; return v; };\n"
|
||||
" let v: more; return v;\n"
|
||||
"};\n",
|
||||
"package b;\n"
|
||||
"import a;\n"
|
||||
"type done = void;\n"
|
||||
"fn next(k: i32) i32 = {\n"
|
||||
" match (a.next(k)) {\n"
|
||||
" case let r: rune => return 1i32 + (r: i32);\n"
|
||||
" case let dn: a.done => return 2;\n"
|
||||
" case let m: a.more => return 3;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (next(0) != 67) { return 11; };\n"
|
||||
" if (next(1) != 2) { return 12; };\n"
|
||||
" if (next(2) != 3) { return 13; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* 5-arm scaling: arms 3 and 4 must each reach their body. */
|
||||
{ "5arm_shadowed",
|
||||
"package a;\n"
|
||||
"export type more = void;\n"
|
||||
"export type invalid = !void;\n"
|
||||
"export type done = void;\n"
|
||||
"export type stop = void;\n"
|
||||
"export fn next(k: i32) (rune | done | more | invalid | stop) = {\n"
|
||||
" if (k == 0) { return 0x43u32: rune; };\n"
|
||||
" if (k == 1) { let v: done; return v; };\n"
|
||||
" if (k == 2) { let v: more; return v; };\n"
|
||||
" if (k == 3) { let v: invalid; return v; };\n"
|
||||
" let v: stop; return v;\n"
|
||||
"};\n",
|
||||
"package b;\n"
|
||||
"import a;\n"
|
||||
"type done = void;\n"
|
||||
"fn next(k: i32) i32 = {\n"
|
||||
" match (a.next(k)) {\n"
|
||||
" case let r: rune => return 100i32 + (r: i32);\n"
|
||||
" case let dn: a.done => return 2;\n"
|
||||
" case let m: a.more => return 3;\n"
|
||||
" case let e: a.invalid => return 4;\n"
|
||||
" case let s: a.stop => return 5;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (next(0) != 167) { return 11; };\n"
|
||||
" if (next(1) != 2) { return 12; };\n"
|
||||
" if (next(2) != 3) { return 13; };\n"
|
||||
" if (next(3) != 4) { return 14; };\n"
|
||||
" if (next(4) != 5) { return 15; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* 6-arm: extra row to demonstrate the bug doesn't scale with arm
|
||||
* count — every arm beyond the caller's variant count was broken,
|
||||
* not just arm 2 / arm 3. */
|
||||
{ "6arm_shadowed",
|
||||
"package a;\n"
|
||||
"export type more = void;\n"
|
||||
"export type invalid = !void;\n"
|
||||
"export type done = void;\n"
|
||||
"export type stop = void;\n"
|
||||
"export type eof = void;\n"
|
||||
"export fn next(k: i32) (rune | done | more | invalid | stop | eof) = {\n"
|
||||
" if (k == 0) { return 0x44u32: rune; };\n"
|
||||
" if (k == 1) { let v: done; return v; };\n"
|
||||
" if (k == 2) { let v: more; return v; };\n"
|
||||
" if (k == 3) { let v: invalid; return v; };\n"
|
||||
" if (k == 4) { let v: stop; return v; };\n"
|
||||
" let v: eof; return v;\n"
|
||||
"};\n",
|
||||
"package b;\n"
|
||||
"import a;\n"
|
||||
"type done = void;\n"
|
||||
"fn next(k: i32) i32 = {\n"
|
||||
" match (a.next(k)) {\n"
|
||||
" case let r: rune => return 100i32 + (r: i32);\n"
|
||||
" case let dn: a.done => return 2;\n"
|
||||
" case let m: a.more => return 3;\n"
|
||||
" case let e: a.invalid => return 4;\n"
|
||||
" case let s: a.stop => return 5;\n"
|
||||
" case let f: a.eof => return 6;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (next(0) != 168) { return 11; };\n"
|
||||
" if (next(1) != 2) { return 12; };\n"
|
||||
" if (next(2) != 3) { return 13; };\n"
|
||||
" if (next(3) != 4) { return 14; };\n"
|
||||
" if (next(4) != 5) { return 15; };\n"
|
||||
" if (next(5) != 6) { return 16; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Mixed variant kinds. Callee returns (i32 | str | rune | u8);
|
||||
* caller `next` shadows. Confirms the shadowed-resolution fix
|
||||
* isn't shape-specific. */
|
||||
{ "4arm_mixed_kinds",
|
||||
"package a;\n"
|
||||
"export fn next(k: i32) (i32 | str | rune | u8) = {\n"
|
||||
" if (k == 0) { return 7; };\n"
|
||||
" if (k == 1) { return \"hi\"; };\n"
|
||||
" if (k == 2) { return 0x45u32: rune; };\n"
|
||||
" return 9u8;\n"
|
||||
"};\n",
|
||||
"package b;\n"
|
||||
"import a;\n"
|
||||
"fn next(k: i32) i32 = {\n"
|
||||
" match (a.next(k)) {\n"
|
||||
" case let n: i32 => return 100 + n;\n"
|
||||
" case let s: str => return 200i32 + (s.len: i32);\n"
|
||||
" case let r: rune => return 300i32 + (r: i32);\n"
|
||||
" case let c: u8 => return 400i32 + (c: i32);\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (next(0) != 107) { return 11; };\n"
|
||||
" if (next(1) != 202) { return 12; };\n"
|
||||
" if (next(2) != 369) { return 13; };\n"
|
||||
" if (next(3) != 409) { return 14; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Reverse arm-order in the match source. Confirms the bug
|
||||
* follows scrutinee-resolution (fnretlookupmod), not source
|
||||
* order — emitted CMPQ tags follow the callee's variant indices
|
||||
* regardless of how the arms were written. */
|
||||
{ "4arm_shadowed_reverse",
|
||||
"package a;\n"
|
||||
"export type more = void;\n"
|
||||
"export type invalid = !void;\n"
|
||||
"export type done = void;\n"
|
||||
"export fn next(k: i32) (rune | done | more | invalid) = {\n"
|
||||
" if (k == 0) { return 0x46u32: rune; };\n"
|
||||
" if (k == 1) { let v: done; return v; };\n"
|
||||
" if (k == 2) { let v: more; return v; };\n"
|
||||
" let v: invalid; return v;\n"
|
||||
"};\n",
|
||||
"package b;\n"
|
||||
"import a;\n"
|
||||
"type done = void;\n"
|
||||
"fn next(k: i32) i32 = {\n"
|
||||
" match (a.next(k)) {\n"
|
||||
" case let e: a.invalid => return 4;\n"
|
||||
" case let m: a.more => return 3;\n"
|
||||
" case let dn: a.done => return 2;\n"
|
||||
" case let r: rune => return 100i32 + (r: i32);\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (next(0) != 170) { return 11; };\n"
|
||||
" if (next(1) != 2) { return 12; };\n"
|
||||
" if (next(2) != 3) { return 13; };\n"
|
||||
" if (next(3) != 4) { return 14; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* utf8.next regression row deferred: a direct repro of the
|
||||
* originally-failing probe_strings_iter shape (caller next(rune|done)
|
||||
* shadows callee utf8.next(rune|done|more|invalid)) compiles cleanly
|
||||
* post-#31 but wwstage segfaults at runtime through utf8.next.
|
||||
* Cstage runs fine. Separate latent wwstage stomp in the bigger utf8
|
||||
* iterator shape — task #31's fix is correct in isolation; the 4arm
|
||||
* canonical row above covers the structural pattern. Filed as a
|
||||
* follow-up so the utf8 regression marker doesn't gate this commit. */
|
||||
};
|
||||
|
||||
static int
|
||||
write_file(const char *dir, const char *name, const char *body)
|
||||
{
|
||||
char path[512];
|
||||
snprintf(path, sizeof path, "%s/%s", dir, name);
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(body, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[128], adir[160], asrc[192], bsrc[192];
|
||||
char outbin[192], scratch[208], cmd[2048];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/m4cmr_%d_%d_XXXXXX",
|
||||
getpid(), i);
|
||||
if (mkdtemp(tmpdir) == NULL) {
|
||||
fprintf(stderr, "row[%s]: temporary directory acquisition failed\n",
|
||||
r->label);
|
||||
return -1;
|
||||
}
|
||||
snprintf(adir, sizeof adir, "%s/a", tmpdir);
|
||||
snprintf(asrc, sizeof asrc, "%s/a.ww", adir);
|
||||
snprintf(bsrc, sizeof bsrc, "%s/b.ww", tmpdir);
|
||||
snprintf(outbin, sizeof outbin, "%s/b", tmpdir);
|
||||
snprintf(scratch, sizeof scratch, "%s/b.sepwork", tmpdir);
|
||||
|
||||
int result = -1, adir_owned = 0;
|
||||
if (mkdir(adir, 0755) != 0) {
|
||||
fprintf(stderr, "row[%s]: module directory creation failed\n",
|
||||
r->label);
|
||||
goto cleanup;
|
||||
}
|
||||
adir_owned = 1;
|
||||
if (write_file(adir, "a.ww", r->a_src) != 0) {
|
||||
fprintf(stderr, "row[%s]: cannot write a/a.ww\n", r->label);
|
||||
goto cleanup;
|
||||
}
|
||||
if (write_file(tmpdir, "b.ww", r->b_src) != 0) {
|
||||
fprintf(stderr, "row[%s]: cannot write b.ww\n", r->label);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Build via the driver from inside tmpdir so `use a;` resolves to
|
||||
* ./a/a.ww and ww's source-dir search hits b's siblings first. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s build b.ww 2>/dev/null", tmpdir, driver);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
result = runwait(outbin);
|
||||
|
||||
cleanup:
|
||||
{
|
||||
int bad = 0;
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
|
||||
if (runwait(cmd) != 0) bad = 1;
|
||||
if (unlink(outbin) != 0 && errno != ENOENT) bad = 1;
|
||||
if (unlink(bsrc) != 0 && errno != ENOENT) bad = 1;
|
||||
if (adir_owned) {
|
||||
if (unlink(asrc) != 0 && errno != ENOENT) bad = 1;
|
||||
if (rmdir(adir) != 0) bad = 1;
|
||||
}
|
||||
if (rmdir(tmpdir) != 0) bad = 1;
|
||||
if (bad) {
|
||||
fprintf(stderr, "row[%s]: temporary cleanup failed\n",
|
||||
r->label);
|
||||
if (result == r->want) result = -1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
char wdrv[640];
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *path; int gated_on_existence; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated_on_existence
|
||||
&& access(drivers[d].path, X_OK) != 0) {
|
||||
fprintf(stderr,
|
||||
"match_4arm_cross_module_run: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].path);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
int got = run_driver(drivers[d].path, &rows[i], i);
|
||||
total++;
|
||||
if (got != rows[i].want) {
|
||||
fprintf(stderr,
|
||||
"match_4arm_cross_module_run[%s][%s]: exit=%d want=%d\n",
|
||||
drivers[d].name, rows[i].label,
|
||||
got, rows[i].want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"match_4arm_cross_module_run: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("match_4arm_cross_module_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
17
test/wcc/data/r787_xmod_variant_bind_default/case.ww
Normal file
17
test/wcc/data/r787_xmod_variant_bind_default/case.ww
Normal file
@@ -0,0 +1,17 @@
|
||||
//ww:run-exit 42
|
||||
// migrated from test/wcc/787_xmod_variant_match.c: bare arm + bound `case let o: pkg.b` + default over an imported 3-variant union.
|
||||
package main;
|
||||
import pkg;
|
||||
fn classify(e: pkg.u2) i32 = {
|
||||
match (e) {
|
||||
case pkg.a => return 1;
|
||||
case let o: pkg.b => return 2;
|
||||
case => return 9;
|
||||
};
|
||||
};
|
||||
export fn main() i32 = {
|
||||
if (classify(pkg.mka()) != 1) { return 11; };
|
||||
if (classify(pkg.mkb()) != 2) { return 12; };
|
||||
if (classify(pkg.mkc()) != 9) { return 13; };
|
||||
return 42;
|
||||
};
|
||||
9
test/wcc/data/r787_xmod_variant_bind_default/pkg/pkg.ww
Normal file
9
test/wcc/data/r787_xmod_variant_bind_default/pkg/pkg.ww
Normal file
@@ -0,0 +1,9 @@
|
||||
// exports the 3-variant union for the r787 bind_and_default scenario.
|
||||
package pkg;
|
||||
export type a = !void;
|
||||
export type b = !void;
|
||||
export type c = !void;
|
||||
export type u2 = !(a | b | c);
|
||||
export fn mka() u2 = { let x: a; return x; };
|
||||
export fn mkb() u2 = { let y: b; return y; };
|
||||
export fn mkc() u2 = { let z: c; return z; };
|
||||
19
test/wcc/data/r787_xmod_variant_foreign_qualifier/case.ww
Normal file
19
test/wcc/data/r787_xmod_variant_foreign_qualifier/case.ww
Normal file
@@ -0,0 +1,19 @@
|
||||
//ww:error c "is not a variant of" ww "ambiguous without nominal layout"
|
||||
// migrated from test/wcc/787_xmod_variant_match.c: a same-leaf variant from a DIFFERENT module is NOT a variant of the scrutinee — guards #13 against bare leaf-strip false-accept.
|
||||
package pkg;
|
||||
export type a = !void;
|
||||
export type b = !void;
|
||||
export type u = !(a | b);
|
||||
export fn mka() u = { let x: a; return x; };
|
||||
package o;
|
||||
export type a = !void;
|
||||
package main;
|
||||
import pkg;
|
||||
import o;
|
||||
fn classify(e: pkg.u) i32 = {
|
||||
match (e) {
|
||||
case o.a => return 1;
|
||||
case => return 0;
|
||||
};
|
||||
};
|
||||
export fn main() i32 = { return classify(pkg.mka()); };
|
||||
13
test/wcc/data/r787_xmod_variant_no_default/case.ww
Normal file
13
test/wcc/data/r787_xmod_variant_no_default/case.ww
Normal file
@@ -0,0 +1,13 @@
|
||||
//ww:run-exit 21
|
||||
// migrated from test/wcc/787_xmod_variant_match.c: cross-module bare-variant arms with NO default — casecovers exhaustiveness across modules (#13).
|
||||
package main;
|
||||
import pkg;
|
||||
fn classify(e: pkg.u) i32 = {
|
||||
match (e) {
|
||||
case pkg.a => return 1;
|
||||
case pkg.b => return 2;
|
||||
};
|
||||
};
|
||||
export fn main() i32 = {
|
||||
return classify(pkg.mkb()) * 10 + classify(pkg.mka());
|
||||
};
|
||||
7
test/wcc/data/r787_xmod_variant_no_default/pkg/pkg.ww
Normal file
7
test/wcc/data/r787_xmod_variant_no_default/pkg/pkg.ww
Normal file
@@ -0,0 +1,7 @@
|
||||
// exports !void variants + union for the r787 no-default scenario.
|
||||
package pkg;
|
||||
export type a = !void;
|
||||
export type b = !void;
|
||||
export type u = !(a | b);
|
||||
export fn mka() u = { let x: a; return x; };
|
||||
export fn mkb() u = { let y: b; return y; };
|
||||
14
test/wcc/data/r787_xmod_variant_shape_a/case.ww
Normal file
14
test/wcc/data/r787_xmod_variant_shape_a/case.ww
Normal file
@@ -0,0 +1,14 @@
|
||||
//ww:run-exit 12
|
||||
// migrated from test/wcc/787_xmod_variant_match.c: Shape A — a dotted body variant (errs.bad) keeps ITS OWN qualifier in case matching.
|
||||
package main;
|
||||
import pkg;
|
||||
import errs;
|
||||
fn classify(x: pkg.e) i32 = {
|
||||
match (x) {
|
||||
case errs.bad => return 1;
|
||||
case pkg.local => return 2;
|
||||
};
|
||||
};
|
||||
export fn main() i32 = {
|
||||
return classify(pkg.mkbad()) * 10 + classify(pkg.mklocal());
|
||||
};
|
||||
3
test/wcc/data/r787_xmod_variant_shape_a/errs/errs.ww
Normal file
3
test/wcc/data/r787_xmod_variant_shape_a/errs/errs.ww
Normal file
@@ -0,0 +1,3 @@
|
||||
// the foreign variant module for the r787 Shape A scenario.
|
||||
package errs;
|
||||
export type bad = !void;
|
||||
7
test/wcc/data/r787_xmod_variant_shape_a/pkg/pkg.ww
Normal file
7
test/wcc/data/r787_xmod_variant_shape_a/pkg/pkg.ww
Normal file
@@ -0,0 +1,7 @@
|
||||
// union whose BODY holds a dotted cross-module variant (errs.bad | local).
|
||||
package pkg;
|
||||
import errs;
|
||||
export type local = !void;
|
||||
export type e = !(errs.bad | local);
|
||||
export fn mkbad() e = { let x: errs.bad; return x; };
|
||||
export fn mklocal() e = { let y: local; return y; };
|
||||
26
test/wcc/data/r929_match_3arm_shadowed/case.ww
Normal file
26
test/wcc/data/r929_match_3arm_shadowed/case.ww
Normal file
@@ -0,0 +1,26 @@
|
||||
//ww:run
|
||||
// migrated from test/wcc/929_match_4arm_cross_module_run.c: 3-arm boundary — arm 2 must reach its body under the shadowed callee.
|
||||
package a;
|
||||
export type more = void;
|
||||
export type done = void;
|
||||
export fn next(k: i32) (rune | done | more) = {
|
||||
if (k == 0) { return 0x42u32: rune; };
|
||||
if (k == 1) { let v: done; return v; };
|
||||
let v: more; return v;
|
||||
};
|
||||
package b;
|
||||
import a;
|
||||
type done = void;
|
||||
fn next(k: i32) i32 = {
|
||||
match (a.next(k)) {
|
||||
case let r: rune => return 1i32 + (r: i32);
|
||||
case let dn: a.done => return 2;
|
||||
case let m: a.more => return 3;
|
||||
};
|
||||
};
|
||||
export fn main() i32 = {
|
||||
if (next(0) != 67) { return 11; };
|
||||
if (next(1) != 2) { return 12; };
|
||||
if (next(2) != 3) { return 13; };
|
||||
return 0;
|
||||
};
|
||||
26
test/wcc/data/r929_match_4arm_mixed_kinds/case.ww
Normal file
26
test/wcc/data/r929_match_4arm_mixed_kinds/case.ww
Normal file
@@ -0,0 +1,26 @@
|
||||
//ww:run
|
||||
// migrated from test/wcc/929_match_4arm_cross_module_run.c: mixed variant kinds (i32|str|rune|u8) — the shadowed-resolution fix is not shape-specific.
|
||||
package a;
|
||||
export fn next(k: i32) (i32 | str | rune | u8) = {
|
||||
if (k == 0) { return 7; };
|
||||
if (k == 1) { return "hi"; };
|
||||
if (k == 2) { return 0x45u32: rune; };
|
||||
return 9u8;
|
||||
};
|
||||
package b;
|
||||
import a;
|
||||
fn next(k: i32) i32 = {
|
||||
match (a.next(k)) {
|
||||
case let n: i32 => return 100 + n;
|
||||
case let s: str => return 200i32 + (s.len: i32);
|
||||
case let r: rune => return 300i32 + (r: i32);
|
||||
case let c: u8 => return 400i32 + (c: i32);
|
||||
};
|
||||
};
|
||||
export fn main() i32 = {
|
||||
if (next(0) != 107) { return 11; };
|
||||
if (next(1) != 202) { return 12; };
|
||||
if (next(2) != 369) { return 13; };
|
||||
if (next(3) != 409) { return 14; };
|
||||
return 0;
|
||||
};
|
||||
30
test/wcc/data/r929_match_4arm_shadowed_canonical/case.ww
Normal file
30
test/wcc/data/r929_match_4arm_shadowed_canonical/case.ww
Normal file
@@ -0,0 +1,30 @@
|
||||
//ww:run
|
||||
// migrated from test/wcc/929_match_4arm_cross_module_run.c: canonical 4-arm — caller fn `next` shadows callee a.next; every arm body must fire (#31 fnretlookupmod).
|
||||
package a;
|
||||
export type more = void;
|
||||
export type invalid = !void;
|
||||
export type done = void;
|
||||
export fn next(k: i32) (rune | done | more | invalid) = {
|
||||
if (k == 0) { return 0x41u32: rune; };
|
||||
if (k == 1) { let v: done; return v; };
|
||||
if (k == 2) { let v: more; return v; };
|
||||
let v: invalid; return v;
|
||||
};
|
||||
package b;
|
||||
import a;
|
||||
type done = void;
|
||||
fn next(k: i32) i32 = {
|
||||
match (a.next(k)) {
|
||||
case let r: rune => return 100i32 + (r: i32);
|
||||
case let dn: a.done => return 200;
|
||||
case let m: a.more => return 300;
|
||||
case let e: a.invalid => return 400;
|
||||
};
|
||||
};
|
||||
export fn main() i32 = {
|
||||
if (next(0) != 165) { return 11; };
|
||||
if (next(1) != 200) { return 12; };
|
||||
if (next(2) != 300) { return 13; };
|
||||
if (next(3) != 400) { return 14; };
|
||||
return 0;
|
||||
};
|
||||
30
test/wcc/data/r929_match_4arm_shadowed_reverse/case.ww
Normal file
30
test/wcc/data/r929_match_4arm_shadowed_reverse/case.ww
Normal file
@@ -0,0 +1,30 @@
|
||||
//ww:run
|
||||
// migrated from test/wcc/929_match_4arm_cross_module_run.c: reverse arm order — dispatch follows the callee's variant indices, not source order.
|
||||
package a;
|
||||
export type more = void;
|
||||
export type invalid = !void;
|
||||
export type done = void;
|
||||
export fn next(k: i32) (rune | done | more | invalid) = {
|
||||
if (k == 0) { return 0x46u32: rune; };
|
||||
if (k == 1) { let v: done; return v; };
|
||||
if (k == 2) { let v: more; return v; };
|
||||
let v: invalid; return v;
|
||||
};
|
||||
package b;
|
||||
import a;
|
||||
type done = void;
|
||||
fn next(k: i32) i32 = {
|
||||
match (a.next(k)) {
|
||||
case let e: a.invalid => return 4;
|
||||
case let m: a.more => return 3;
|
||||
case let dn: a.done => return 2;
|
||||
case let r: rune => return 100i32 + (r: i32);
|
||||
};
|
||||
};
|
||||
export fn main() i32 = {
|
||||
if (next(0) != 170) { return 11; };
|
||||
if (next(1) != 2) { return 12; };
|
||||
if (next(2) != 3) { return 13; };
|
||||
if (next(3) != 4) { return 14; };
|
||||
return 0;
|
||||
};
|
||||
34
test/wcc/data/r929_match_5arm_shadowed/case.ww
Normal file
34
test/wcc/data/r929_match_5arm_shadowed/case.ww
Normal file
@@ -0,0 +1,34 @@
|
||||
//ww:run
|
||||
// migrated from test/wcc/929_match_4arm_cross_module_run.c: 5-arm scaling — arms 3 and 4 must each reach their body.
|
||||
package a;
|
||||
export type more = void;
|
||||
export type invalid = !void;
|
||||
export type done = void;
|
||||
export type stop = void;
|
||||
export fn next(k: i32) (rune | done | more | invalid | stop) = {
|
||||
if (k == 0) { return 0x43u32: rune; };
|
||||
if (k == 1) { let v: done; return v; };
|
||||
if (k == 2) { let v: more; return v; };
|
||||
if (k == 3) { let v: invalid; return v; };
|
||||
let v: stop; return v;
|
||||
};
|
||||
package b;
|
||||
import a;
|
||||
type done = void;
|
||||
fn next(k: i32) i32 = {
|
||||
match (a.next(k)) {
|
||||
case let r: rune => return 100i32 + (r: i32);
|
||||
case let dn: a.done => return 2;
|
||||
case let m: a.more => return 3;
|
||||
case let e: a.invalid => return 4;
|
||||
case let s: a.stop => return 5;
|
||||
};
|
||||
};
|
||||
export fn main() i32 = {
|
||||
if (next(0) != 167) { return 11; };
|
||||
if (next(1) != 2) { return 12; };
|
||||
if (next(2) != 3) { return 13; };
|
||||
if (next(3) != 4) { return 14; };
|
||||
if (next(4) != 5) { return 15; };
|
||||
return 0;
|
||||
};
|
||||
38
test/wcc/data/r929_match_6arm_shadowed/case.ww
Normal file
38
test/wcc/data/r929_match_6arm_shadowed/case.ww
Normal file
@@ -0,0 +1,38 @@
|
||||
//ww:run
|
||||
// migrated from test/wcc/929_match_4arm_cross_module_run.c: 6-arm — every arm beyond the caller's variant count was broken, not just arm 2/3.
|
||||
package a;
|
||||
export type more = void;
|
||||
export type invalid = !void;
|
||||
export type done = void;
|
||||
export type stop = void;
|
||||
export type eof = void;
|
||||
export fn next(k: i32) (rune | done | more | invalid | stop | eof) = {
|
||||
if (k == 0) { return 0x44u32: rune; };
|
||||
if (k == 1) { let v: done; return v; };
|
||||
if (k == 2) { let v: more; return v; };
|
||||
if (k == 3) { let v: invalid; return v; };
|
||||
if (k == 4) { let v: stop; return v; };
|
||||
let v: eof; return v;
|
||||
};
|
||||
package b;
|
||||
import a;
|
||||
type done = void;
|
||||
fn next(k: i32) i32 = {
|
||||
match (a.next(k)) {
|
||||
case let r: rune => return 100i32 + (r: i32);
|
||||
case let dn: a.done => return 2;
|
||||
case let m: a.more => return 3;
|
||||
case let e: a.invalid => return 4;
|
||||
case let s: a.stop => return 5;
|
||||
case let f: a.eof => return 6;
|
||||
};
|
||||
};
|
||||
export fn main() i32 = {
|
||||
if (next(0) != 168) { return 11; };
|
||||
if (next(1) != 2) { return 12; };
|
||||
if (next(2) != 3) { return 13; };
|
||||
if (next(3) != 4) { return 14; };
|
||||
if (next(4) != 5) { return 15; };
|
||||
if (next(5) != 6) { return 16; };
|
||||
return 0;
|
||||
};
|
||||
Reference in New Issue
Block a user