test: port the xmod same-leaf collision carriers to corpus fixtures
752_modparam_callee.c -> r752_modparam_xmod_variadic_vs_scalar,
r752_modparam_variadic_no_collision (run; alpha/beta module dirs)
784_xmod_alias_struct_collide_run.c -> r784_xmod_alias_struct_collide
(run-exit 42), r784_xmod_alias_struct_symmetric (21),
r784_xmod_alias_control (30) (sa/sb module dirs)
793_xmod_struct_argpush_collide_run.c -> r793_xmod_argpush_
{recvpush_t16 107, fieldread_t16 107, combined_t16 114,
recvpush_t12 6} (m1/m2 module dirs)
797_xmod_struct_field_layout_collide_run.c -> r797_xmod_layout_
{ptrread_t16 107, letcopy_t16 107, addrptr_pq 10, addrval_pq 10,
ptrwrite_t16 109, valwrite_t16 109} (m1/m2) + r797_nestfill_box (66)
r770-style sibling module dirs express the trees; the fixture corpus
runs both drivers natively (784's wwstage-build-only leg upgrades to
build+run) and test-data-byteid owns each carrier's aggregated per-
package .s compare (letcopy_t16's real discriminator). All 16 probed
green on both stages including per-package byte identity.
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
package wwfixture;
|
||||
|
||||
def protocolversion: i32 = 1;
|
||||
def corpuscount: i32 = 1643;
|
||||
def corpuscount: i32 = 1659;
|
||||
def errorcount: i32 = 338;
|
||||
def compilecount: i32 = 18;
|
||||
def runcount: i32 = 198;
|
||||
def runexitcount: i32 = 1089;
|
||||
def nativecount: i32 = 3286;
|
||||
def corpushash: str = "c2bfc683a76c7bffa727612f102893c7dbfdb9e73e56212eed8687b8087d7870";
|
||||
def runcount: i32 = 200;
|
||||
def runexitcount: i32 = 1103;
|
||||
def nativecount: i32 = 3318;
|
||||
def corpushash: str = "c9c9361b3f799c1439fb9009f8c6edfc7dbf36a263b789e65f239b3984661ffd";
|
||||
|
||||
type directive = enum i32 {
|
||||
ERROR = 0,
|
||||
|
||||
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* 752_modparam_callee — sentinel for task #16 (sub-bug of #4d):
|
||||
* wwstage's callee_variadic_param helper (selfhost/cmd/wcc/
|
||||
* cgenutil.ww) took an N_DOT callee but routed both arms through
|
||||
* bare-leaf fnparamslookup. Post-#4d that walk is same-module-first
|
||||
* then head-walk fallback — for a cross-module N_DOT call from a
|
||||
* caller whose c.curmod doesn't match either side, the head-walk
|
||||
* returns whichever module's same-leaf fn sits at the head of
|
||||
* c.fnrets. When that head-side fn has a divergent variadic-vs-
|
||||
* non-variadic shape the callee_variadic_param result fires the
|
||||
* gather machinery against the wrong-module shape: arg-prep
|
||||
* builds a slice for `xs: T...` while the CALL still targets the
|
||||
* non-variadic module-qualified label. Label correct, ABI wrong.
|
||||
*
|
||||
* Surfaced by attempting strings.contains' Hare-shaped
|
||||
* `(needles: (str | rune)...)` graduation while bytes.contains
|
||||
* stays at `(needle: (u8 | []u8))` — caller's
|
||||
* `bytes.contains(b, n)` site picked strings.contains' variadic
|
||||
* params for arg-prep.
|
||||
*
|
||||
* Cstage carries no sister bug: cmd/w6c/cgen.c reads the callee
|
||||
* fn-type from the typed `n->lhs->type` (TY_FN sig) and walks
|
||||
* `callee_params->variadic` directly, module-aware via the typed
|
||||
* AST. Mirror of #4d / #28 / #31 / #34: cstage sidesteps every
|
||||
* bare-leaf table.
|
||||
*
|
||||
* Fix: route the N_DOT arm through fnparamslookupmod(c, leaf,
|
||||
* callee.lhs.str). The N_IDENT arm stays on bare fnparamslookup
|
||||
* (already same-module-first post-#4d).
|
||||
*
|
||||
* row | what it pins
|
||||
* ---------------------------------------|-------------------------
|
||||
* cross_module_same_leaf_variadic_vs_scalar
|
||||
* | primary wedge. A.foo
|
||||
* | variadic, B.foo scalar
|
||||
* | same leaf. Caller in
|
||||
* | a third package calls
|
||||
* | B.foo with a tagged
|
||||
* | arg; pre-fix wwstage
|
||||
* | preps the call site as
|
||||
* | variadic gather and
|
||||
* | mis-marshals — rc=11.
|
||||
* | Post-fix both stages
|
||||
* | rc=0.
|
||||
* bare_leaf_no_collision | control: variadic fn
|
||||
* | declared in only one
|
||||
* | module, called via
|
||||
* | bare leaf (N_IDENT)
|
||||
* | from the same module.
|
||||
* | Confirms the N_IDENT
|
||||
* | arm still graduates
|
||||
* | through bare
|
||||
* | fnparamslookup
|
||||
* | (post-#4d).
|
||||
*
|
||||
* The standalone same_module_same_leaf runtime control now lives in
|
||||
* test/wcc/data/r75_modparam_same_module_same_leaf/case.ww. This wrapper
|
||||
* retains only the two package-layout rows that require separately named
|
||||
* imported source files and therefore are not expressible by case.ww.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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;
|
||||
int want_exit;
|
||||
int n_files;
|
||||
const char *files[8]; /* {path, src, path, src, ...} */
|
||||
const char *entry; /* file to `ww build` (relative) */
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* Primary wedge. alpha.foo is variadic (i32...), beta.foo is
|
||||
* non-variadic with a tagged-union param. Caller in `main`
|
||||
* imports both and calls beta.foo with a tagged (u8|[]u8). The
|
||||
* combined-source layout puts `beta` after `alpha` so beta.foo
|
||||
* sits at the head of c.fnrets among non-main fns; with
|
||||
* c.curmod="main" the bare-leaf walk falls through same-module
|
||||
* to head-walk and picks beta.foo here — but reverse the import
|
||||
* order or any other ordering perturbation flips it to alpha.foo.
|
||||
* Either fnparamslookup result is wrong half the time; the
|
||||
* N_DOT arm must route through fnparamslookupmod to be stable.
|
||||
* Pre-#16 the head-pick happens to be the variadic side and
|
||||
* arg-prep builds an `i32...` gather slice while CALL targets
|
||||
* beta.foo's scalar ABI. */
|
||||
{ "cross_module_same_leaf_variadic_vs_scalar", 0, 3,
|
||||
{ "alpha/alpha.ww",
|
||||
"package alpha;\n"
|
||||
"export fn foo(a: i32, xs: i32...) i32 = {\n"
|
||||
"\treturn a + xs.len;\n"
|
||||
"};\n",
|
||||
"beta/beta.ww",
|
||||
"package beta;\n"
|
||||
"export fn foo(a: i32, n: (u8 | []u8)) i32 = {\n"
|
||||
"\tmatch (n) {\n"
|
||||
"\tcase let c: u8 => return a + (c: i32);\n"
|
||||
"\tcase let s: []u8 => return a + s.len;\n"
|
||||
"\t};\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
"main.ww",
|
||||
"package main;\n"
|
||||
"import alpha;\n"
|
||||
"import beta;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet n: (u8 | []u8) = 7u8;\n"
|
||||
"\tlet r: i32 = beta.foo(10, n);\n"
|
||||
"\tif (r != 17) { return 11; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n" },
|
||||
"main.ww" },
|
||||
|
||||
/* Control. A variadic fn exists in only one imported module;
|
||||
* the caller calls it via N_DOT. No same-leaf collision, so
|
||||
* fnparamslookupmod's same-module hint and the head-walk
|
||||
* fallback agree. Pre+post fix both stages rc=0. */
|
||||
{ "bare_leaf_no_collision", 0, 2,
|
||||
{ "alpha/alpha.ww",
|
||||
"package alpha;\n"
|
||||
"export fn sum(a: i32, xs: i32...) i32 = {\n"
|
||||
"\tlet t: i32 = a;\n"
|
||||
"\tlet i: i32 = 0;\n"
|
||||
"\tfor (i < xs.len) { t += xs[i]; i += 1; };\n"
|
||||
"\treturn t;\n"
|
||||
"};\n",
|
||||
"main.ww",
|
||||
"package main;\n"
|
||||
"import alpha;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet r: i32 = alpha.sum(1, 2, 3, 4);\n"
|
||||
"\tif (r != 10) { return 40; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n" },
|
||||
"main.ww" },
|
||||
};
|
||||
|
||||
static int
|
||||
write_one(const char *dir, const char *rel, const char *src,
|
||||
int *parent_owned)
|
||||
{
|
||||
char path[512];
|
||||
snprintf(path, sizeof path, "%s/%s", dir, rel);
|
||||
char *slash = strchr(path + strlen(dir) + 1, '/');
|
||||
if (slash) {
|
||||
*slash = '\0';
|
||||
if (mkdir(path, 0755) != 0) return -1;
|
||||
*parent_owned = 1;
|
||||
*slash = '/';
|
||||
}
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return -1;
|
||||
int rc = fputs(src, f) == EOF ? -1 : 0;
|
||||
if (fclose(f) != 0) rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
cleanup_row(const char *tmpdir, const struct row *r,
|
||||
const int *parent_owned)
|
||||
{
|
||||
char path[512], cmd[640], entry[512];
|
||||
int rc = 0;
|
||||
|
||||
for (int k = 0; k < r->n_files; k++) {
|
||||
snprintf(path, sizeof path, "%s/%s", tmpdir, r->files[2 * k]);
|
||||
char *slash = strchr(path + strlen(tmpdir) + 1, '/');
|
||||
if (slash && !parent_owned[k]) continue;
|
||||
if (unlink(path) != 0 && errno != ENOENT) rc = -1;
|
||||
}
|
||||
|
||||
snprintf(entry, sizeof entry, "%s/%s", tmpdir, r->entry);
|
||||
char *dot = strrchr(entry, '.');
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
if (unlink(entry) != 0 && errno != ENOENT) rc = -1;
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s.sepwork", entry);
|
||||
if (runwait(cmd) != 0) rc = -1;
|
||||
|
||||
for (int k = 0; k < r->n_files; k++) {
|
||||
snprintf(path, sizeof path, "%s/%s", tmpdir, r->files[2 * k]);
|
||||
char *slash = strchr(path + strlen(tmpdir) + 1, '/');
|
||||
if (slash && parent_owned[k]) {
|
||||
*slash = '\0';
|
||||
if (rmdir(path) != 0 && errno != ENOENT) rc = -1;
|
||||
}
|
||||
}
|
||||
if (rmdir(tmpdir) != 0) rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
run_row(const char *driver, const struct row *r, int idx, const char *tag)
|
||||
{
|
||||
(void)idx;
|
||||
char tmpdir[] = "/tmp/modparam_XXXXXX";
|
||||
if (mkdtemp(tmpdir) == NULL) {
|
||||
fprintf(stderr, "modparam_callee[%s][%s]: mkdtemp failed\n",
|
||||
tag, r->label);
|
||||
return 1;
|
||||
}
|
||||
int fail = 0;
|
||||
int parent_owned[4] = { 0 };
|
||||
|
||||
for (int k = 0; k < r->n_files; k++) {
|
||||
if (write_one(tmpdir, r->files[2 * k],
|
||||
r->files[2 * k + 1], &parent_owned[k]) != 0) {
|
||||
fprintf(stderr,
|
||||
"modparam_callee[%s][%s]: write %s failed\n",
|
||||
tag, r->label, r->files[2 * k]);
|
||||
fail++;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s build %s >/dev/null 2>&1", tmpdir, driver, r->entry);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr,
|
||||
"modparam_callee[%s][%s]: build failed\n",
|
||||
tag, r->label);
|
||||
fail++;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char entry_bin[512];
|
||||
snprintf(entry_bin, sizeof entry_bin, "%s/%s", tmpdir, r->entry);
|
||||
char *dot = strrchr(entry_bin, '.');
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
int got = runwait(entry_bin);
|
||||
if (got != r->want_exit) {
|
||||
fprintf(stderr,
|
||||
"modparam_callee[%s][%s]: rc=%d want=%d\n",
|
||||
tag, r->label, got, r->want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (cleanup_row(tmpdir, r, parent_owned) != 0) {
|
||||
fprintf(stderr, "modparam_callee[%s][%s]: cleanup failed\n",
|
||||
tag, r->label);
|
||||
if (fail == 0) fail++;
|
||||
}
|
||||
return fail;
|
||||
}
|
||||
|
||||
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], wdrv[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
fail += run_row(cdrv, &rows[i], i, "cs");
|
||||
if (have_ww) {
|
||||
total++;
|
||||
fail += run_row(wdrv, &rows[i], i, "ws");
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"modparam_callee: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("modparam_callee: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,393 +0,0 @@
|
||||
/*
|
||||
* 784_xmod_alias_struct_collide_run — project #223 runtime + byte-id
|
||||
* net. A struct-field access through a pointer-ALIAS receiver
|
||||
* (`s.<field>` where `s` is a `type s = *vtable` alias) miscompiled in
|
||||
* wwstage when the alias leaf collided with a DIFFERENT module's
|
||||
* same-leaf STRUCT (the eFinal io.stream = *vtable alias vs the
|
||||
* memio.stream struct). cgen's #191 alias-peel loop
|
||||
* (selfhost/cmd/wcc/cgenexpr.ww cgdot) broke on the name-keyed
|
||||
* structlookup's any-module fallback — it found the FOREIGN struct,
|
||||
* halted the peel with the receiver still N_TNAME, skipped the
|
||||
* pointer-to-struct field-load arm, and the field load fell through to
|
||||
* the SB-global fallback: `MOVQ <field>(SB), AX` (an undefined symbol →
|
||||
* w6l link-fail / garbage). cstage is correct — type_chase_named
|
||||
* (cmd/w6c/cgen.c) follows the resolved NAMED.under POINTER chain
|
||||
* (module-correct), never a name re-lookup. Sibling of #208 (lossy
|
||||
* name-keyed resolution leaking to a global leaf, but in the CHECKER).
|
||||
*
|
||||
* Fix (wwstage-only align-down, cgenexpr.ww:1769 peel loop): the
|
||||
* struct-break is now MODULE-AWARE — break ONLY on a struct that THIS
|
||||
* module defines (structsamemod), a same-module ALIAS keeps peeling
|
||||
* (aliassamemod), and a foreign leaf falls back to the prior
|
||||
* any-module heuristic.
|
||||
*
|
||||
* GATE-BLIND in the bootstrap: the io.stream-alias vs memio.stream-
|
||||
* struct collision is the only same-leaf alias-vs-struct cross-module
|
||||
* pair, and it only entered the corpus at the #94 eFinal FLIP (master
|
||||
* never had it). A single-module synthetic does NOT trigger — the
|
||||
* collision needs a SECOND module contributing the same-leaf struct
|
||||
* into the corpus. Hence this 2-module probe.
|
||||
*
|
||||
* row | shape | exit | byte-id
|
||||
* -----------+----------------------------------------+------+--------
|
||||
* collision | sa: type s=*vtable + vtable{reader} + | 42 | cs==ww
|
||||
* | dispatcher `read(x:s) match(x.reader)`| |
|
||||
* | sb: type s=struct{} (same leaf, NO | |
|
||||
* | reader field) — the #223 trigger | |
|
||||
* symmetric | sa: type s=struct{} + value-receiver | 21 | cs==ww
|
||||
* | `geta(x:s)`; sb: type s=*vtable alias | |
|
||||
* | — guards the same-module-struct break | |
|
||||
* | still fires (a naive alias-first | |
|
||||
* | reorder would mis-peel A's struct) | |
|
||||
* control | sa: alias+vtable+dispatcher, NO foreign | 30 | cs==ww
|
||||
* | same-leaf struct — common io-like case| |
|
||||
*
|
||||
* The dispatcher uses a BRANCHED callee (void arm → -1, fn-ptr arm →
|
||||
* v+100) so the field load actually feeds control flow (gate-blind
|
||||
* discipline). cstage `ww build` + run pins runtime; raw w6c vs w6c_ww
|
||||
* `.s` cmp on the self-built two-module unit pins rule-10 byte-id
|
||||
* (the discriminating net for #223 — pre-fix the dispatcher diverges
|
||||
* `MOVQ reader(SB)` vs the pointer-field load).
|
||||
*
|
||||
* Filed-not-fixed here: #224 (the broader cross-module same-leaf STRUCT
|
||||
* name-keying at structlookupchain) — distinct, not FLIP-triggered.
|
||||
*/
|
||||
#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;
|
||||
};
|
||||
|
||||
/* ---- collision: the #223 bug ------------------------------------- */
|
||||
static const struct file collision_files[] = {
|
||||
{ "sa.ww",
|
||||
"package sa;\n"
|
||||
"\n"
|
||||
"export type reader = fn(x: s, v: i32) i32;\n"
|
||||
"\n"
|
||||
"export type vtable = struct {\n"
|
||||
" reader: (*reader | void),\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"export type s = *vtable;\n"
|
||||
"\n"
|
||||
"export fn read(x: s, v: i32) i32 = {\n"
|
||||
" match (x.reader) {\n"
|
||||
" case void => return -1;\n"
|
||||
" case let f: *reader => return (*f)(x, v);\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"export fn mkvt(f: *reader) vtable = {\n"
|
||||
" return vtable { reader = f };\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"export fn mkempty() vtable = {\n"
|
||||
" return vtable { reader = void };\n"
|
||||
"};\n" },
|
||||
/* sb's `s` is a STRUCT sharing the leaf `s` with sa's alias — the
|
||||
* name-keyed structlookup any-module fallback used to halt sa.read's
|
||||
* alias-peel here. No `reader` field, so the mis-resolved field walk
|
||||
* fell through to `MOVQ reader(SB)`. */
|
||||
{ "sb.ww",
|
||||
"package sb;\n"
|
||||
"\n"
|
||||
"export type s = struct {\n"
|
||||
" a: i32,\n"
|
||||
" b: i32,\n"
|
||||
"};\n" },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"\n"
|
||||
"import sa;\n"
|
||||
"import sb;\n"
|
||||
"\n"
|
||||
"fn cb(x: sa.s, v: i32) i32 = { return v + 100; };\n"
|
||||
"\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let vt = sa.mkvt((&cb): *sa.reader);\n"
|
||||
" let st: sa.s = &vt;\n"
|
||||
" let r = sa.read(st, 5);\n"
|
||||
" let vt2 = sa.mkempty();\n"
|
||||
" let st2: sa.s = &vt2;\n"
|
||||
" let r2 = sa.read(st2, 5);\n"
|
||||
" if (r != 105) { return 11; };\n"
|
||||
" if (r2 != -1) { return 12; };\n"
|
||||
" return 42;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- symmetric: A struct, B alias; receiver is A's struct value -- */
|
||||
static const struct file symmetric_files[] = {
|
||||
{ "sa.ww",
|
||||
"package sa;\n"
|
||||
"\n"
|
||||
"export type s = struct {\n"
|
||||
" a: i32,\n"
|
||||
" b: i32,\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"export fn mk(av: i32, bv: i32) s = {\n"
|
||||
" return s { a = av, b = bv };\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"export fn geta(x: s) i32 = {\n"
|
||||
" return x.a;\n"
|
||||
"};\n" },
|
||||
{ "sb.ww",
|
||||
"package sb;\n"
|
||||
"\n"
|
||||
"export type vtable = struct {\n"
|
||||
" q: i32,\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"export type s = *vtable;\n" },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"\n"
|
||||
"import sa;\n"
|
||||
"import sb;\n"
|
||||
"\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let o = sa.mk(21, 8);\n"
|
||||
" let r = sa.geta(o);\n"
|
||||
" if (r != 21) { return 11; };\n"
|
||||
" return 21;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- control: same-module alias+vtable, no foreign same-leaf ----- */
|
||||
static const struct file control_files[] = {
|
||||
{ "sa.ww",
|
||||
"package sa;\n"
|
||||
"\n"
|
||||
"export type reader = fn(x: s, v: i32) i32;\n"
|
||||
"\n"
|
||||
"export type vtable = struct {\n"
|
||||
" reader: (*reader | void),\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"export type s = *vtable;\n"
|
||||
"\n"
|
||||
"export fn read(x: s, v: i32) i32 = {\n"
|
||||
" match (x.reader) {\n"
|
||||
" case void => return -1;\n"
|
||||
" case let f: *reader => return (*f)(x, v);\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"export fn mkvt(f: *reader) vtable = {\n"
|
||||
" return vtable { reader = f };\n"
|
||||
"};\n" },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"\n"
|
||||
"import sa;\n"
|
||||
"\n"
|
||||
"fn cb(x: sa.s, v: i32) i32 = { return v + 100; };\n"
|
||||
"\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let vt = sa.mkvt((&cb): *sa.reader);\n"
|
||||
" let st: sa.s = &vt;\n"
|
||||
" let r = sa.read(st, 5);\n"
|
||||
" if (r != 105) { return 11; };\n"
|
||||
" return 30;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct scenario scenarios[] = {
|
||||
{ "collision", collision_files, 42 },
|
||||
{ "symmetric", symmetric_files, 21 },
|
||||
{ "control", control_files, 30 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_scenario(const char *bin, const char *cdrv, const char *wdrv,
|
||||
const struct scenario *sc)
|
||||
{
|
||||
(void)bin;
|
||||
/* Private fixture dir — #215: srcd is this dir (not shared /tmp),
|
||||
* so the driver's srcd-first import search can't pick up a
|
||||
* polluting same-name file. mkdtemp gives a unique path. */
|
||||
char dir[] = "/tmp/ww784_XXXXXX";
|
||||
if (mkdtemp(dir) == NULL) {
|
||||
fprintf(stderr, "784[%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, "784[%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, "784[%s]: write %s\n", sc->label,
|
||||
sc->files[i].name); rc = -1; goto done; }
|
||||
}
|
||||
|
||||
/* #94 sep layout: cstage build + run pins runtime; 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, "784[%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, "784[%s]: cstage exit %d, want %d\n",
|
||||
sc->label, got, sc->want_exit);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
/* Byte-id net (the #223 discriminator): the wwstage driver's
|
||||
* build emits per-package w6c_ww asm; pre-fix the dispatcher diverges.
|
||||
* The root + each imported pkg compile to separate <stem>.sepwork/<pkg>.s;
|
||||
* concat (sorted glob, identical set both stages) for the compare. */
|
||||
char cs_s[1024], ws_s[1024];
|
||||
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, "784[%s]: ww_ww build failed\n", sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
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, "784[%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, "784[%s]: wwstage asm aggregation failed\n",
|
||||
sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr, "784[%s]: cs.s/ww.s DIFFER (rule-10 byte-id "
|
||||
"violation — #223 regression)\n", sc->label);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
done:
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "784[%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, "784[%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, "784[%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, "784[%s]: cleanup %s\n", sc->label,
|
||||
children[i]);
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rmdir(dir) != 0) {
|
||||
fprintf(stderr, "784[%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, "784: 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, "784 xmod_alias_struct_collide: %d/%d "
|
||||
"scenarios failed\n", fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("xmod_alias_struct_collide: %d/%d ok (cstage run + cs==ww "
|
||||
"byte-id)\n", n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
* 793_xmod_struct_argpush_collide_run — project #21/#224 runtime + byte-id
|
||||
* net. An inferred-let struct LOCAL whose type leaf collides with a
|
||||
* DIFFERENT module's same-leaf struct miscompiled in wwstage at THREE
|
||||
* caller-side register-ABI sites, all keyed off the name-resolved
|
||||
* (collision-prone) struct instead of the checker-STAMPED tinfo:
|
||||
*
|
||||
* - push (cgenutil.ww pushargsrev): the by-value struct call-arg
|
||||
* COUNT came from name-keyed structparamsize(lc.tnode). On the
|
||||
* collision lc.tnode is a bare leaf structlookup mis-resolves to the
|
||||
* foreign same-leaf struct (>16B → 0), so the fast path was skipped
|
||||
* and the arg dropped to a scalar single-PUSHQ — the 2nd eightbyte
|
||||
* (the sub-8 tail) was lost. [the original #21 site]
|
||||
* - recv (cgenstmt.ww cgletbody): `let s = m1.mk()` sized the
|
||||
* register receive via structlookup(tn.str) → the foreign struct's
|
||||
* word count, OVER-copying a 24B foreign over a 16B local (spilling
|
||||
* CX into a neighbour slot).
|
||||
* - field (cgenexpr.ww cgdot direct-struct arm): `s.<field>` resolved
|
||||
* the offset via structlookupchain(tn) → the foreign struct's layout,
|
||||
* reading the field at the WRONG offset + wrong load-op. [#224, filed
|
||||
* not-fixed by 784/#223]
|
||||
*
|
||||
* cstage is correct at all three — it keys on args[i]->type / lu->size /
|
||||
* t->fields (the checker-STAMPED type), never a name re-lookup. Fix
|
||||
* (wwstage-only align-UP, #21/#224, ONE commit): route all three off the
|
||||
* stamped tinfo — structabisizetn(arg/tn.type_) for the push+recv COUNT,
|
||||
* tichase(dotlhs.type_).fields (tfield) for the field offset+type.
|
||||
*
|
||||
* GATE-BLIND in the bootstrap (same class as 784/#223): the trigger needs
|
||||
* a SECOND module contributing a same-leaf struct of a DIFFERENT size, so
|
||||
* the name-keyed lookup mis-resolves. A single-module synthetic resolves
|
||||
* correctly (structlookup's same-module pass) and stays byte-id — it does
|
||||
* NOT redden. Hence this 2-module probe.
|
||||
*
|
||||
* row | m1.pair (local) | m2.pair (foreign) | site(s) | exit
|
||||
* -----------------+----------------------+-------------------+---------------+-----
|
||||
* recvpush_t16 | {u64,u16} 16B | 24B | recv + push | 107
|
||||
* fieldread_t16 | {u64,u16} 16B | 24B | recv + field | 107
|
||||
* combined_t16 | {u64,u16} 16B | 24B | recv+field+push| 114
|
||||
* recvpush_t12 | {u32,u32,u32} 12B | 24B | recv + push | 6
|
||||
*
|
||||
* cstage `ww build` + run pins runtime; the wwstage binary is run
|
||||
* too (the bug WAS a wrong wwstage runtime value); raw cs.s vs ww.s cmp
|
||||
* over the driver-produced per-package asm pins rule-10 byte-id (the
|
||||
* discriminating net — pre-fix __root.s diverges at the push/recv/field).
|
||||
*/
|
||||
#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;
|
||||
};
|
||||
|
||||
/* m2.pair is a 24B struct sharing the leaf `pair` with m1.pair — the
|
||||
* name-keyed lookup's any-module fallback mis-resolves m1.pair to it. */
|
||||
static const char m2_24[] =
|
||||
"package m2;\n"
|
||||
"export type pair = struct { hi: u64, mid: u64, lo: u64 };\n"
|
||||
"export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };\n";
|
||||
|
||||
/* ---- recvpush_t16: inferred-let recv + by-value push (the #21 site) -- */
|
||||
static const struct file recvpush_t16_files[] = {
|
||||
{ "m1.ww",
|
||||
"package m1;\n"
|
||||
"export type pair = struct { hi: u64, lo: u16 };\n"
|
||||
"export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };\n"
|
||||
"export fn consume(p: pair) i32 = { return (p.hi + (p.lo: u64)): i32; };\n" },
|
||||
{ "m2.ww", m2_24 },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import m1;\n"
|
||||
"import m2;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let dummy: m2.pair;\n"
|
||||
" dummy.hi = 0: u64;\n"
|
||||
" let s = m1.mk();\n"
|
||||
" return m1.consume(s);\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- fieldread_t16: inferred-let recv + direct field read (#224) ----- */
|
||||
static const struct file fieldread_t16_files[] = {
|
||||
{ "m1.ww",
|
||||
"package m1;\n"
|
||||
"export type pair = struct { hi: u64, lo: u16 };\n"
|
||||
"export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };\n" },
|
||||
{ "m2.ww", m2_24 },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import m1;\n"
|
||||
"import m2;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let dummy: m2.pair;\n"
|
||||
" dummy.hi = 0: u64;\n"
|
||||
" let s = m1.mk();\n"
|
||||
" return (s.hi + (s.lo: u64)): i32;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- combined_t16: recv + field + push in one fn -------------------- */
|
||||
static const struct file combined_t16_files[] = {
|
||||
{ "m1.ww",
|
||||
"package m1;\n"
|
||||
"export type pair = struct { hi: u64, lo: u16 };\n"
|
||||
"export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };\n"
|
||||
"export fn consume(p: pair) i32 = { return (p.hi + (p.lo: u64)): i32; };\n" },
|
||||
{ "m2.ww", m2_24 },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import m1;\n"
|
||||
"import m2;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let dummy: m2.pair;\n"
|
||||
" dummy.hi = 0: u64;\n"
|
||||
" let s = m1.mk();\n"
|
||||
" let x: i32 = (s.lo: i32);\n"
|
||||
" return m1.consume(s) + x;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- recvpush_t12: a maxalign-4 12B sub-8-tail tail shape ----------- */
|
||||
static const struct file recvpush_t12_files[] = {
|
||||
{ "m1.ww",
|
||||
"package m1;\n"
|
||||
"export type pair = struct { a: u32, b: u32, c: u32 };\n"
|
||||
"export fn mk() pair = { return pair { a = 1: u32, b = 2: u32, c = 3: u32 }; };\n"
|
||||
"export fn consume(p: pair) i32 = { return (p.a + p.b + p.c): i32; };\n" },
|
||||
{ "m2.ww", m2_24 },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import m1;\n"
|
||||
"import m2;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let dummy: m2.pair;\n"
|
||||
" dummy.hi = 0: u64;\n"
|
||||
" let s = m1.mk();\n"
|
||||
" return m1.consume(s);\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct scenario scenarios[] = {
|
||||
{ "recvpush_t16", recvpush_t16_files, 107 },
|
||||
{ "fieldread_t16", fieldread_t16_files, 107 },
|
||||
{ "combined_t16", combined_t16_files, 114 },
|
||||
{ "recvpush_t12", recvpush_t12_files, 6 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc)
|
||||
{
|
||||
/* Private fixture dir — the driver's srcd-first import search can't
|
||||
* pick up a polluting same-name file (mirrors 784/#215). */
|
||||
char dir[] = "/tmp/ww793_XXXXXX";
|
||||
if (mkdtemp(dir) == NULL) {
|
||||
fprintf(stderr, "793[%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, "793[%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, "793[%s]: write %s\n", sc->label,
|
||||
sc->files[i].name); rc = -1; goto done; }
|
||||
}
|
||||
|
||||
/* cstage build + run pins runtime; all outputs remain 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, "793[%s]: cstage build failed\n", sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
snprintf(path, sizeof path, "%s/main", dir);
|
||||
int gotc = runwait(path);
|
||||
if (gotc != sc->want_exit) {
|
||||
fprintf(stderr, "793[%s]: cstage exit %d, want %d\n",
|
||||
sc->label, gotc, sc->want_exit);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
/* wwstage build + run: the bug WAS a wrong wwstage runtime
|
||||
* value (a dropped/over-copied field), so run the wwstage binary too. */
|
||||
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, "793[%s]: ww_ww build failed\n", sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
snprintf(path, sizeof path, "%s/mainww", dir);
|
||||
int gotw = runwait(path);
|
||||
if (gotw != sc->want_exit) {
|
||||
fprintf(stderr, "793[%s]: wwstage exit %d, want %d\n",
|
||||
sc->label, gotw, sc->want_exit);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
/* Byte-id net (the discriminator): per-package asm under
|
||||
* <stem>.sepwork/<pkg>.s; concat (sorted glob, identical set both
|
||||
* stages) for the compare. Pre-fix __root.s diverges. */
|
||||
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, "793[%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, "793[%s]: wwstage asm aggregation failed\n",
|
||||
sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr, "793[%s]: cs.s/ww.s DIFFER (rule-10 byte-id "
|
||||
"violation — #21/#224 regression)\n", sc->label);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
done:
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "793[%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, "793[%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, "793[%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, "793[%s]: cleanup %s\n", sc->label,
|
||||
children[i]);
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rmdir(dir) != 0) {
|
||||
fprintf(stderr, "793[%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, "793: 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(cdrv, wdrv, &scenarios[i]) != 0)
|
||||
fail++;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "793 xmod_struct_argpush_collide: %d/%d "
|
||||
"scenarios failed\n", fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("xmod_struct_argpush_collide: %d/%d ok (cstage+wwstage run + "
|
||||
"cs==ww byte-id)\n", n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,452 +0,0 @@
|
||||
/*
|
||||
* 797_xmod_struct_field_layout_collide_run — project #31 (RULING R2,
|
||||
* commit-1: the OFFSET/SIZE align-up arms) runtime + byte-id net. The
|
||||
* direct continuation of 793 (#21): #21 type-keyed the THREE caller-side
|
||||
* sites the 793 collision drove (push/recv/field-read); #31 closes the
|
||||
* field-LAYOUT *receiver* arms #21 left name-keyed. Commit-1 covers the
|
||||
* offset/size half:
|
||||
*
|
||||
* - R1 (cgenexpr cgdot, local *struct READ): `p.f` where p is an
|
||||
* inferred-let *struct resolved the field OFFSET via
|
||||
* structlookupchain(inner) → the FOREIGN same-leaf struct's layout
|
||||
* (wrong offset + wrong load-op). Now tichase(dotlhs.type_)->.sub
|
||||
* ->fields (the #21 R0 tfield template), via the shared read
|
||||
* choke-point cgptrfieldloadtf.
|
||||
* - C1 (cgenstmt cgletbody, let-COPY size): `let p2 = p1` sized the
|
||||
* struct memcpy run via structlookup(tn.str) → the foreign 24B
|
||||
* struct, OVER-reading a 16B source + OVER-writing p2's slot (a
|
||||
* SILENT OOB whose *value* coincidentally round-trips — the asm
|
||||
* diverges, cs!=ww). Now structabisizetn(rhs.type_) (the COPY
|
||||
* source's stamped tinfo).
|
||||
* - A1/A2 (cgenexpr cgun, addr-of `&p.f` / `&o.f`): the LEAQ field
|
||||
* offset came from structlookupchain → wrong offset. Now
|
||||
* tichase(opnd.lhs.type_) (->.sub for the *struct A1 arm).
|
||||
*
|
||||
* cstage is correct at all of them — it keys on type_chase_named(base->
|
||||
* type)->fields / lu->size (the checker-STAMPED type), never a name
|
||||
* re-lookup. #31 aligns wwstage UP (ww-only change; cstage cgen.c is the
|
||||
* untouched oracle).
|
||||
*
|
||||
* GATE-BLIND in the bootstrap (same class as 793/784): the trigger needs
|
||||
* a SECOND module contributing a same-leaf struct of a DIFFERENT size so
|
||||
* the name-keyed lookup mis-resolves. The reddening surface is INFERRED
|
||||
* LOCALS (a global struct decl is always explicitly qualified, so its
|
||||
* qualifier resolves correctly — non-reddenable; the global value/ptr
|
||||
* arms R2/R3/A3/W3/W4a are converted-for-construction and ride the
|
||||
* corpus byte-id, not pinned here).
|
||||
*
|
||||
* row | m1 (local) | m2 (foreign) | arm | exit
|
||||
* ----------------+-------------------+--------------+-----+-----
|
||||
* ptrread_t16 | pair{u64,u16} 16B | pair 24B | R1 | 107
|
||||
* letcopy_t16 | pair{u64,u16} 16B | pair 24B | C1 | 107 (value coincides; byte-id is the net)
|
||||
* addrptr_pq | pq{u64,u64} 16B | pq 24B | A1 | 10
|
||||
* addrval_pq | pq{u64,u64} 16B | pq 24B | A2 | 10
|
||||
* ptrwrite_t16 | pair{u64,u16} 16B | pair 24B | W1 | 109 (commit-2 STORE)
|
||||
* valwrite_t16 | pair{u64,u16} 16B | pair 24B | W2 | 109 (commit-2 STORE)
|
||||
* nestfill_box | box{tag,ir:nst} 32B | box 24B | W2 | 66 (commit-2 cgstructlitfilltn nested recursion)
|
||||
*
|
||||
* Commit-2 (RULING R2 / Opt-2) converts the W1/W2/W5/W4b store-loop arms
|
||||
* off the name-keyed receiver walk to a tinfo-native fill (cgstructlitfilltn
|
||||
* + sretretsizetn), closing the in-loop #32 nested struct-receive/structlit/
|
||||
* ident sub-arms by construction. The two scalar WRITE rows STORE through the
|
||||
* mis-resolved offset: at commit-1 (R1 read already fixed) wwstage stored at
|
||||
* the FOREIGN offset 16 while reading off 8 -> 100+7=107 WRONG vs cstage 109.
|
||||
* They reach only the scalar storeopsz arm, NOT cgstructlitfilltn; the
|
||||
* nestfill_box row is the cgstructlitfilltn fill + nested-recursion value pin
|
||||
* (a struct-literal stored into a struct-typed field, whose own field is a
|
||||
* nested struct literal) — the riskiest new helper, which no bootstrap
|
||||
* construct exercises.
|
||||
*
|
||||
* Each row reddens under an INDEPENDENT revert of its arm (verified
|
||||
* impl-side: R1 ww=101, C1 cs!=ww OOB, A1/A2 ww=8, W1/W2 ww=107 cs!=ww).
|
||||
* cstage `ww build` + run pins runtime; the wwstage binary is run too
|
||||
* (the bug WAS a wrong wwstage runtime value / OOB); raw cs.s vs ww.s over
|
||||
* the driver-produced per-package asm pins rule-10 byte-id.
|
||||
*
|
||||
* NOTE the addr rows use an all-u64 `pq` (offset-only collision): the
|
||||
* canonical {u64,u16} field would, after `&p.lo`, force a `*q = v:u16`
|
||||
* narrow deref-STORE which wwstage currently emits as MOVQ where cstage
|
||||
* emits MOVW — a SEPARATE pre-existing wwstage narrow-deref-store-width
|
||||
* divergence (NOT #31; surfaced + filed by impl-31). An all-u64 store is
|
||||
* MOVQ in both stages, so the row isolates the #31 offset fix.
|
||||
*/
|
||||
#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;
|
||||
};
|
||||
|
||||
/* m2 contributes 24B same-leaf `pair` and `pq` — the name-keyed lookup's
|
||||
* any-module fallback mis-resolves m1's 16B versions to these. */
|
||||
static const char m2_src[] =
|
||||
"package m2;\n"
|
||||
"export type pair = struct { hi: u64, mid: u64, lo: u64 };\n"
|
||||
"export type pq = struct { a: u64, b: u64, v: u64 };\n"
|
||||
"export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };\n";
|
||||
|
||||
/* m1: 16B `pair` (sub-8 tail) + 16B all-u64 `pq` (offset-only). */
|
||||
static const char m1_src[] =
|
||||
"package m1;\n"
|
||||
"export type pair = struct { hi: u64, lo: u16 };\n"
|
||||
"export type pq = struct { a: u64, v: u64 };\n"
|
||||
"let g: pair = pair { hi = 100: u64, lo = 7: u16 };\n"
|
||||
"let gq: pq = pq { a = 1: u64, v = 7: u64 };\n"
|
||||
"export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };\n"
|
||||
"export fn mkp() *pair = { return &g; };\n"
|
||||
"export fn mkq() pq = { return pq { a = 1: u64, v = 7: u64 }; };\n"
|
||||
"export fn mkpq() *pq = { return &gq; };\n";
|
||||
|
||||
/* ---- ptrread_t16 (R1): inferred-let *struct field read ------------- */
|
||||
static const struct file ptrread_files[] = {
|
||||
{ "m1.ww", m1_src },
|
||||
{ "m2.ww", m2_src },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import m1;\n"
|
||||
"import m2;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let dummy: m2.pair;\n"
|
||||
" dummy.hi = 0: u64;\n"
|
||||
" let p = m1.mkp();\n"
|
||||
" return (p.hi + (p.lo: u64)): i32;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- letcopy_t16 (C1): inferred-let local-to-local struct copy ----- */
|
||||
static const struct file letcopy_files[] = {
|
||||
{ "m1.ww", m1_src },
|
||||
{ "m2.ww", m2_src },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import m1;\n"
|
||||
"import m2;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let dummy: m2.pair;\n"
|
||||
" dummy.hi = 0: u64;\n"
|
||||
" let p1 = m1.mk();\n"
|
||||
" let p2 = p1;\n"
|
||||
" return (p2.hi + (p2.lo: u64)): i32;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- addrptr_pq (A1): &p.f over an inferred-let *struct ------------ */
|
||||
static const struct file addrptr_files[] = {
|
||||
{ "m1.ww", m1_src },
|
||||
{ "m2.ww", m2_src },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import m1;\n"
|
||||
"import m2;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let dummy: m2.pq;\n"
|
||||
" dummy.a = 0: u64;\n"
|
||||
" let p = m1.mkpq();\n"
|
||||
" let q = &p.v;\n"
|
||||
" *q = 9: u64;\n"
|
||||
" return (p.a + p.v): i32;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- addrval_pq (A2): &o.f over an inferred-let value struct ------- */
|
||||
static const struct file addrval_files[] = {
|
||||
{ "m1.ww", m1_src },
|
||||
{ "m2.ww", m2_src },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import m1;\n"
|
||||
"import m2;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let dummy: m2.pq;\n"
|
||||
" dummy.a = 0: u64;\n"
|
||||
" let s = m1.mkq();\n"
|
||||
" let q = &s.v;\n"
|
||||
" *q = 9: u64;\n"
|
||||
" return (s.a + s.v): i32;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- ptrwrite_t16 (W1, commit-2): inferred-let *struct field STORE.
|
||||
* `p.lo = 9: u16` resolved the field OFFSET via the name-keyed lookup ->
|
||||
* the FOREIGN 24B pair (lo @ off 16, u64) -> the store landed at off 16
|
||||
* (a u64 MOVQ into g's DATA neighbour) while the read (R1, fixed in
|
||||
* commit-1) reads off 8 -> p.lo stays 7 -> 100+7=107 WRONG (cstage 109,
|
||||
* cs!=ww). The {u64,u16} field stores MOVW in both stages (a direct
|
||||
* field store, NOT the addr-of `*q=v:u16` narrow-deref-store divergence
|
||||
* the A1/A2 pq rows dodge), so it isolates the #31 W1 offset/width fix. */
|
||||
static const struct file ptrwrite_files[] = {
|
||||
{ "m1.ww", m1_src },
|
||||
{ "m2.ww", m2_src },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import m1;\n"
|
||||
"import m2;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let dummy: m2.pair;\n"
|
||||
" dummy.hi = 0: u64;\n"
|
||||
" let p = m1.mkp();\n"
|
||||
" p.lo = 9: u16;\n"
|
||||
" return (p.hi + (p.lo: u64)): i32;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- valwrite_t16 (W2, commit-2): inferred-let value-struct field STORE.
|
||||
* `s.lo = 9: u16` resolved the OFFSET via the FOREIGN 24B pair -> stored
|
||||
* 8 bytes at off 16 (past s's 16B slot) while s.lo (off 8) stays 7 ->
|
||||
* 100+7=107 WRONG (cstage 109). The write-twin #21 missed. */
|
||||
static const struct file valwrite_files[] = {
|
||||
{ "m1.ww", m1_src },
|
||||
{ "m2.ww", m2_src },
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"import m1;\n"
|
||||
"import m2;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let dummy: m2.pair;\n"
|
||||
" dummy.hi = 0: u64;\n"
|
||||
" let s = m1.mk();\n"
|
||||
" s.lo = 9: u16;\n"
|
||||
" return (s.hi + (s.lo: u64)): i32;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* ---- nestfill_box (W2 -> cgstructlitfilltn, commit-2): the nested-
|
||||
* recursion VALUE pin for the new fill helper. `s.ir = nst{...}` stores a
|
||||
* struct-LITERAL into a struct-typed field of a value-struct local,
|
||||
* driving cgstructlitfilltn (the W2 N_STRUCTLIT sub-arm, mode 0); the
|
||||
* literal's `dp = deep{...}` field is itself a struct literal, so
|
||||
* cgstructlitfilltn RECURSES — cgstructlitfilltn(tichase(tf.type_)) at
|
||||
* the inner offset (box.ir.dp @ +16). This is the riskiest new code and
|
||||
* is exercised by NO bootstrap construct (the corpus has zero `x.f =
|
||||
* Y{...}` struct-lit field stores) and by NO other 797 row (they all
|
||||
* store scalars/idents/calls, hitting only the storeopsz scalar arm).
|
||||
* Want = i0 + dp.d0 + dp.d1 = 11 + 22 + 33 = 66; a broken recursion
|
||||
* drops dp -> 11, a mis-offset -> a wrong sum.
|
||||
*
|
||||
* COLLISION-FREE by necessity (NOT an oversight): a cross-module
|
||||
* same-leaf box collision CANNOT drive this pin because TWO pre-existing
|
||||
* wwstage CHECKER gaps (orthogonal to #31, which is cgen-only) block the
|
||||
* wwstage readback under collision — (a) the checker mis-resolves a
|
||||
* nested value-struct field read to the FOREIGN field type (`u64 -> nst
|
||||
* not assignable`); (b) a chained `s.ir.dp.d0` read trips `asserttyped:
|
||||
* dot`. So the value is read back via single-dot-through-param helpers
|
||||
* (sumnst/sumdeep), the only nested-read shape wwstage accepts. The pin
|
||||
* still catches every cgstructlitfilltn fill/recursion miscompile (cs ==
|
||||
* ww runtime + cs.s == ww.s byte-id); the collision surface for the
|
||||
* SCALAR store stays covered by ptrwrite/valwrite above. */
|
||||
static const struct file nestfill_files[] = {
|
||||
{ "main.ww",
|
||||
"package main;\n"
|
||||
"type deep = struct { d0: u64, d1: u64 };\n"
|
||||
"type nst = struct { i0: u64, dp: deep };\n"
|
||||
"type box = struct { tag: u64, ir: nst };\n"
|
||||
"fn mkbox() box = { return box { tag = 0: u64, ir = nst { i0 = 0: u64, dp = deep { d0 = 0: u64, d1 = 0: u64 } } }; };\n"
|
||||
"fn sumdeep(d: deep) u64 = { return d.d0 + d.d1; };\n"
|
||||
"fn sumnst(n: nst) u64 = { return n.i0 + sumdeep(n.dp); };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let s = mkbox();\n"
|
||||
" s.ir = nst { i0 = 11: u64, dp = deep { d0 = 22: u64, d1 = 33: u64 } };\n"
|
||||
" return sumnst(s.ir): i32;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct scenario scenarios[] = {
|
||||
{ "ptrread_t16", ptrread_files, 107 },
|
||||
{ "letcopy_t16", letcopy_files, 107 },
|
||||
{ "addrptr_pq", addrptr_files, 10 },
|
||||
{ "addrval_pq", addrval_files, 10 },
|
||||
{ "ptrwrite_t16", ptrwrite_files, 109 },
|
||||
{ "valwrite_t16", valwrite_files, 109 },
|
||||
{ "nestfill_box", nestfill_files, 66 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_scenario(const char *cdrv, const char *wdrv, const struct scenario *sc)
|
||||
{
|
||||
char dir[] = "/tmp/ww797_XXXXXX";
|
||||
if (mkdtemp(dir) == NULL) {
|
||||
fprintf(stderr, "797[%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, "797[%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, "797[%s]: write %s\n", sc->label,
|
||||
sc->files[i].name); rc = -1; goto done; }
|
||||
}
|
||||
|
||||
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, "797[%s]: cstage build failed\n", sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
snprintf(path, sizeof path, "%s/main", dir);
|
||||
int gotc = runwait(path);
|
||||
if (gotc != sc->want_exit) {
|
||||
fprintf(stderr, "797[%s]: cstage exit %d, want %d\n",
|
||||
sc->label, gotc, sc->want_exit);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
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, "797[%s]: ww_ww build failed\n", sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
snprintf(path, sizeof path, "%s/mainww", dir);
|
||||
int gotw = runwait(path);
|
||||
if (gotw != sc->want_exit) {
|
||||
fprintf(stderr, "797[%s]: wwstage exit %d, want %d\n",
|
||||
sc->label, gotw, sc->want_exit);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
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, "797[%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, "797[%s]: wwstage asm aggregation failed\n",
|
||||
sc->label);
|
||||
rc = -1; goto done;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr, "797[%s]: cs.s/ww.s DIFFER (rule-10 byte-id "
|
||||
"violation — #31 regression)\n", sc->label);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
done:
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s/main.sepwork", dir);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "797[%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, "797[%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, "797[%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, "797[%s]: cleanup %s\n", sc->label,
|
||||
children[i]);
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rmdir(dir) != 0) {
|
||||
fprintf(stderr, "797[%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, "797: 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(cdrv, wdrv, &scenarios[i]) != 0)
|
||||
fail++;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "797 xmod_struct_field_layout_collide: %d/%d "
|
||||
"scenarios failed\n", fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("xmod_struct_field_layout_collide: %d/%d ok (cstage+wwstage run + "
|
||||
"cs==ww byte-id)\n", n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// sole variadic callee for the r752 no-collision control.
|
||||
package alpha;
|
||||
export fn sum(a: i32, xs: i32...) i32 = {
|
||||
let t: i32 = a;
|
||||
let i: i32 = 0;
|
||||
for (i < xs.len) { t += xs[i]; i += 1; };
|
||||
return t;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
//ww:run
|
||||
// migrated from test/wcc/752_modparam_callee.c: control — variadic callee in one module only; the N_IDENT arm stays on bare fnparamslookup.
|
||||
package main;
|
||||
import alpha;
|
||||
export fn main() i32 = {
|
||||
let r: i32 = alpha.sum(1, 2, 3, 4);
|
||||
if (r != 10) { return 40; };
|
||||
return 0;
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
// same-leaf variadic side of the r752 collision pair.
|
||||
package alpha;
|
||||
export fn foo(a: i32, xs: i32...) i32 = {
|
||||
return a + xs.len;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
// same-leaf scalar-tagged side of the r752 collision pair.
|
||||
package beta;
|
||||
export fn foo(a: i32, n: (u8 | []u8)) i32 = {
|
||||
match (n) {
|
||||
case let c: u8 => return a + (c: i32);
|
||||
case let s: []u8 => return a + s.len;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
11
test/wcc/data/r752_modparam_xmod_variadic_vs_scalar/case.ww
Normal file
11
test/wcc/data/r752_modparam_xmod_variadic_vs_scalar/case.ww
Normal file
@@ -0,0 +1,11 @@
|
||||
//ww:run
|
||||
// migrated from test/wcc/752_modparam_callee.c: N_DOT callee param lookup must be module-keyed — alpha.foo variadic vs beta.foo scalar same leaf (#16).
|
||||
package main;
|
||||
import alpha;
|
||||
import beta;
|
||||
export fn main() i32 = {
|
||||
let n: (u8 | []u8) = 7u8;
|
||||
let r: i32 = beta.foo(10, n);
|
||||
if (r != 17) { return 11; };
|
||||
return 0;
|
||||
};
|
||||
12
test/wcc/data/r784_xmod_alias_control/case.ww
Normal file
12
test/wcc/data/r784_xmod_alias_control/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:run-exit 30
|
||||
// migrated from test/wcc/784_xmod_alias_struct_collide_run.c: control — alias+vtable dispatcher with NO foreign same-leaf struct in the closure.
|
||||
package main;
|
||||
import sa;
|
||||
fn cb(x: sa.s, v: i32) i32 = { return v + 100; };
|
||||
export fn main() i32 = {
|
||||
let vt = sa.mkvt((&cb): *sa.reader);
|
||||
let st: sa.s = &vt;
|
||||
let r = sa.read(st, 5);
|
||||
if (r != 105) { return 11; };
|
||||
return 30;
|
||||
};
|
||||
21
test/wcc/data/r784_xmod_alias_control/sa/sa.ww
Normal file
21
test/wcc/data/r784_xmod_alias_control/sa/sa.ww
Normal file
@@ -0,0 +1,21 @@
|
||||
// alias+vtable dispatcher, collision-free control for r784.
|
||||
package sa;
|
||||
|
||||
export type reader = fn(x: s, v: i32) i32;
|
||||
|
||||
export type vtable = struct {
|
||||
reader: (*reader | void),
|
||||
};
|
||||
|
||||
export type s = *vtable;
|
||||
|
||||
export fn read(x: s, v: i32) i32 = {
|
||||
match (x.reader) {
|
||||
case void => return -1;
|
||||
case let f: *reader => return (*f)(x, v);
|
||||
};
|
||||
};
|
||||
|
||||
export fn mkvt(f: *reader) vtable = {
|
||||
return vtable { reader = f };
|
||||
};
|
||||
17
test/wcc/data/r784_xmod_alias_struct_collide/case.ww
Normal file
17
test/wcc/data/r784_xmod_alias_struct_collide/case.ww
Normal file
@@ -0,0 +1,17 @@
|
||||
//ww:run-exit 42
|
||||
// migrated from test/wcc/784_xmod_alias_struct_collide_run.c: #223 trigger — sa's *vtable alias peel must not halt on sb's same-leaf foreign struct.
|
||||
package main;
|
||||
import sa;
|
||||
import sb;
|
||||
fn cb(x: sa.s, v: i32) i32 = { return v + 100; };
|
||||
export fn main() i32 = {
|
||||
let vt = sa.mkvt((&cb): *sa.reader);
|
||||
let st: sa.s = &vt;
|
||||
let r = sa.read(st, 5);
|
||||
let vt2 = sa.mkempty();
|
||||
let st2: sa.s = &vt2;
|
||||
let r2 = sa.read(st2, 5);
|
||||
if (r != 105) { return 11; };
|
||||
if (r2 != -1) { return 12; };
|
||||
return 42;
|
||||
};
|
||||
25
test/wcc/data/r784_xmod_alias_struct_collide/sa/sa.ww
Normal file
25
test/wcc/data/r784_xmod_alias_struct_collide/sa/sa.ww
Normal file
@@ -0,0 +1,25 @@
|
||||
// alias-side of the r784 #223 pair: s = *vtable with a branched dispatcher.
|
||||
package sa;
|
||||
|
||||
export type reader = fn(x: s, v: i32) i32;
|
||||
|
||||
export type vtable = struct {
|
||||
reader: (*reader | void),
|
||||
};
|
||||
|
||||
export type s = *vtable;
|
||||
|
||||
export fn read(x: s, v: i32) i32 = {
|
||||
match (x.reader) {
|
||||
case void => return -1;
|
||||
case let f: *reader => return (*f)(x, v);
|
||||
};
|
||||
};
|
||||
|
||||
export fn mkvt(f: *reader) vtable = {
|
||||
return vtable { reader = f };
|
||||
};
|
||||
|
||||
export fn mkempty() vtable = {
|
||||
return vtable { reader = void };
|
||||
};
|
||||
7
test/wcc/data/r784_xmod_alias_struct_collide/sb/sb.ww
Normal file
7
test/wcc/data/r784_xmod_alias_struct_collide/sb/sb.ww
Normal file
@@ -0,0 +1,7 @@
|
||||
// foreign same-leaf STRUCT `s` (no reader field) — the #223 mis-peel bait.
|
||||
package sb;
|
||||
|
||||
export type s = struct {
|
||||
a: i32,
|
||||
b: i32,
|
||||
};
|
||||
11
test/wcc/data/r784_xmod_alias_struct_symmetric/case.ww
Normal file
11
test/wcc/data/r784_xmod_alias_struct_symmetric/case.ww
Normal file
@@ -0,0 +1,11 @@
|
||||
//ww:run-exit 21
|
||||
// migrated from test/wcc/784_xmod_alias_struct_collide_run.c: symmetric guard — sa's same-module STRUCT break must still fire with sb's same-leaf alias present.
|
||||
package main;
|
||||
import sa;
|
||||
import sb;
|
||||
export fn main() i32 = {
|
||||
let o = sa.mk(21, 8);
|
||||
let r = sa.geta(o);
|
||||
if (r != 21) { return 11; };
|
||||
return 21;
|
||||
};
|
||||
15
test/wcc/data/r784_xmod_alias_struct_symmetric/sa/sa.ww
Normal file
15
test/wcc/data/r784_xmod_alias_struct_symmetric/sa/sa.ww
Normal file
@@ -0,0 +1,15 @@
|
||||
// struct-side receiver for the r784 symmetric guard.
|
||||
package sa;
|
||||
|
||||
export type s = struct {
|
||||
a: i32,
|
||||
b: i32,
|
||||
};
|
||||
|
||||
export fn mk(av: i32, bv: i32) s = {
|
||||
return s { a = av, b = bv };
|
||||
};
|
||||
|
||||
export fn geta(x: s) i32 = {
|
||||
return x.a;
|
||||
};
|
||||
8
test/wcc/data/r784_xmod_alias_struct_symmetric/sb/sb.ww
Normal file
8
test/wcc/data/r784_xmod_alias_struct_symmetric/sb/sb.ww
Normal file
@@ -0,0 +1,8 @@
|
||||
// foreign same-leaf pointer ALIAS `s` — a naive alias-first reorder would mis-peel sa's struct.
|
||||
package sb;
|
||||
|
||||
export type vtable = struct {
|
||||
q: i32,
|
||||
};
|
||||
|
||||
export type s = *vtable;
|
||||
12
test/wcc/data/r793_xmod_argpush_combined_t16/case.ww
Normal file
12
test/wcc/data/r793_xmod_argpush_combined_t16/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:run-exit 114
|
||||
// migrated from test/wcc/793_xmod_struct_argpush_collide_run.c: recv+field+push in one fn under the same-leaf collision.
|
||||
package main;
|
||||
import m1;
|
||||
import m2;
|
||||
fn main() i32 = {
|
||||
let dummy: m2.pair;
|
||||
dummy.hi = 0: u64;
|
||||
let s = m1.mk();
|
||||
let x: i32 = (s.lo: i32);
|
||||
return m1.consume(s) + x;
|
||||
};
|
||||
5
test/wcc/data/r793_xmod_argpush_combined_t16/m1/m1.ww
Normal file
5
test/wcc/data/r793_xmod_argpush_combined_t16/m1/m1.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
// 16B sub-8-tail pair — the local side of the r793 collision.
|
||||
package m1;
|
||||
export type pair = struct { hi: u64, lo: u16 };
|
||||
export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };
|
||||
export fn consume(p: pair) i32 = { return (p.hi + (p.lo: u64)): i32; };
|
||||
4
test/wcc/data/r793_xmod_argpush_combined_t16/m2/m2.ww
Normal file
4
test/wcc/data/r793_xmod_argpush_combined_t16/m2/m2.ww
Normal file
@@ -0,0 +1,4 @@
|
||||
// 24B same-leaf foreign pair — the name-keyed lookup's mis-resolution bait.
|
||||
package m2;
|
||||
export type pair = struct { hi: u64, mid: u64, lo: u64 };
|
||||
export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };
|
||||
11
test/wcc/data/r793_xmod_argpush_fieldread_t16/case.ww
Normal file
11
test/wcc/data/r793_xmod_argpush_fieldread_t16/case.ww
Normal file
@@ -0,0 +1,11 @@
|
||||
//ww:run-exit 107
|
||||
// migrated from test/wcc/793_xmod_struct_argpush_collide_run.c: #224 site — direct field read off the inferred-let recv under the same-leaf collision.
|
||||
package main;
|
||||
import m1;
|
||||
import m2;
|
||||
fn main() i32 = {
|
||||
let dummy: m2.pair;
|
||||
dummy.hi = 0: u64;
|
||||
let s = m1.mk();
|
||||
return (s.hi + (s.lo: u64)): i32;
|
||||
};
|
||||
4
test/wcc/data/r793_xmod_argpush_fieldread_t16/m1/m1.ww
Normal file
4
test/wcc/data/r793_xmod_argpush_fieldread_t16/m1/m1.ww
Normal file
@@ -0,0 +1,4 @@
|
||||
// 16B sub-8-tail pair — the local side of the r793 collision.
|
||||
package m1;
|
||||
export type pair = struct { hi: u64, lo: u16 };
|
||||
export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };
|
||||
4
test/wcc/data/r793_xmod_argpush_fieldread_t16/m2/m2.ww
Normal file
4
test/wcc/data/r793_xmod_argpush_fieldread_t16/m2/m2.ww
Normal file
@@ -0,0 +1,4 @@
|
||||
// 24B same-leaf foreign pair — the name-keyed lookup's mis-resolution bait.
|
||||
package m2;
|
||||
export type pair = struct { hi: u64, mid: u64, lo: u64 };
|
||||
export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };
|
||||
11
test/wcc/data/r793_xmod_argpush_recvpush_t12/case.ww
Normal file
11
test/wcc/data/r793_xmod_argpush_recvpush_t12/case.ww
Normal file
@@ -0,0 +1,11 @@
|
||||
//ww:run-exit 6
|
||||
// migrated from test/wcc/793_xmod_struct_argpush_collide_run.c: maxalign-4 12B {u32,u32,u32} sub-8 tail under the same-leaf collision.
|
||||
package main;
|
||||
import m1;
|
||||
import m2;
|
||||
fn main() i32 = {
|
||||
let dummy: m2.pair;
|
||||
dummy.hi = 0: u64;
|
||||
let s = m1.mk();
|
||||
return m1.consume(s);
|
||||
};
|
||||
5
test/wcc/data/r793_xmod_argpush_recvpush_t12/m1/m1.ww
Normal file
5
test/wcc/data/r793_xmod_argpush_recvpush_t12/m1/m1.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
// 12B maxalign-4 triple — the local side of the r793 t12 collision.
|
||||
package m1;
|
||||
export type pair = struct { a: u32, b: u32, c: u32 };
|
||||
export fn mk() pair = { return pair { a = 1: u32, b = 2: u32, c = 3: u32 }; };
|
||||
export fn consume(p: pair) i32 = { return (p.a + p.b + p.c): i32; };
|
||||
4
test/wcc/data/r793_xmod_argpush_recvpush_t12/m2/m2.ww
Normal file
4
test/wcc/data/r793_xmod_argpush_recvpush_t12/m2/m2.ww
Normal file
@@ -0,0 +1,4 @@
|
||||
// 24B same-leaf foreign pair — the name-keyed lookup's mis-resolution bait.
|
||||
package m2;
|
||||
export type pair = struct { hi: u64, mid: u64, lo: u64 };
|
||||
export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };
|
||||
11
test/wcc/data/r793_xmod_argpush_recvpush_t16/case.ww
Normal file
11
test/wcc/data/r793_xmod_argpush_recvpush_t16/case.ww
Normal file
@@ -0,0 +1,11 @@
|
||||
//ww:run-exit 107
|
||||
// migrated from test/wcc/793_xmod_struct_argpush_collide_run.c: #21 push+recv sites — 16B m1.pair vs 24B foreign same-leaf; count must come from stamped tinfo.
|
||||
package main;
|
||||
import m1;
|
||||
import m2;
|
||||
fn main() i32 = {
|
||||
let dummy: m2.pair;
|
||||
dummy.hi = 0: u64;
|
||||
let s = m1.mk();
|
||||
return m1.consume(s);
|
||||
};
|
||||
5
test/wcc/data/r793_xmod_argpush_recvpush_t16/m1/m1.ww
Normal file
5
test/wcc/data/r793_xmod_argpush_recvpush_t16/m1/m1.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
// 16B sub-8-tail pair — the local side of the r793 collision.
|
||||
package m1;
|
||||
export type pair = struct { hi: u64, lo: u16 };
|
||||
export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };
|
||||
export fn consume(p: pair) i32 = { return (p.hi + (p.lo: u64)): i32; };
|
||||
4
test/wcc/data/r793_xmod_argpush_recvpush_t16/m2/m2.ww
Normal file
4
test/wcc/data/r793_xmod_argpush_recvpush_t16/m2/m2.ww
Normal file
@@ -0,0 +1,4 @@
|
||||
// 24B same-leaf foreign pair — the name-keyed lookup's mis-resolution bait.
|
||||
package m2;
|
||||
export type pair = struct { hi: u64, mid: u64, lo: u64 };
|
||||
export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };
|
||||
14
test/wcc/data/r797_nestfill_box/case.ww
Normal file
14
test/wcc/data/r797_nestfill_box/case.ww
Normal file
@@ -0,0 +1,14 @@
|
||||
//ww:run-exit 66
|
||||
// migrated from test/wcc/797_xmod_struct_field_layout_collide_run.c: cgstructlitfilltn nested-recursion pin — struct-literal field store with a nested literal; single-dot readback helpers per the open wwstage chained-read gaps.
|
||||
package main;
|
||||
type deep = struct { d0: u64, d1: u64 };
|
||||
type nst = struct { i0: u64, dp: deep };
|
||||
type box = struct { tag: u64, ir: nst };
|
||||
fn mkbox() box = { return box { tag = 0: u64, ir = nst { i0 = 0: u64, dp = deep { d0 = 0: u64, d1 = 0: u64 } } }; };
|
||||
fn sumdeep(d: deep) u64 = { return d.d0 + d.d1; };
|
||||
fn sumnst(n: nst) u64 = { return n.i0 + sumdeep(n.dp); };
|
||||
fn main() i32 = {
|
||||
let s = mkbox();
|
||||
s.ir = nst { i0 = 11: u64, dp = deep { d0 = 22: u64, d1 = 33: u64 } };
|
||||
return sumnst(s.ir): i32;
|
||||
};
|
||||
13
test/wcc/data/r797_xmod_layout_addrptr_pq/case.ww
Normal file
13
test/wcc/data/r797_xmod_layout_addrptr_pq/case.ww
Normal file
@@ -0,0 +1,13 @@
|
||||
//ww:run-exit 10
|
||||
// migrated from test/wcc/797_xmod_struct_field_layout_collide_run.c: A1 arm — &p.v LEAQ offset over *struct; all-u64 pq isolates offset from the narrow-deref-store width family.
|
||||
package main;
|
||||
import m1;
|
||||
import m2;
|
||||
fn main() i32 = {
|
||||
let dummy: m2.pq;
|
||||
dummy.a = 0: u64;
|
||||
let p = m1.mkpq();
|
||||
let q = &p.v;
|
||||
*q = 9: u64;
|
||||
return (p.a + p.v): i32;
|
||||
};
|
||||
10
test/wcc/data/r797_xmod_layout_addrptr_pq/m1/m1.ww
Normal file
10
test/wcc/data/r797_xmod_layout_addrptr_pq/m1/m1.ww
Normal file
@@ -0,0 +1,10 @@
|
||||
// 16B pair + 16B all-u64 pq providers — the local side of the r797 collision.
|
||||
package m1;
|
||||
export type pair = struct { hi: u64, lo: u16 };
|
||||
export type pq = struct { a: u64, v: u64 };
|
||||
let g: pair = pair { hi = 100: u64, lo = 7: u16 };
|
||||
let gq: pq = pq { a = 1: u64, v = 7: u64 };
|
||||
export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };
|
||||
export fn mkp() *pair = { return &g; };
|
||||
export fn mkq() pq = { return pq { a = 1: u64, v = 7: u64 }; };
|
||||
export fn mkpq() *pq = { return &gq; };
|
||||
5
test/wcc/data/r797_xmod_layout_addrptr_pq/m2/m2.ww
Normal file
5
test/wcc/data/r797_xmod_layout_addrptr_pq/m2/m2.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
// 24B same-leaf pair + pq — the name-keyed lookup's mis-resolution bait.
|
||||
package m2;
|
||||
export type pair = struct { hi: u64, mid: u64, lo: u64 };
|
||||
export type pq = struct { a: u64, b: u64, v: u64 };
|
||||
export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };
|
||||
13
test/wcc/data/r797_xmod_layout_addrval_pq/case.ww
Normal file
13
test/wcc/data/r797_xmod_layout_addrval_pq/case.ww
Normal file
@@ -0,0 +1,13 @@
|
||||
//ww:run-exit 10
|
||||
// migrated from test/wcc/797_xmod_struct_field_layout_collide_run.c: A2 arm — &s.v over a value struct under the same-leaf collision.
|
||||
package main;
|
||||
import m1;
|
||||
import m2;
|
||||
fn main() i32 = {
|
||||
let dummy: m2.pq;
|
||||
dummy.a = 0: u64;
|
||||
let s = m1.mkq();
|
||||
let q = &s.v;
|
||||
*q = 9: u64;
|
||||
return (s.a + s.v): i32;
|
||||
};
|
||||
10
test/wcc/data/r797_xmod_layout_addrval_pq/m1/m1.ww
Normal file
10
test/wcc/data/r797_xmod_layout_addrval_pq/m1/m1.ww
Normal file
@@ -0,0 +1,10 @@
|
||||
// 16B pair + 16B all-u64 pq providers — the local side of the r797 collision.
|
||||
package m1;
|
||||
export type pair = struct { hi: u64, lo: u16 };
|
||||
export type pq = struct { a: u64, v: u64 };
|
||||
let g: pair = pair { hi = 100: u64, lo = 7: u16 };
|
||||
let gq: pq = pq { a = 1: u64, v = 7: u64 };
|
||||
export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };
|
||||
export fn mkp() *pair = { return &g; };
|
||||
export fn mkq() pq = { return pq { a = 1: u64, v = 7: u64 }; };
|
||||
export fn mkpq() *pq = { return &gq; };
|
||||
5
test/wcc/data/r797_xmod_layout_addrval_pq/m2/m2.ww
Normal file
5
test/wcc/data/r797_xmod_layout_addrval_pq/m2/m2.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
// 24B same-leaf pair + pq — the name-keyed lookup's mis-resolution bait.
|
||||
package m2;
|
||||
export type pair = struct { hi: u64, mid: u64, lo: u64 };
|
||||
export type pq = struct { a: u64, b: u64, v: u64 };
|
||||
export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };
|
||||
12
test/wcc/data/r797_xmod_layout_letcopy_t16/case.ww
Normal file
12
test/wcc/data/r797_xmod_layout_letcopy_t16/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:run-exit 107
|
||||
// migrated from test/wcc/797_xmod_struct_field_layout_collide_run.c: C1 arm — let-copy size from the COPY source's stamped tinfo (value coincides; byte-id is the net).
|
||||
package main;
|
||||
import m1;
|
||||
import m2;
|
||||
fn main() i32 = {
|
||||
let dummy: m2.pair;
|
||||
dummy.hi = 0: u64;
|
||||
let p1 = m1.mk();
|
||||
let p2 = p1;
|
||||
return (p2.hi + (p2.lo: u64)): i32;
|
||||
};
|
||||
10
test/wcc/data/r797_xmod_layout_letcopy_t16/m1/m1.ww
Normal file
10
test/wcc/data/r797_xmod_layout_letcopy_t16/m1/m1.ww
Normal file
@@ -0,0 +1,10 @@
|
||||
// 16B pair + 16B all-u64 pq providers — the local side of the r797 collision.
|
||||
package m1;
|
||||
export type pair = struct { hi: u64, lo: u16 };
|
||||
export type pq = struct { a: u64, v: u64 };
|
||||
let g: pair = pair { hi = 100: u64, lo = 7: u16 };
|
||||
let gq: pq = pq { a = 1: u64, v = 7: u64 };
|
||||
export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };
|
||||
export fn mkp() *pair = { return &g; };
|
||||
export fn mkq() pq = { return pq { a = 1: u64, v = 7: u64 }; };
|
||||
export fn mkpq() *pq = { return &gq; };
|
||||
5
test/wcc/data/r797_xmod_layout_letcopy_t16/m2/m2.ww
Normal file
5
test/wcc/data/r797_xmod_layout_letcopy_t16/m2/m2.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
// 24B same-leaf pair + pq — the name-keyed lookup's mis-resolution bait.
|
||||
package m2;
|
||||
export type pair = struct { hi: u64, mid: u64, lo: u64 };
|
||||
export type pq = struct { a: u64, b: u64, v: u64 };
|
||||
export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };
|
||||
11
test/wcc/data/r797_xmod_layout_ptrread_t16/case.ww
Normal file
11
test/wcc/data/r797_xmod_layout_ptrread_t16/case.ww
Normal file
@@ -0,0 +1,11 @@
|
||||
//ww:run-exit 107
|
||||
// migrated from test/wcc/797_xmod_struct_field_layout_collide_run.c: R1 arm — *struct field read offset must come from stamped tinfo, not the foreign layout.
|
||||
package main;
|
||||
import m1;
|
||||
import m2;
|
||||
fn main() i32 = {
|
||||
let dummy: m2.pair;
|
||||
dummy.hi = 0: u64;
|
||||
let p = m1.mkp();
|
||||
return (p.hi + (p.lo: u64)): i32;
|
||||
};
|
||||
10
test/wcc/data/r797_xmod_layout_ptrread_t16/m1/m1.ww
Normal file
10
test/wcc/data/r797_xmod_layout_ptrread_t16/m1/m1.ww
Normal file
@@ -0,0 +1,10 @@
|
||||
// 16B pair + 16B all-u64 pq providers — the local side of the r797 collision.
|
||||
package m1;
|
||||
export type pair = struct { hi: u64, lo: u16 };
|
||||
export type pq = struct { a: u64, v: u64 };
|
||||
let g: pair = pair { hi = 100: u64, lo = 7: u16 };
|
||||
let gq: pq = pq { a = 1: u64, v = 7: u64 };
|
||||
export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };
|
||||
export fn mkp() *pair = { return &g; };
|
||||
export fn mkq() pq = { return pq { a = 1: u64, v = 7: u64 }; };
|
||||
export fn mkpq() *pq = { return &gq; };
|
||||
5
test/wcc/data/r797_xmod_layout_ptrread_t16/m2/m2.ww
Normal file
5
test/wcc/data/r797_xmod_layout_ptrread_t16/m2/m2.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
// 24B same-leaf pair + pq — the name-keyed lookup's mis-resolution bait.
|
||||
package m2;
|
||||
export type pair = struct { hi: u64, mid: u64, lo: u64 };
|
||||
export type pq = struct { a: u64, b: u64, v: u64 };
|
||||
export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };
|
||||
12
test/wcc/data/r797_xmod_layout_ptrwrite_t16/case.ww
Normal file
12
test/wcc/data/r797_xmod_layout_ptrwrite_t16/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:run-exit 109
|
||||
// migrated from test/wcc/797_xmod_struct_field_layout_collide_run.c: W1 arm — p.lo store offset/width through *struct under the same-leaf collision.
|
||||
package main;
|
||||
import m1;
|
||||
import m2;
|
||||
fn main() i32 = {
|
||||
let dummy: m2.pair;
|
||||
dummy.hi = 0: u64;
|
||||
let p = m1.mkp();
|
||||
p.lo = 9: u16;
|
||||
return (p.hi + (p.lo: u64)): i32;
|
||||
};
|
||||
10
test/wcc/data/r797_xmod_layout_ptrwrite_t16/m1/m1.ww
Normal file
10
test/wcc/data/r797_xmod_layout_ptrwrite_t16/m1/m1.ww
Normal file
@@ -0,0 +1,10 @@
|
||||
// 16B pair + 16B all-u64 pq providers — the local side of the r797 collision.
|
||||
package m1;
|
||||
export type pair = struct { hi: u64, lo: u16 };
|
||||
export type pq = struct { a: u64, v: u64 };
|
||||
let g: pair = pair { hi = 100: u64, lo = 7: u16 };
|
||||
let gq: pq = pq { a = 1: u64, v = 7: u64 };
|
||||
export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };
|
||||
export fn mkp() *pair = { return &g; };
|
||||
export fn mkq() pq = { return pq { a = 1: u64, v = 7: u64 }; };
|
||||
export fn mkpq() *pq = { return &gq; };
|
||||
5
test/wcc/data/r797_xmod_layout_ptrwrite_t16/m2/m2.ww
Normal file
5
test/wcc/data/r797_xmod_layout_ptrwrite_t16/m2/m2.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
// 24B same-leaf pair + pq — the name-keyed lookup's mis-resolution bait.
|
||||
package m2;
|
||||
export type pair = struct { hi: u64, mid: u64, lo: u64 };
|
||||
export type pq = struct { a: u64, b: u64, v: u64 };
|
||||
export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };
|
||||
12
test/wcc/data/r797_xmod_layout_valwrite_t16/case.ww
Normal file
12
test/wcc/data/r797_xmod_layout_valwrite_t16/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:run-exit 109
|
||||
// migrated from test/wcc/797_xmod_struct_field_layout_collide_run.c: W2 arm — s.lo store into a value struct under the same-leaf collision.
|
||||
package main;
|
||||
import m1;
|
||||
import m2;
|
||||
fn main() i32 = {
|
||||
let dummy: m2.pair;
|
||||
dummy.hi = 0: u64;
|
||||
let s = m1.mk();
|
||||
s.lo = 9: u16;
|
||||
return (s.hi + (s.lo: u64)): i32;
|
||||
};
|
||||
10
test/wcc/data/r797_xmod_layout_valwrite_t16/m1/m1.ww
Normal file
10
test/wcc/data/r797_xmod_layout_valwrite_t16/m1/m1.ww
Normal file
@@ -0,0 +1,10 @@
|
||||
// 16B pair + 16B all-u64 pq providers — the local side of the r797 collision.
|
||||
package m1;
|
||||
export type pair = struct { hi: u64, lo: u16 };
|
||||
export type pq = struct { a: u64, v: u64 };
|
||||
let g: pair = pair { hi = 100: u64, lo = 7: u16 };
|
||||
let gq: pq = pq { a = 1: u64, v = 7: u64 };
|
||||
export fn mk() pair = { return pair { hi = 100: u64, lo = 7: u16 }; };
|
||||
export fn mkp() *pair = { return &g; };
|
||||
export fn mkq() pq = { return pq { a = 1: u64, v = 7: u64 }; };
|
||||
export fn mkpq() *pq = { return &gq; };
|
||||
5
test/wcc/data/r797_xmod_layout_valwrite_t16/m2/m2.ww
Normal file
5
test/wcc/data/r797_xmod_layout_valwrite_t16/m2/m2.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
// 24B same-leaf pair + pq — the name-keyed lookup's mis-resolution bait.
|
||||
package m2;
|
||||
export type pair = struct { hi: u64, mid: u64, lo: u64 };
|
||||
export type pq = struct { a: u64, b: u64, v: u64 };
|
||||
export fn use2(p: pair) i32 = { return (p.hi + p.mid + p.lo): i32; };
|
||||
Reference in New Issue
Block a user