wcc,ww: prepend synth use test; user fn run coexists with runner (M4 E2, #80)
The -T harness synthesized `use test;` after name-binding, so the lib/test runner run keyed the bare scope and collided with a user-defined bare fn run — a spurious "duplicate fn run" reject (the E1 tolerance seam). Prepending the synth use before binding keys the runner as test.run in the test module namespace, distinct from the user bare run; the two coexist. Hare-faithful: the runner is its own test module (ref/hare/test/+test.ha:97). Inverts attest_userrun.ww from the #23-mandated reject to a coexist fixture; gate asserts exactly 1 TEXT run + 1 TEXT test.run on the -T asm (distinct symbols, not a dead-dup). Closes #80.
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
@@ -282,17 +283,49 @@ accept(const char *bin, const char *comp, const char *fixture, const char *what)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* collide_run — a @test unit defining a user `fn run` collides with
|
||||
* lib/test's bound runner once `<drv> test -c` bundles it; `<comp> -T` of
|
||||
* the combined must loud-reject the duplicate (#23). Pre-fix wwstage built
|
||||
* a binary that called the user run and silently skipped every @test. */
|
||||
/* count_text — number of `TEXT <sym>,` directive lines in an asm file
|
||||
* (Plan-9 `TEXT name,$frame`), -1 on open failure. A COUNT (not presence)
|
||||
* is what distinguishes the #84 dead-dup: the bug yields 0x `TEXT run` +
|
||||
* 2x `TEXT test.run`, which a mere presence check would miss. */
|
||||
static int
|
||||
collide_run(const char *bin, const char *comp, const char *drv)
|
||||
count_text(const char *path, const char *sym)
|
||||
{
|
||||
char want[128];
|
||||
snprintf(want, sizeof want, "TEXT %s,", sym);
|
||||
size_t wlen = strlen(want);
|
||||
FILE *f = fopen(path, "r");
|
||||
if (f == NULL) return -1;
|
||||
char line[8192];
|
||||
int n = 0;
|
||||
while (fgets(line, sizeof line, f) != NULL)
|
||||
if (strncmp(line, want, wlen) == 0) n++;
|
||||
fclose(f);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* coexist_run — a @test unit that ALSO defines a user `fn run` must
|
||||
* COEXIST with lib/test's bound runner, not collide. Post-#80 the synth
|
||||
* `use test;` is prepended before binding, so lib/test's `run` keys under
|
||||
* "test" (not "") and no longer duplicate-collides with the user's bare
|
||||
* `run` (which mangles bare via #84). `<comp> -T` of the `<drv> test -c`
|
||||
* combined now ACCEPTS; the -T asm carries EXACTLY 1 `TEXT run` (user,
|
||||
* bare) + 1 `TEXT test.run` (lib runner) — the static-label proof that the
|
||||
* user run is distinct in the real @test/-T path where #84 lives; then,
|
||||
* assembled, linked and run, the synth entry exits 0 (the user `run` did
|
||||
* not hijack the runner and the @test passes). Pre-#80 this loud-rejected
|
||||
* "duplicate fn run". */
|
||||
static int
|
||||
coexist_run(const char *bin, const char *comp, const char *drv)
|
||||
{
|
||||
int pid = getpid();
|
||||
char stem[256], comb[300], cmd[4096];
|
||||
char stem[256], comb[300], asmf[320], obj[320], exe[320];
|
||||
char rt[1024], cmd[4096];
|
||||
snprintf(stem, sizeof stem, "/tmp/at910cr_%s_%d", comp, pid);
|
||||
snprintf(comb, sizeof comb, "%s.combined.ww", stem);
|
||||
snprintf(asmf, sizeof asmf, "%s.run.s", stem);
|
||||
snprintf(obj, sizeof obj, "%s.run.o", stem);
|
||||
snprintf(exe, sizeof exe, "%s.exe", stem);
|
||||
snprintf(rt, sizeof rt, "%s/../lib/libwwrt.a", bin);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"%s/%s test -c -o %s test/wcc/data/attest_userrun.ww > /dev/null 2>&1",
|
||||
bin, drv, stem);
|
||||
@@ -301,15 +334,40 @@ collide_run(const char *bin, const char *comp, const char *drv)
|
||||
fprintf(stderr, "910 FAIL: %s build produced no %s\n", drv, comb);
|
||||
return 1;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s/%s -T %s -o /dev/null 2>/dev/null",
|
||||
bin, comp, comb);
|
||||
int rc = 0;
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "910 FAIL: %s -T accepted user `fn run` collision "
|
||||
"(expected duplicate-fn reject)\n", comp);
|
||||
snprintf(cmd, sizeof cmd, "%s/%s -T %s -o %s 2>/dev/null",
|
||||
bin, comp, comb, asmf);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "910 FAIL: %s -T rejected user `fn run` coexist "
|
||||
"(expected accept post-#80)\n", comp);
|
||||
rc = 1;
|
||||
}
|
||||
unlink(comb);
|
||||
if (rc == 0) {
|
||||
int nr = count_text(asmf, "run");
|
||||
int nt = count_text(asmf, "test.run");
|
||||
if (nr != 1 || nt != 1) {
|
||||
fprintf(stderr, "910 FAIL: %s -T asm label count run=%d "
|
||||
"test.run=%d (want 1/1) — user `fn run` not distinct from "
|
||||
"lib runner (a #84 dead-dup gives run=0/test.run=2)\n",
|
||||
comp, nr, nt);
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s 2>/dev/null", bin, obj, asmf);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "910 FAIL: coexist w6a\n"); rc = 1; }
|
||||
}
|
||||
if (rc == 0) {
|
||||
snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s %s 2>/dev/null",
|
||||
bin, exe, obj, rt);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "910 FAIL: coexist w6l\n"); rc = 1; }
|
||||
}
|
||||
if (rc == 0 && runwait(exe) != 0) {
|
||||
fprintf(stderr, "910 FAIL: coexist synth entry nonzero — user "
|
||||
"`fn run` hijacked the runner or the @test failed\n");
|
||||
rc = 1;
|
||||
}
|
||||
unlink(comb); unlink(asmf); unlink(obj); unlink(exe);
|
||||
char tmp[320];
|
||||
snprintf(tmp, sizeof tmp, "%s.s", stem); unlink(tmp);
|
||||
snprintf(tmp, sizeof tmp, "%s.o", stem); unlink(tmp);
|
||||
@@ -384,11 +442,11 @@ main(void)
|
||||
"use-before-value") != 0) return 1;
|
||||
if (modfn_run(bin, "ww", "test/wcc/data/modfn_coexist_vbu_ok.ww",
|
||||
"value-before-use") != 0) return 1;
|
||||
if (collide_run(bin, "w6c", "ww") != 0) return 1;
|
||||
if (coexist_run(bin, "w6c", "ww") != 0) return 1;
|
||||
|
||||
printf("@test -T: run ok + user-main and bad-signature rejected + "
|
||||
"non-T @test drop + checked-body + dangling-call link-fail (#6) + "
|
||||
"dup fn/type/def/let reject + xpkg + builtin-redecl accept + "
|
||||
"modfn coexist (#30) + fn-run collision (#23)\n");
|
||||
"modfn coexist (#30) + fn-run coexist (#80)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -330,34 +330,113 @@ accept_byteid(const char *bin, const char *fixture, const char *what)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* collide_run — a @test unit defining a user `fn run` collides with
|
||||
* lib/test's bound runner once `ww test -c` bundles it; `w6c_ww -T` of the
|
||||
* combined must loud-reject the duplicate (#23). Pre-fix wwstage built a
|
||||
* binary that called the user run and silently skipped every @test. */
|
||||
/* count_text — number of `TEXT <sym>,` directive lines in an asm file
|
||||
* (Plan-9 `TEXT name,$frame`), -1 on open failure. A COUNT (not presence)
|
||||
* is what distinguishes the #84 dead-dup: the bug yields 0x `TEXT run` +
|
||||
* 2x `TEXT test.run`, which a mere presence check would miss. */
|
||||
static int
|
||||
collide_run(const char *bin)
|
||||
count_text(const char *path, const char *sym)
|
||||
{
|
||||
char want[128];
|
||||
snprintf(want, sizeof want, "TEXT %s,", sym);
|
||||
size_t wlen = strlen(want);
|
||||
FILE *f = fopen(path, "r");
|
||||
if (f == NULL) return -1;
|
||||
char line[8192];
|
||||
int n = 0;
|
||||
while (fgets(line, sizeof line, f) != NULL)
|
||||
if (strncmp(line, want, wlen) == 0) n++;
|
||||
fclose(f);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* coexist_run — a @test unit that ALSO defines a user `fn run` must
|
||||
* COEXIST with lib/test's bound runner, not collide. Post-#80 the synth
|
||||
* `use test;` is prepended before binding, so lib/test's `run` keys under
|
||||
* "test" (not "") and no longer duplicate-collides with the user's bare
|
||||
* `run` (which mangles bare via #84). `w6c_ww -T` of the `ww_ww test -c`
|
||||
* combined now ACCEPTS; the -T asm carries EXACTLY 1 `TEXT run` (user,
|
||||
* bare) + 1 `TEXT test.run` (lib runner) and is byte-identical to w6c's
|
||||
* (cs==ww) — the static-label proof that the user run is distinct in the
|
||||
* real @test/-T path where #84 lives; then, assembled, linked and run, the
|
||||
* synth entry exits 0 (the user `run` did not hijack the runner and the
|
||||
* @test passes). Pre-#80 this loud-rejected "duplicate fn run". */
|
||||
static int
|
||||
coexist_run(const char *bin)
|
||||
{
|
||||
int pid = getpid();
|
||||
char stem[256], comb[300], cmd[4096];
|
||||
char stem[256], comb[300], asmf[320], csasm[320], obj[320], exe[320];
|
||||
char rt[1024], cmd[4096];
|
||||
snprintf(stem, sizeof stem, "/tmp/at997cr_%d", pid);
|
||||
snprintf(comb, sizeof comb, "%s.combined.ww", stem);
|
||||
snprintf(asmf, sizeof asmf, "%s.run.s", stem);
|
||||
snprintf(csasm, sizeof csasm, "%s.cs.s", stem);
|
||||
snprintf(obj, sizeof obj, "%s.run.o", stem);
|
||||
snprintf(exe, sizeof exe, "%s.exe", stem);
|
||||
snprintf(rt, sizeof rt, "%s/../lib/libwwrt.a", bin);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"%s/ww test -c -o %s test/wcc/data/attest_userrun.ww > /dev/null 2>&1",
|
||||
"%s/ww_ww test -c -o %s test/wcc/data/attest_userrun.ww > /dev/null 2>&1",
|
||||
bin, stem);
|
||||
runwait(cmd);
|
||||
if (access(comb, 0) != 0) {
|
||||
fprintf(stderr, "997 FAIL: ww build produced no %s\n", comb);
|
||||
fprintf(stderr, "997 FAIL: ww_ww build produced no %s\n", comb);
|
||||
return 1;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -T %s -o /dev/null 2>/dev/null",
|
||||
bin, comb);
|
||||
int rc = 0;
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "997 FAIL: w6c_ww -T accepted user `fn run` collision "
|
||||
"(expected duplicate-fn reject)\n");
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -T %s -o %s 2>/dev/null",
|
||||
bin, comb, asmf);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "997 FAIL: w6c_ww -T rejected user `fn run` coexist "
|
||||
"(expected accept post-#80)\n");
|
||||
rc = 1;
|
||||
}
|
||||
unlink(comb);
|
||||
if (rc == 0) {
|
||||
int nr = count_text(asmf, "run");
|
||||
int nt = count_text(asmf, "test.run");
|
||||
if (nr != 1 || nt != 1) {
|
||||
fprintf(stderr, "997 FAIL: w6c_ww -T asm label count run=%d "
|
||||
"test.run=%d (want 1/1) — user `fn run` not distinct from "
|
||||
"lib runner (a #84 dead-dup gives run=0/test.run=2)\n",
|
||||
nr, nt);
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c -T %s -o %s 2>/dev/null",
|
||||
bin, comb, csasm);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "997 FAIL: w6c -T (coexist cs==ww)\n");
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
char *bc = NULL, *bw = NULL;
|
||||
size_t nc = 0, nw = 0;
|
||||
if (slurp(csasm, &bc, &nc) < 0 || slurp(asmf, &bw, &nw) < 0) {
|
||||
fprintf(stderr, "997 FAIL: slurp coexist asm\n");
|
||||
rc = 1;
|
||||
} else if (nc != nw || memcmp(bc, bw, nc) != 0) {
|
||||
fprintf(stderr, "997 FAIL: coexist -T asm cs!=ww "
|
||||
"(cs %zu, ww %zu)\n", nc, nw);
|
||||
rc = 1;
|
||||
}
|
||||
free(bc); free(bw);
|
||||
}
|
||||
if (rc == 0) {
|
||||
snprintf(cmd, sizeof cmd, "%s/w6a_ww -o %s %s 2>/dev/null", bin, obj, asmf);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: coexist w6a_ww\n"); rc = 1; }
|
||||
}
|
||||
if (rc == 0) {
|
||||
snprintf(cmd, sizeof cmd, "%s/w6l_ww -o %s %s %s 2>/dev/null",
|
||||
bin, exe, obj, rt);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "997 FAIL: coexist w6l_ww\n"); rc = 1; }
|
||||
}
|
||||
if (rc == 0 && runwait(exe) != 0) {
|
||||
fprintf(stderr, "997 FAIL: coexist synth entry nonzero — user "
|
||||
"`fn run` hijacked the runner or the @test failed\n");
|
||||
rc = 1;
|
||||
}
|
||||
unlink(comb); unlink(asmf); unlink(csasm); unlink(obj); unlink(exe);
|
||||
char tmp[320];
|
||||
snprintf(tmp, sizeof tmp, "%s.s", stem); unlink(tmp);
|
||||
snprintf(tmp, sizeof tmp, "%s.o", stem); unlink(tmp);
|
||||
@@ -434,11 +513,11 @@ main(void)
|
||||
"use-before-value") != 0) return 1;
|
||||
if (modfn_run(bin, "test/wcc/data/modfn_coexist_vbu_ok.ww",
|
||||
"value-before-use") != 0) return 1;
|
||||
if (collide_run(bin) != 0) return 1;
|
||||
if (coexist_run(bin) != 0) return 1;
|
||||
|
||||
printf("@test -T (ww_ww): run ok + cs/ww byte-id + rejects + "
|
||||
"non-T @test drop cs/ww byte-id + checked-body (#6) + "
|
||||
"dup fn/type/def/let reject + xpkg/builtin-redecl byte-id + "
|
||||
"modfn coexist byte-id (#30) + fn-run collision (#23)\n");
|
||||
"modfn coexist byte-id (#30) + fn-run coexist (#80)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
// #23 -T collision — a user `fn run` collides with lib/test's bound
|
||||
// runner `run` (the synth's callee) once `ww test -c` bundles lib/test.
|
||||
// Both stages must loud-reject "duplicate fn run" (rc!=0), never silently
|
||||
// build a binary that calls the wrong run and skips every @test.
|
||||
fn run() void = { return; };
|
||||
// #80/#84 -T coexist — a user `fn run` COEXISTS with lib/test's bound
|
||||
// runner `run` (the synth main's callee). Once `ww test -c` bundles
|
||||
// lib/test, the synth `use test;` keys lib/test's `run` under module
|
||||
// "test" (#80: prepended before binding) and the user's package-less
|
||||
// `run` mangles to a DISTINCT bare `run` (#84) — no duplicate. Both
|
||||
// stages accept under -T and the synth runner runs the @test to exit 0;
|
||||
// the user `run` must NOT hijack the entry. Pre-#80 this loud-rejected
|
||||
// "duplicate fn run". The gate (910/997 coexist_run) static-label-counts
|
||||
// the -T asm — exactly 1 `TEXT run` (user, bare) + 1 `TEXT test.run` (lib
|
||||
// runner) — pinning #84 distinctness here in the REAL @test/-T path (a
|
||||
// dead-dup regression yields 0x run / 2x test.run). General callability of
|
||||
// the bare run is gated separately by 989_barefn_collide.
|
||||
fn run() i32 = { return 9; };
|
||||
|
||||
@test fn t_one() void = {
|
||||
assert(1 == 1);
|
||||
|
||||
Reference in New Issue
Block a user