test: port the xmod collide observers to ww; birth test/xmod
test/xmod/collide_test.ww replaces 989_fnptrcollide_run.c and 989_barefn_collide_run.c with every assertion preserved (build-reject both stages; run-exit 9 + exactly-one TEXT main.run/aa.run + cs==ww). Makefile grows the XMOD_WW_TESTS block mirroring SEP_WW_TESTS, wired into test-compiler.
This commit is contained in:
@@ -1,268 +0,0 @@
|
||||
/*
|
||||
* 989_barefn_collide_run — #84 cgen bare-module fn-leaf collision gate.
|
||||
*
|
||||
* A bare-module fn (module==NULL — a package-LESS `//ww:module-reset`
|
||||
* primary, e.g. a user test file) whose leaf collides with an IMPORTED
|
||||
* module's same-leaf fn was MIS-MANGLED to the imported qualified name:
|
||||
* mod_collect skipped bare decls, so the bare fn never entered mod_map,
|
||||
* and at emission mod_lookup_for_fn(leaf, hint=NULL) first-matched the
|
||||
* imported entry → emitted `<mod>.<leaf>` instead of bare `<leaf>`. That
|
||||
* produced a DUPLICATE symbol with the imported fn (w6l-tolerated, #31-
|
||||
* class) and the bare fn became silently dead — a #40 residual / #263-
|
||||
* class silent miscompile, gate-blind and symmetric cs==ww. Surfaced by
|
||||
* #80's `ww test` coexist (lib/test exports `run`).
|
||||
*
|
||||
* Strict-package (#24a) form: a dir-package `aa` exporting `run`, imported
|
||||
* by a `package main;` root that defines its OWN `fn run` and calls it from
|
||||
* `main`. The root's `run` mangles `main.run` — DISTINCT from the imported
|
||||
* `aa.run` and from the bare entry `main`. The original #84 trigger (a
|
||||
* truly BARE `run` from a PACKAGE-LESS root) is unreachable now that
|
||||
* strict-package forbids package-less primaries; the #84 cgen machinery
|
||||
* (mod_collect / mod_lookup_for_fn) stays dormant-but-load-bearing for
|
||||
* genuinely package-less `//ww:module-reset` deps until #24b/#26.
|
||||
*
|
||||
* Asserts (default separate compilation, both driver stages):
|
||||
* 1. Build + run, BOTH stages → exit 9 (the user's `main.run`, return 9),
|
||||
* never aa.run (return 5).
|
||||
* 2. cs==ww (rule 10): the concatenated per-package `.s` is byte-identical
|
||||
* between the
|
||||
* two driver stages.
|
||||
* 3. DISTINCTNESS: the `.s` has EXACTLY ONE `TEXT main.run` (the user's)
|
||||
* AND EXACTLY ONE `TEXT aa.run` (the import) — distinct symbols, no dup.
|
||||
*
|
||||
* Light wwstage-driver test (CLAUDE.md rule 14): all intermediates are
|
||||
* `-o`-redirected to /tmp, phase-parallel-safe. Models 989_sepbuild_run.c.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.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 const char *
|
||||
absbin(void)
|
||||
{
|
||||
const char *b = getenv("BIN");
|
||||
if (!b) b = "out/bin";
|
||||
if (b[0] == '/') return b;
|
||||
static char buf[2048];
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return NULL;
|
||||
snprintf(buf, sizeof buf, "%s/%s", cwd, b);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp(const char *path, char **outbuf, size_t *outlen)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return -1;
|
||||
fseek(f, 0, SEEK_END);
|
||||
long n = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (n < 0) { fclose(f); return -1; }
|
||||
char *b = malloc((size_t)n + 1);
|
||||
if (!b) { fclose(f); return -1; }
|
||||
if (fread(b, 1, (size_t)n, f) != (size_t)n) { free(b); fclose(f); return -1; }
|
||||
b[n] = '\0';
|
||||
fclose(f);
|
||||
*outbuf = b;
|
||||
*outlen = (size_t)n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
files_eq(const char *a, const char *b)
|
||||
{
|
||||
char *ba = NULL, *bb = NULL;
|
||||
size_t na = 0, nb = 0;
|
||||
if (slurp(a, &ba, &na) < 0 || slurp(b, &bb, &nb) < 0) {
|
||||
free(ba); free(bb);
|
||||
return -1;
|
||||
}
|
||||
int eq = (na == nb && memcmp(ba, bb, na) == 0);
|
||||
free(ba); free(bb);
|
||||
return eq ? 0 : 1;
|
||||
}
|
||||
|
||||
static int
|
||||
write_file(const char *path, const char *body)
|
||||
{
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(body, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Count occurrences of a line beginning with `^TEXT <sym>,` in the .s. */
|
||||
static int
|
||||
count_text_label(const char *sfile, const char *sym)
|
||||
{
|
||||
char *buf = NULL;
|
||||
size_t n = 0;
|
||||
if (slurp(sfile, &buf, &n) < 0) return -1;
|
||||
char needle[128];
|
||||
snprintf(needle, sizeof needle, "TEXT %s,", sym);
|
||||
size_t nl = strlen(needle);
|
||||
int count = 0;
|
||||
size_t i = 0;
|
||||
while (i < n) {
|
||||
size_t j = i;
|
||||
while (j < n && buf[j] != '\n') j++;
|
||||
if (j - i >= nl && strncmp(buf + i, needle, nl) == 0)
|
||||
count++;
|
||||
i = j + 1;
|
||||
}
|
||||
free(buf);
|
||||
return count;
|
||||
}
|
||||
|
||||
static const char *aa_src =
|
||||
"package aa;\n"
|
||||
"export fn run() i32 = { return 5; };\n";
|
||||
|
||||
/* Strict-package (#24a) root: declares `package main;`, so its `fn run`
|
||||
* mangles `main.run` — DISTINCT from the imported `aa.run`, and from the
|
||||
* bare entry `main`. The bare call `run()` resolves to the local `main.run`
|
||||
* via the standard exact-hint path (the #84 special-case is bypassed for a
|
||||
* moduled root). */
|
||||
static const char *root_src =
|
||||
"package main;\n"
|
||||
"import aa;\n"
|
||||
"fn run() i32 = { return 9; };\n"
|
||||
"export fn main() i32 = { return run(); };\n";
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = absbin();
|
||||
if (!bin) { fprintf(stderr, "84 FAIL: getcwd\n"); return 1; }
|
||||
|
||||
char td[] = "/tmp/wwbare84_XXXXXX";
|
||||
char cmd[8192];
|
||||
int fail = 0, aaowned = 0, aafile = 0, rootfile = 0;
|
||||
int stage_started[2] = { 0, 0 };
|
||||
if (mkdtemp(td) == NULL) {
|
||||
perror("wwbare84: mkdtemp");
|
||||
return 1;
|
||||
}
|
||||
/* All paths derive from `td` (a small fixed 64-byte buffer) so the
|
||||
* snprintfs are provably non-truncating (warning-clean). */
|
||||
char aadir[1024], aaww[1024], rootww[1024];
|
||||
snprintf(aadir, sizeof aadir, "%s/aa", td);
|
||||
snprintf(aaww, sizeof aaww, "%s/aa/aa.ww", td);
|
||||
snprintf(rootww, sizeof rootww, "%s/root.ww", td);
|
||||
if (mkdir(aadir, 0755) != 0) {
|
||||
perror(aadir);
|
||||
fail++;
|
||||
goto out;
|
||||
}
|
||||
aaowned = 1;
|
||||
if (write_file(aaww, aa_src) != 0) {
|
||||
fprintf(stderr, "84 FAIL: write fixture\n");
|
||||
fail++;
|
||||
goto out;
|
||||
}
|
||||
aafile = 1;
|
||||
if (write_file(rootww, root_src) != 0) {
|
||||
fprintf(stderr, "84 FAIL: write fixture\n");
|
||||
fail++;
|
||||
goto out;
|
||||
}
|
||||
rootfile = 1;
|
||||
|
||||
struct { const char *drv, *tag; char prog[1024], sfile[1024]; }
|
||||
stg[] = { { "ww", "cs", {0}, {0} }, { "ww_ww", "ww", {0}, {0} } };
|
||||
|
||||
for (int s = 0; s < 2; s++) {
|
||||
snprintf(stg[s].prog, sizeof stg[s].prog, "%s/prog.%s", td, stg[s].tag);
|
||||
snprintf(stg[s].sfile, sizeof stg[s].sfile, "%s/prog.%s.s", td, stg[s].tag);
|
||||
/* #93 sep layout: `-o <prog>` splits the asm across
|
||||
* <prog>.sepwork/<pkg>.s — the bare user `TEXT run,` lands in
|
||||
* __root.s and the imported `TEXT aa.run,` in aa.s. Concat those
|
||||
* exact per-unit .s files in a fixed order into the
|
||||
* flat <prog>.s the label-count + byte-id checks consume. The
|
||||
* `-I %s` source path is preserved, and all outputs stay under
|
||||
* the tmpdir. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 240 %s/%s build -o %s "
|
||||
"-I %s %s >/dev/null 2>&1 && "
|
||||
"cat %s.sepwork/__root.s %s.sepwork/aa.s > %s",
|
||||
bin, stg[s].drv, stg[s].prog, td, rootww,
|
||||
stg[s].prog, stg[s].prog, stg[s].sfile);
|
||||
stage_started[s] = 1;
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "84 FAIL: %s build\n", stg[s].drv);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
/* (1) the user's `main.run` (9) must win over imported aa.run (5). */
|
||||
int rc = runwait(stg[s].prog);
|
||||
if (rc != 9) {
|
||||
fprintf(stderr, "84 FAIL: %s prog exit=%d expected 9 "
|
||||
"(user main.run, not aa.run=5)\n", stg[s].drv, rc);
|
||||
fail++;
|
||||
}
|
||||
/* (3) distinct symbols, no dup: exactly one `main.run` + one
|
||||
* `aa.run`. */
|
||||
int nrun = count_text_label(stg[s].sfile, "main.run");
|
||||
int naa = count_text_label(stg[s].sfile, "aa.run");
|
||||
if (nrun != 1 || naa != 1) {
|
||||
fprintf(stderr, "84 FAIL: %s labels TEXT main.run=%d aa.run=%d "
|
||||
"(want 1/1 — user main.run distinct from import, no dup)\n",
|
||||
stg[s].drv, nrun, naa);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
/* (2) cs==ww (rule 10): concatenated per-package .s is byte-identical. */
|
||||
if (files_eq(stg[0].sfile, stg[1].sfile) != 0) {
|
||||
fprintf(stderr, "84 FAIL: cs .s != ww .s (rule 10)\n");
|
||||
fail++;
|
||||
}
|
||||
|
||||
out:
|
||||
{
|
||||
int cleanfail = 0;
|
||||
char path[1024];
|
||||
const char *tags[] = { "cs", "ww" };
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (!stage_started[i]) continue;
|
||||
snprintf(path, sizeof path, "%s/prog.%s.sepwork", td, tags[i]);
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", path);
|
||||
if (runwait(cmd) != 0) cleanfail = 1;
|
||||
snprintf(path, sizeof path, "%s/prog.%s", td, tags[i]);
|
||||
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
|
||||
snprintf(path, sizeof path, "%s/prog.%s.s", td, tags[i]);
|
||||
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
|
||||
}
|
||||
if (rootfile && unlink(rootww) != 0 && errno != ENOENT) cleanfail = 1;
|
||||
if (aafile && unlink(aaww) != 0 && errno != ENOENT) cleanfail = 1;
|
||||
if (aaowned && rmdir(aadir) != 0) cleanfail = 1;
|
||||
if (rmdir(td) != 0) cleanfail = 1;
|
||||
if (cleanfail) {
|
||||
fprintf(stderr, "84 FAIL: temporary cleanup failed\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
if (fail) {
|
||||
fprintf(stderr, "84: %d check(s) failed\n", fail);
|
||||
return 1;
|
||||
}
|
||||
printf("barefn_collide: `package main;` root `fn run` (main.run) coexists "
|
||||
"with imported aa.run — distinct labels, no dup, user run wins "
|
||||
"(exit 9), cs==ww, both driver stages (#84/#24a)\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* 989_fnptrcollide_run — F7-c7 (#14): nodefnptr must be type-keyed, not
|
||||
* name-keyed, so a value ident whose LEAF collides with a fn name is not
|
||||
* mis-folded into the fn's TEXT reloc.
|
||||
*
|
||||
* THE BUG (cat-A silent miscompile, gate-blind): nodefnptr
|
||||
* (selfhost/cmd/wcc/cgen.ww) decided whether `&x` (in a static-init / DATAR
|
||||
* fold context) was the address-of a top-level fn by NAME — `fnretlookup(
|
||||
* c, x.str) != nil`. So `&slot` for a data global `slot: i64` that shares a
|
||||
* leaf with a fn `slot` (e.g. an imported `bar.slot`) matched the fn and
|
||||
* folded into a DATAR reloc to the fn's TEXT symbol instead of the data
|
||||
* symbol. cstage is type-keyed (node_fnptr_sym: type_chase_named(opnd->
|
||||
* type)->kind == TY_FN, cmd/w6c/cgen.c:15542) — `slot`'s stamped type is
|
||||
* i64, not TY_FN, so it does NOT fold the reloc and the build errors loud.
|
||||
* Pre-fix wwstage silently produced a binary (the cat-A divergence: cs
|
||||
* loud-fails, ww builds). THE FIX: read the stamped operand type
|
||||
* (opnd.type_ chased == TY_FN), aligning wwstage UP — a non-fn operand can
|
||||
* never fold to a fn reloc, closing the leaf-name collision by construction.
|
||||
*
|
||||
* This shape is gate-blind (the corpus never collides a data-global leaf
|
||||
* with a fn name on the &-fold path); only this row catches it. The
|
||||
* conversion's neutrality on the corpus's REAL &fn static-inits (the
|
||||
* #117/#119 reloc machinery) is proven separately by the c7 self-compile
|
||||
* byte-id bind (B1/B2 zero-move) — see /tmp/implf7_result.txt.
|
||||
*
|
||||
* Assertion: the construction must FAIL TO BUILD on BOTH stages (cstage
|
||||
* already rejects; wwstage now rejects too — rule-10 align-up). Pre-fix
|
||||
* wwstage BUILT it (rc=0) — the row was RED on the pre-c7 binary.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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;
|
||||
}
|
||||
|
||||
/* build_must_fail — write a bar/ module (fn `slot`) and a main.ww that
|
||||
* declares a data global `slot: i64` plus `let fp: *i64 = &slot`, then
|
||||
* `drv build -I bar main.ww`. Returns 0 when the build correctly FAILS
|
||||
* (the leaf-name collision no longer diverts &slot into the fn reloc),
|
||||
* non-zero when it wrongly built. */
|
||||
static int
|
||||
build_must_fail(const char *drv, int tag, int *cleanup_failed)
|
||||
{
|
||||
int pid = getpid();
|
||||
char dir[96], bard[160], p[224], cmd[1024];
|
||||
int rc = -1, setupfail = 1, cleanfail = 0, bard_owned = 0;
|
||||
*cleanup_failed = 0;
|
||||
snprintf(dir, sizeof dir, "/tmp/fnpc_%d_%d", pid, tag);
|
||||
snprintf(bard, sizeof bard, "%s/bar", dir);
|
||||
if (mkdir(dir, 0755) != 0) return -2;
|
||||
if (mkdir(bard, 0755) != 0) goto cleanup;
|
||||
bard_owned = 1;
|
||||
|
||||
snprintf(p, sizeof p, "%s/bar.ww", bard);
|
||||
FILE *f = fopen(p, "wb");
|
||||
if (!f) goto cleanup;
|
||||
fputs("package bar;\n"
|
||||
"export fn slot() i64 = { return 99; };\n", f);
|
||||
if (fclose(f) != 0) goto cleanup;
|
||||
|
||||
snprintf(p, sizeof p, "%s/main.ww", dir);
|
||||
f = fopen(p, "wb");
|
||||
if (!f) goto cleanup;
|
||||
fputs("package main;\n"
|
||||
"import bar;\n"
|
||||
"let slot: i64 = 7;\n"
|
||||
"let fp: *i64 = &slot;\n"
|
||||
"export fn main() int = { return (*fp): int; };\n", f);
|
||||
if (fclose(f) != 0) goto cleanup;
|
||||
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s build -I bar main.ww >/dev/null 2>&1", dir, drv);
|
||||
rc = runwait(cmd);
|
||||
setupfail = 0;
|
||||
|
||||
cleanup:
|
||||
snprintf(p, sizeof p, "%s/main.sepwork", dir);
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", p);
|
||||
if (runwait(cmd) != 0) cleanfail = 1;
|
||||
snprintf(p, sizeof p, "%s/main", dir);
|
||||
if (unlink(p) != 0 && access(p, F_OK) == 0) cleanfail = 1;
|
||||
snprintf(p, sizeof p, "%s/main.ww", dir);
|
||||
if (unlink(p) != 0 && access(p, F_OK) == 0) cleanfail = 1;
|
||||
if (bard_owned) {
|
||||
snprintf(p, sizeof p, "%s/bar.ww", bard);
|
||||
if (unlink(p) != 0 && access(p, F_OK) == 0) cleanfail = 1;
|
||||
if (rmdir(bard) != 0) cleanfail = 1;
|
||||
}
|
||||
if (rmdir(dir) != 0) cleanfail = 1;
|
||||
*cleanup_failed = cleanfail;
|
||||
|
||||
if (setupfail) return -2;
|
||||
return rc == 0 ? -1 : 0; /* build must NOT succeed */
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
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[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int total = 0, fail = 0;
|
||||
|
||||
total++;
|
||||
int cleanfail = 0;
|
||||
int got = build_must_fail(cdrv, 1, &cleanfail);
|
||||
if (got != 0) {
|
||||
fprintf(stderr, "fnptrcollide_run[cstage]: %s\n",
|
||||
got == -2 ? "setup failed" :
|
||||
"built ok, expected the leaf-name collision to be rejected");
|
||||
fail++;
|
||||
}
|
||||
if (cleanfail) {
|
||||
fprintf(stderr, "fnptrcollide_run[cstage]: temporary cleanup failed\n");
|
||||
fail++;
|
||||
}
|
||||
if (access(wdrv, X_OK) == 0) { /* wwstage gated */
|
||||
total++;
|
||||
got = build_must_fail(wdrv, 2, &cleanfail);
|
||||
if (got != 0) {
|
||||
fprintf(stderr, "fnptrcollide_run[wwstage]: %s\n",
|
||||
got == -2 ? "setup failed" :
|
||||
"built ok, expected reject (#14 — &slot mis-folded "
|
||||
"to the fn TEXT reloc by the name-keyed nodefnptr)");
|
||||
fail++;
|
||||
}
|
||||
if (cleanfail) {
|
||||
fprintf(stderr, "fnptrcollide_run[wwstage]: temporary cleanup failed\n");
|
||||
fail++;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "fnptrcollide_run: skip wwstage (no %s)\n", wdrv);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "fnptrcollide_run: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("fnptrcollide_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
134
test/xmod/collide_test.ww
Normal file
134
test/xmod/collide_test.ww
Normal file
@@ -0,0 +1,134 @@
|
||||
package collide_test;
|
||||
|
||||
// Cross-module leaf-name collision observers on both driver stages.
|
||||
// Ports of the retired native carriers test/wcc/989_fnptrcollide_run.c
|
||||
// and 989_barefn_collide_run.c; every assertion preserved.
|
||||
//
|
||||
// fnptrcollide (#14 F7-c7) — a data global `slot: i64` whose LEAF
|
||||
// collides with the imported fn bar.slot must FAIL to build on BOTH
|
||||
// stages: type-keyed nodefnptr refuses to fold `&slot` into the fn's
|
||||
// TEXT reloc. Pre-fix wwstage was name-keyed and silently BUILT it
|
||||
// (a DATAR to the fn symbol — cat-A: cs loud-fails, ww builds). The
|
||||
// collision REQUIRES the import path (imported fn leaf vs local data
|
||||
// global), so a single-file fixture cannot express it.
|
||||
//
|
||||
// barefn (#84/#24a) — a `package main;` root `fn run` coexists with
|
||||
// imported aa.run: build+run exit 9 on BOTH stages (the user's
|
||||
// main.run wins, never aa.run=5); the concatenated
|
||||
// __root.s+aa.s (fixed #93 sep order) carries EXACTLY ONE
|
||||
// `TEXT main.run,` and EXACTLY ONE `TEXT aa.run,` (distinct symbols,
|
||||
// no #31-class w6l-tolerated duplicate, no silently-dead bare fn);
|
||||
// cs==ww byte-for-byte (rule 10).
|
||||
//
|
||||
// Dropped C machinery, not assertions: the ww_ww-absent skip gate
|
||||
// (the Make target declares both drivers), the shell `timeout 240`
|
||||
// (runcommand's deadline carries it), and the unlink/rmdir accounting
|
||||
// (testenv.clean asserts the removal).
|
||||
|
||||
import os;
|
||||
import os.exec;
|
||||
import strings;
|
||||
import testenv;
|
||||
import time;
|
||||
|
||||
fn fail(label: str, why: str) void = {
|
||||
let m: str = strings.concat("collide FAIL: ", label, " -- ", why,
|
||||
"\n");
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
assert(false);
|
||||
};
|
||||
|
||||
fn tmo() time.duration = {
|
||||
return (240i64 * (time.second: i64)): time.duration;
|
||||
};
|
||||
|
||||
// -1 encodes an abnormal (non-EXIT) termination, never a valid code.
|
||||
fn runcode(dir: str, name: str, argv: []str) i32 = {
|
||||
let co: testenv.commandout;
|
||||
testenv.runcommand(dir, dir, name, argv, tmo(), &co);
|
||||
if (co.termination != exec.termination.EXIT) { return -1; };
|
||||
return co.code;
|
||||
};
|
||||
|
||||
@test fn fnptrcollide() void = {
|
||||
let drvs: []str = ["ww", "ww_ww"];
|
||||
let i: i32 = 0;
|
||||
for (i < 2) {
|
||||
let td: str = testenv.fresh();
|
||||
assert(os.mkdir(strings.concat(td, "/bar"), 493) == 0);
|
||||
testenv.writefile(strings.concat(td, "/bar/bar.ww"),
|
||||
strings.concat(
|
||||
"package bar;\n",
|
||||
"export fn slot() i64 = { return 99; };\n"));
|
||||
testenv.writefile(strings.concat(td, "/main.ww"), strings.concat(
|
||||
"package main;\n",
|
||||
"import bar;\n",
|
||||
"let slot: i64 = 7;\n",
|
||||
"let fp: *i64 = &slot;\n",
|
||||
"export fn main() int = { return (*fp): int; };\n"));
|
||||
let av: []str = [testenv.driver(drvs[i]), "build", "-I", "bar",
|
||||
"main.ww"];
|
||||
if (runcode(td, strings.concat("build_", drvs[i]), av) == 0) {
|
||||
fail("fnptrcollide", strings.concat(drvs[i], " built ok, ",
|
||||
"expected the leaf-name collision to be rejected (#14 -- ",
|
||||
"&slot mis-folded to the fn TEXT reloc)"));
|
||||
};
|
||||
testenv.clean(td);
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
|
||||
// __root.s then aa.s: the carrier's fixed #93 sep concat order.
|
||||
fn catfixed(stem: str) str = {
|
||||
return strings.concat(
|
||||
testenv.readfile(strings.concat(stem, ".sepwork/__root.s")),
|
||||
testenv.readfile(strings.concat(stem, ".sepwork/aa.s")));
|
||||
};
|
||||
|
||||
// column-0 anchored label lines; the '\n' prepend counts a file-leading
|
||||
// label too.
|
||||
fn textcount(s: str, sym: str) i32 = {
|
||||
return testenv.occurrences(strings.concat("\n", s),
|
||||
strings.concat("\nTEXT ", sym, ","));
|
||||
};
|
||||
|
||||
@test fn barefn() void = {
|
||||
let td: str = testenv.fresh();
|
||||
assert(os.mkdir(strings.concat(td, "/aa"), 493) == 0);
|
||||
testenv.writefile(strings.concat(td, "/aa/aa.ww"), strings.concat(
|
||||
"package aa;\n",
|
||||
"export fn run() i32 = { return 5; };\n"));
|
||||
testenv.writefile(strings.concat(td, "/root.ww"), strings.concat(
|
||||
"package main;\n",
|
||||
"import aa;\n",
|
||||
"fn run() i32 = { return 9; };\n",
|
||||
"export fn main() i32 = { return run(); };\n"));
|
||||
let drvs: []str = ["ww", "ww_ww"];
|
||||
let tags: []str = ["cs", "ww"];
|
||||
let asms: []str = ["", ""];
|
||||
let s: i32 = 0;
|
||||
for (s < 2) {
|
||||
let stem: str = strings.concat(td, "/prog.", tags[s]);
|
||||
let av: []str = [testenv.driver(drvs[s]), "build", "-o", stem,
|
||||
"-I", td, strings.concat(td, "/root.ww")];
|
||||
if (runcode(td, strings.concat("build_", tags[s]), av) != 0) {
|
||||
fail("barefn", strings.concat(drvs[s], " build failed"));
|
||||
};
|
||||
let rav: []str = [stem];
|
||||
if (runcode(td, strings.concat("run_", tags[s]), rav) != 9) {
|
||||
fail("barefn", strings.concat(drvs[s], " exit != 9 ",
|
||||
"(user main.run must win, not aa.run=5)"));
|
||||
};
|
||||
asms[s] = catfixed(stem);
|
||||
if (textcount(asms[s], "main.run") != 1
|
||||
|| textcount(asms[s], "aa.run") != 1) {
|
||||
fail("barefn", strings.concat(drvs[s], " TEXT label counts ",
|
||||
"!= 1/1 (user main.run distinct from import, no dup)"));
|
||||
};
|
||||
s += 1;
|
||||
};
|
||||
if (!testenv.same(asms[0], asms[1])) {
|
||||
fail("barefn", "cs .s != ww .s (rule 10)");
|
||||
};
|
||||
testenv.clean(td);
|
||||
};
|
||||
Reference in New Issue
Block a user