wcc: drop @test fns from non-T builds, both stages (harec check.c:3941)
Splice @test N_FNDECLs out of the unit after the body-check passes, mirroring harec's checked-but-not-emitted: a broken @test body still errors loudly in non-T; @test-free units are emission-unchanged. 910/997 table rows pin keep/test x non-T/-T, head+consecutive unlink, undef-body reject, and plain-calls-dropped loud link-fail. w6c+wwdump combined.ww regen. (#6-team)
This commit is contained in:
@@ -3228,4 +3228,38 @@ check_file(Checker *c, Node *file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c->cur_mod = NULL;
|
c->cur_mod = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #6 harec-fidelity (ref/harec/src/check.c:3941): a @test fn is
|
||||||
|
* fully checked above — pass 2 walked its body like every fn — but
|
||||||
|
* is NOT emitted in a non-test build. harec skips append_decl for
|
||||||
|
* FN_TEST && !is_test, so the fn never reaches unit->declarations
|
||||||
|
* (the list codegen walks); the body is still type-checked, only the
|
||||||
|
* emission is dropped. ww shares one file->list across check + cgen
|
||||||
|
* (no separate checked-decl list, project_hare_ast_no_result), so we
|
||||||
|
* splice the already-checked @test fns out here, after pass 2 — they
|
||||||
|
* stay checked, never reach cg_file. The -T path is untouched: its
|
||||||
|
* synth main calls the @test fns, so they must remain. Prereq for
|
||||||
|
* in-package @test colocation (#9). Twin: selfhost/cmd/wcc/check.ww.
|
||||||
|
*/
|
||||||
|
if (!c->is_test) {
|
||||||
|
Node *prev = NULL;
|
||||||
|
for (Node *d = file->list; d; ) {
|
||||||
|
int istest = 0;
|
||||||
|
if (d->kind == N_FNDECL)
|
||||||
|
for (Node *at = d->attr; at; at = at->next)
|
||||||
|
if (at->str
|
||||||
|
&& strcmp(at->str, "test") == 0) {
|
||||||
|
istest = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Node *nx = d->next;
|
||||||
|
if (istest) {
|
||||||
|
if (prev == NULL) file->list = nx;
|
||||||
|
else prev->next = nx;
|
||||||
|
} else
|
||||||
|
prev = d;
|
||||||
|
d = nx;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15996,6 +15996,42 @@ export fn checkfile(c: *checker, file: *node) void = {
|
|||||||
d = d.next;
|
d = d.next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// #6 harec-fidelity (ref/harec/src/check.c:3941): a @test fn is fully
|
||||||
|
// checked above (pass 2 + pass 3 walked it like every fn) but is NOT
|
||||||
|
// emitted in a non-test build. harec skips append_decl for
|
||||||
|
// FN_TEST && !is_test, so the fn never reaches the codegen decl list;
|
||||||
|
// the body is still checked, only the emission is dropped. ww shares
|
||||||
|
// one file.list across check + cgen (no separate checked-decl list),
|
||||||
|
// so we splice the already-checked @test fns out here, after all
|
||||||
|
// passes — they stay checked, never reach cgen. The -T path is
|
||||||
|
// untouched: its synth main calls the @test fns, so they must remain.
|
||||||
|
// Twin: cmd/wcc/check.c.
|
||||||
|
if (c.istest == 0) {
|
||||||
|
let prev: *node = nil;
|
||||||
|
let e: *node = file.list;
|
||||||
|
for (e != nil) {
|
||||||
|
let istest: bool = false;
|
||||||
|
if (e.kind == nkind.N_FNDECL) {
|
||||||
|
let at: *node = e.attr;
|
||||||
|
for (at != nil) {
|
||||||
|
if (at.kind == nkind.N_ATTR
|
||||||
|
&& streq(at.str, "test")) {
|
||||||
|
istest = true;
|
||||||
|
};
|
||||||
|
at = at.next;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let nx: *node = e.next;
|
||||||
|
if (istest) {
|
||||||
|
if (prev == nil) { file.list = nx; }
|
||||||
|
else { prev.next = nx; };
|
||||||
|
} else {
|
||||||
|
prev = e;
|
||||||
|
};
|
||||||
|
e = nx;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
let empty: str;
|
let empty: str;
|
||||||
c.curmod = empty;
|
c.curmod = empty;
|
||||||
|
|
||||||
|
|||||||
@@ -5664,6 +5664,42 @@ export fn checkfile(c: *checker, file: *node) void = {
|
|||||||
d = d.next;
|
d = d.next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// #6 harec-fidelity (ref/harec/src/check.c:3941): a @test fn is fully
|
||||||
|
// checked above (pass 2 + pass 3 walked it like every fn) but is NOT
|
||||||
|
// emitted in a non-test build. harec skips append_decl for
|
||||||
|
// FN_TEST && !is_test, so the fn never reaches the codegen decl list;
|
||||||
|
// the body is still checked, only the emission is dropped. ww shares
|
||||||
|
// one file.list across check + cgen (no separate checked-decl list),
|
||||||
|
// so we splice the already-checked @test fns out here, after all
|
||||||
|
// passes — they stay checked, never reach cgen. The -T path is
|
||||||
|
// untouched: its synth main calls the @test fns, so they must remain.
|
||||||
|
// Twin: cmd/wcc/check.c.
|
||||||
|
if (c.istest == 0) {
|
||||||
|
let prev: *node = nil;
|
||||||
|
let e: *node = file.list;
|
||||||
|
for (e != nil) {
|
||||||
|
let istest: bool = false;
|
||||||
|
if (e.kind == nkind.N_FNDECL) {
|
||||||
|
let at: *node = e.attr;
|
||||||
|
for (at != nil) {
|
||||||
|
if (at.kind == nkind.N_ATTR
|
||||||
|
&& streq(at.str, "test")) {
|
||||||
|
istest = true;
|
||||||
|
};
|
||||||
|
at = at.next;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let nx: *node = e.next;
|
||||||
|
if (istest) {
|
||||||
|
if (prev == nil) { file.list = nx; }
|
||||||
|
else { prev.next = nx; };
|
||||||
|
} else {
|
||||||
|
prev = e;
|
||||||
|
};
|
||||||
|
e = nx;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
let empty: str;
|
let empty: str;
|
||||||
c.curmod = empty;
|
c.curmod = empty;
|
||||||
|
|
||||||
|
|||||||
@@ -15996,6 +15996,42 @@ export fn checkfile(c: *checker, file: *node) void = {
|
|||||||
d = d.next;
|
d = d.next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// #6 harec-fidelity (ref/harec/src/check.c:3941): a @test fn is fully
|
||||||
|
// checked above (pass 2 + pass 3 walked it like every fn) but is NOT
|
||||||
|
// emitted in a non-test build. harec skips append_decl for
|
||||||
|
// FN_TEST && !is_test, so the fn never reaches the codegen decl list;
|
||||||
|
// the body is still checked, only the emission is dropped. ww shares
|
||||||
|
// one file.list across check + cgen (no separate checked-decl list),
|
||||||
|
// so we splice the already-checked @test fns out here, after all
|
||||||
|
// passes — they stay checked, never reach cgen. The -T path is
|
||||||
|
// untouched: its synth main calls the @test fns, so they must remain.
|
||||||
|
// Twin: cmd/wcc/check.c.
|
||||||
|
if (c.istest == 0) {
|
||||||
|
let prev: *node = nil;
|
||||||
|
let e: *node = file.list;
|
||||||
|
for (e != nil) {
|
||||||
|
let istest: bool = false;
|
||||||
|
if (e.kind == nkind.N_FNDECL) {
|
||||||
|
let at: *node = e.attr;
|
||||||
|
for (at != nil) {
|
||||||
|
if (at.kind == nkind.N_ATTR
|
||||||
|
&& streq(at.str, "test")) {
|
||||||
|
istest = true;
|
||||||
|
};
|
||||||
|
at = at.next;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let nx: *node = e.next;
|
||||||
|
if (istest) {
|
||||||
|
if (prev == nil) { file.list = nx; }
|
||||||
|
else { prev.next = nx; };
|
||||||
|
} else {
|
||||||
|
prev = e;
|
||||||
|
};
|
||||||
|
e = nx;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
let empty: str;
|
let empty: str;
|
||||||
c.curmod = empty;
|
c.curmod = empty;
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,132 @@ static const struct {
|
|||||||
{ "test/wcc/data/attest_badsig.ww", "non-void @test" },
|
{ "test/wcc/data/attest_badsig.ww", "non-void @test" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #6: each row asserts whether a symbol's TEXT def appears in the asm
|
||||||
|
* `<comp>` emits for attest_nondrop.ww in a given mode. A @test fn is
|
||||||
|
* spliced out of a non-test build (harec ref/harec/src/check.c:3941 —
|
||||||
|
* checked but never emitted) and kept under -T (the synth entry calls
|
||||||
|
* it); a plain fn is always emitted.
|
||||||
|
*/
|
||||||
|
static const struct {
|
||||||
|
const char *sym; /* TEXT label to grep for */
|
||||||
|
int testmode; /* 1 => -T, 0 => plain build */
|
||||||
|
int present; /* expected: 1 present, 0 absent */
|
||||||
|
const char *what;
|
||||||
|
} nondrop_rows[] = {
|
||||||
|
{ "data.nondrop_keep", 0, 1, "plain fn kept non-T" },
|
||||||
|
{ "data.nondrop_test_a", 0, 0, "head @test dropped non-T" },
|
||||||
|
{ "data.nondrop_test_b", 0, 0, "mid @test dropped non-T" },
|
||||||
|
{ "data.nondrop_test_c", 0, 0, "consecutive @test dropped non-T" },
|
||||||
|
{ "data.nondrop_keep", 1, 1, "plain fn kept under -T" },
|
||||||
|
{ "data.nondrop_test_a", 1, 1, "head @test kept under -T" },
|
||||||
|
{ "data.nondrop_test_b", 1, 1, "mid @test kept under -T" },
|
||||||
|
{ "data.nondrop_test_c", 1, 1, "consecutive @test kept under -T" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* nondrop — compile attest_nondrop.ww plain and `-T`, then assert each
|
||||||
|
* row's TEXT def is present/absent. */
|
||||||
|
static int
|
||||||
|
nondrop(const char *bin, const char *comp)
|
||||||
|
{
|
||||||
|
int pid = getpid();
|
||||||
|
char plain[256], tee[256], cmd[4096];
|
||||||
|
snprintf(plain, sizeof plain, "/tmp/at910nd_%s_p_%d.s", comp, pid);
|
||||||
|
snprintf(tee, sizeof tee, "/tmp/at910nd_%s_t_%d.s", comp, pid);
|
||||||
|
|
||||||
|
snprintf(cmd, sizeof cmd,
|
||||||
|
"%s/%s test/wcc/data/attest_nondrop.ww -o %s 2>/dev/null",
|
||||||
|
bin, comp, plain);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "910 FAIL: %s non-T nondrop compile\n", comp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
snprintf(cmd, sizeof cmd,
|
||||||
|
"%s/%s -T test/wcc/data/attest_nondrop.ww -o %s 2>/dev/null",
|
||||||
|
bin, comp, tee);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "910 FAIL: %s -T nondrop compile\n", comp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = 0;
|
||||||
|
for (size_t i = 0; i < sizeof nondrop_rows / sizeof nondrop_rows[0]; i++) {
|
||||||
|
const char *f = nondrop_rows[i].testmode ? tee : plain;
|
||||||
|
snprintf(cmd, sizeof cmd, "grep -q '^TEXT %s,' %s",
|
||||||
|
nondrop_rows[i].sym, f);
|
||||||
|
int found = runwait(cmd) == 0;
|
||||||
|
if (found != nondrop_rows[i].present) {
|
||||||
|
fprintf(stderr, "910 FAIL: %s — %s: %s %s (expected %s)\n",
|
||||||
|
comp, nondrop_rows[i].what, nondrop_rows[i].sym,
|
||||||
|
found ? "present" : "absent",
|
||||||
|
nondrop_rows[i].present ? "present" : "absent");
|
||||||
|
rc = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unlink(plain); unlink(tee);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reject_plain — `<comp> <fixture>` (NON-T) must exit nonzero. Pins that
|
||||||
|
* a @test body is type-checked before the #6 splice drops it: an
|
||||||
|
* undefined symbol in the body is caught loud even though the fn never
|
||||||
|
* reaches codegen (harec checks at :3913, drops at :3941). */
|
||||||
|
static int
|
||||||
|
reject_plain(const char *bin, const char *comp, const char *fixture,
|
||||||
|
const char *what)
|
||||||
|
{
|
||||||
|
char cmd[4096];
|
||||||
|
snprintf(cmd, sizeof cmd, "%s/%s %s -o /dev/null 2>/dev/null",
|
||||||
|
bin, comp, fixture);
|
||||||
|
if (runwait(cmd) == 0) {
|
||||||
|
fprintf(stderr, "910 FAIL: %s (non-T) accepted %s "
|
||||||
|
"(expected reject)\n", comp, what);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* linkfail — a plain fn calling a @test fn, built non-T: compile +
|
||||||
|
* assemble succeed, but the link MUST fail (the dropped @test def leaves
|
||||||
|
* the call's symbol dangling — harec-faithful loud failure, never a
|
||||||
|
* silent mis-link). Linker behavior is stage-independent, so cstage
|
||||||
|
* suffices; 997's byte-id proves the wwstage .s is identical. */
|
||||||
|
static int
|
||||||
|
linkfail(const char *bin, const char *comp)
|
||||||
|
{
|
||||||
|
int pid = getpid();
|
||||||
|
char asmf[256], obj[256], exe[256], rt[1024], cmd[4096];
|
||||||
|
snprintf(asmf, sizeof asmf, "/tmp/at910lf_%s_%d.s", comp, pid);
|
||||||
|
snprintf(obj, sizeof obj, "/tmp/at910lf_%s_%d.o", comp, pid);
|
||||||
|
snprintf(exe, sizeof exe, "/tmp/at910lf_%s_%d.exe", comp, pid);
|
||||||
|
snprintf(rt, sizeof rt, "%s/../lib/libwwrt.a", bin);
|
||||||
|
|
||||||
|
snprintf(cmd, sizeof cmd,
|
||||||
|
"%s/%s test/wcc/data/attest_calldropped.ww -o %s 2>/dev/null",
|
||||||
|
bin, comp, asmf);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "910 FAIL: %s non-T calldropped compile\n", comp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s 2>/dev/null", bin, obj, asmf);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "910 FAIL: w6a calldropped\n");
|
||||||
|
unlink(asmf);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s %s 2>/dev/null",
|
||||||
|
bin, exe, obj, rt);
|
||||||
|
int rc = 0;
|
||||||
|
if (runwait(cmd) == 0) {
|
||||||
|
fprintf(stderr, "910 FAIL: calldropped linked (expected "
|
||||||
|
"undefined-reference to the dropped @test sym)\n");
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
unlink(asmf); unlink(obj); unlink(exe);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
@@ -131,7 +257,12 @@ main(void)
|
|||||||
for (size_t i = 0; i < sizeof rejects / sizeof rejects[0]; i++)
|
for (size_t i = 0; i < sizeof rejects / sizeof rejects[0]; i++)
|
||||||
if (reject(bin, "w6c", rejects[i].fixture, rejects[i].what) != 0)
|
if (reject(bin, "w6c", rejects[i].fixture, rejects[i].what) != 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
if (nondrop(bin, "w6c") != 0) return 1;
|
||||||
|
if (reject_plain(bin, "w6c", "test/wcc/data/attest_undefbody.ww",
|
||||||
|
"undefined symbol in @test body") != 0) return 1;
|
||||||
|
if (linkfail(bin, "w6c") != 0) return 1;
|
||||||
|
|
||||||
printf("@test -T: run ok + user-main and bad-signature rejected\n");
|
printf("@test -T: run ok + user-main and bad-signature rejected + "
|
||||||
|
"non-T @test drop + checked-body + dangling-call link-fail (#6)\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -292,8 +292,8 @@ check_one(const char *bin, const char *cwd, const struct ent *e, int idx)
|
|||||||
snprintf(cs, sizeof cs, "%s/c.s", td);
|
snprintf(cs, sizeof cs, "%s/c.s", td);
|
||||||
snprintf(ws, sizeof ws, "%s/w.s", td);
|
snprintf(ws, sizeof ws, "%s/w.s", td);
|
||||||
/* Fixtures are main-less @test files: -T synthesizes the entry and
|
/* Fixtures are main-less @test files: -T synthesizes the entry and
|
||||||
* keeps the @test fns symmetrically (non-T drops them cstage-side,
|
* keeps the @test fns symmetrically (non-T drops them on both stages,
|
||||||
* task #6, so byte-id needs -T). Import-probes carry their own
|
* #6, so byte-id needs -T). Import-probes carry their own
|
||||||
* `fn main()`, which -T loud-rejects (910), so they stay non-T. */
|
* `fn main()`, which -T loud-rejects (910), so they stay non-T. */
|
||||||
const char *tflag = e->fixture ? "-T " : "";
|
const char *tflag = e->fixture ? "-T " : "";
|
||||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/w6c %s%s > %s 2>/dev/null",
|
snprintf(cmd, sizeof cmd, "timeout 180 %s/w6c %s%s > %s 2>/dev/null",
|
||||||
|
|||||||
@@ -156,6 +156,104 @@ static const struct {
|
|||||||
{ "test/wcc/data/attest_badsig.ww", "non-void @test" },
|
{ "test/wcc/data/attest_badsig.ww", "non-void @test" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* #6: TEXT-def present/absent rows for attest_nondrop.ww (mirror 910). */
|
||||||
|
static const struct {
|
||||||
|
const char *sym;
|
||||||
|
int testmode; /* 1 => -T, 0 => plain build */
|
||||||
|
int present;
|
||||||
|
const char *what;
|
||||||
|
} nondrop_rows[] = {
|
||||||
|
{ "data.nondrop_keep", 0, 1, "plain fn kept non-T" },
|
||||||
|
{ "data.nondrop_test_a", 0, 0, "head @test dropped non-T" },
|
||||||
|
{ "data.nondrop_test_b", 0, 0, "mid @test dropped non-T" },
|
||||||
|
{ "data.nondrop_test_c", 0, 0, "consecutive @test dropped non-T" },
|
||||||
|
{ "data.nondrop_keep", 1, 1, "plain fn kept under -T" },
|
||||||
|
{ "data.nondrop_test_a", 1, 1, "head @test kept under -T" },
|
||||||
|
{ "data.nondrop_test_b", 1, 1, "mid @test kept under -T" },
|
||||||
|
{ "data.nondrop_test_c", 1, 1, "consecutive @test kept under -T" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* nondrop — drive the #6 splice through w6c_ww (rows) AND assert the
|
||||||
|
* non-T asm is cs/ww byte-identical (rule 10: the drop must be symmetric,
|
||||||
|
* not just cstage-side — 997's byteid above only covered -T mode). */
|
||||||
|
static int
|
||||||
|
nondrop(const char *bin)
|
||||||
|
{
|
||||||
|
int pid = getpid();
|
||||||
|
char wp[256], wt[256], cp[256], cmd[4096];
|
||||||
|
snprintf(wp, sizeof wp, "/tmp/at997nd_wp_%d.s", pid);
|
||||||
|
snprintf(wt, sizeof wt, "/tmp/at997nd_wt_%d.s", pid);
|
||||||
|
snprintf(cp, sizeof cp, "/tmp/at997nd_cp_%d.s", pid);
|
||||||
|
|
||||||
|
snprintf(cmd, sizeof cmd,
|
||||||
|
"%s/w6c_ww test/wcc/data/attest_nondrop.ww -o %s 2>/dev/null", bin, wp);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "997 FAIL: w6c_ww non-T nondrop compile\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
snprintf(cmd, sizeof cmd,
|
||||||
|
"%s/w6c_ww -T test/wcc/data/attest_nondrop.ww -o %s 2>/dev/null", bin, wt);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "997 FAIL: w6c_ww -T nondrop compile\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
snprintf(cmd, sizeof cmd,
|
||||||
|
"%s/w6c test/wcc/data/attest_nondrop.ww -o %s 2>/dev/null", bin, cp);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "997 FAIL: w6c non-T nondrop compile\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = 0;
|
||||||
|
for (size_t i = 0; i < sizeof nondrop_rows / sizeof nondrop_rows[0]; i++) {
|
||||||
|
const char *f = nondrop_rows[i].testmode ? wt : wp;
|
||||||
|
snprintf(cmd, sizeof cmd, "grep -q '^TEXT %s,' %s",
|
||||||
|
nondrop_rows[i].sym, f);
|
||||||
|
int found = runwait(cmd) == 0;
|
||||||
|
if (found != nondrop_rows[i].present) {
|
||||||
|
fprintf(stderr, "997 FAIL: w6c_ww — %s: %s %s (expected %s)\n",
|
||||||
|
nondrop_rows[i].what, nondrop_rows[i].sym,
|
||||||
|
found ? "present" : "absent",
|
||||||
|
nondrop_rows[i].present ? "present" : "absent");
|
||||||
|
rc = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rc == 0) {
|
||||||
|
char *bc = NULL, *bw = NULL;
|
||||||
|
size_t nc = 0, nw = 0;
|
||||||
|
if (slurp(cp, &bc, &nc) < 0 || slurp(wp, &bw, &nw) < 0) {
|
||||||
|
fprintf(stderr, "997 FAIL: slurp non-T nondrop asm\n");
|
||||||
|
rc = 1;
|
||||||
|
} else if (nc != nw || memcmp(bc, bw, nc) != 0) {
|
||||||
|
fprintf(stderr, "997 FAIL: non-T @test-drop asm differs "
|
||||||
|
"(cs %zu, ww %zu)\n", nc, nw);
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
free(bc); free(bw);
|
||||||
|
}
|
||||||
|
unlink(wp); unlink(wt); unlink(cp);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reject_plain — `w6c_ww <fixture>` (NON-T) must exit nonzero. Pins (as
|
||||||
|
* 910 does for cstage) that wwstage type-checks a @test body before the
|
||||||
|
* #6 splice drops it: an undefined symbol is caught loud even though the
|
||||||
|
* fn never reaches codegen (checked at check.ww pass 2/3, dropped after). */
|
||||||
|
static int
|
||||||
|
reject_plain(const char *bin, const char *fixture, const char *what)
|
||||||
|
{
|
||||||
|
char cmd[4096];
|
||||||
|
snprintf(cmd, sizeof cmd, "%s/w6c_ww %s -o /dev/null 2>/dev/null",
|
||||||
|
bin, fixture);
|
||||||
|
if (runwait(cmd) == 0) {
|
||||||
|
fprintf(stderr, "997 FAIL: w6c_ww (non-T) accepted %s "
|
||||||
|
"(expected reject)\n", what);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
@@ -167,7 +265,11 @@ main(void)
|
|||||||
for (size_t i = 0; i < sizeof rejects / sizeof rejects[0]; i++)
|
for (size_t i = 0; i < sizeof rejects / sizeof rejects[0]; i++)
|
||||||
if (reject(bin, rejects[i].fixture, rejects[i].what) != 0)
|
if (reject(bin, rejects[i].fixture, rejects[i].what) != 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
if (nondrop(bin) != 0) return 1;
|
||||||
|
if (reject_plain(bin, "test/wcc/data/attest_undefbody.ww",
|
||||||
|
"undefined symbol in @test body") != 0) return 1;
|
||||||
|
|
||||||
printf("@test -T (ww_ww): run ok + cs/ww byte-id + rejects ok\n");
|
printf("@test -T (ww_ww): run ok + cs/ww byte-id + rejects + "
|
||||||
|
"non-T @test drop cs/ww byte-id + checked-body (#6)\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
18
test/wcc/data/attest_calldropped.ww
Normal file
18
test/wcc/data/attest_calldropped.ww
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// #6 fixture: a plain fn (the entry) CALLS a @test fn. In non-T the
|
||||||
|
// @test def is spliced out but the CALL survives — harec-faithful: the
|
||||||
|
// reference is left DANGLING and fails LOUDLY at link, never a silent
|
||||||
|
// mis-link (ref/harec/src/check.c:3941 drops the decl; the call site's
|
||||||
|
// symbol is never satisfied). Built non-T then linked: the link MUST
|
||||||
|
// fail with an undefined reference to the dropped @test symbol (910
|
||||||
|
// linkfail probe). No -T variant — a user main is -T-rejected (910).
|
||||||
|
|
||||||
|
package data;
|
||||||
|
|
||||||
|
@test fn calldropped_test() void = {
|
||||||
|
let a: i32 = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
export fn main() i32 = {
|
||||||
|
calldropped_test();
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
28
test/wcc/data/attest_nondrop.ww
Normal file
28
test/wcc/data/attest_nondrop.ww
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// #6 fixture: non-(-T) @test drop, harec-fidelity (ref/harec/src/
|
||||||
|
// check.c:3941 — FN_TEST && !is_test is checked but not emitted). A
|
||||||
|
// normal fn must survive a non-test build; uncalled @test fns must NOT
|
||||||
|
// reach codegen. Under -T the synth entry calls the @test fns, so their
|
||||||
|
// TEXT defs reappear. No imports — compiles straight to asm in both
|
||||||
|
// modes for a TEXT-label grep (910/997 nondrop rows).
|
||||||
|
//
|
||||||
|
// Layout exercises the splice loop's unlink edges: a @test fn FIRST
|
||||||
|
// (prev==nil head-unlink), a surviving plain fn, then TWO consecutive
|
||||||
|
// @test fns (unlink-after-unlink). All three @test defs vanish non-T.
|
||||||
|
|
||||||
|
package data;
|
||||||
|
|
||||||
|
@test fn nondrop_test_a() void = {
|
||||||
|
let a: i32 = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn nondrop_keep() i32 = {
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
@test fn nondrop_test_b() void = {
|
||||||
|
let a: i32 = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
@test fn nondrop_test_c() void = {
|
||||||
|
let a: i32 = 1;
|
||||||
|
};
|
||||||
17
test/wcc/data/attest_undefbody.ww
Normal file
17
test/wcc/data/attest_undefbody.ww
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// #6 fixture: a @test body is type-checked in a non-test build even
|
||||||
|
// though the fn is then spliced out. harec checks the body
|
||||||
|
// (ref/harec/src/check.c:3913) BEFORE the append_decl skip (:3941), so
|
||||||
|
// the splice is check-THEN-drop, never drop-then-skip-check. Pins that
|
||||||
|
// placement decision: an undefined symbol in the @test body must be
|
||||||
|
// caught LOUDLY in non-T (a pre-pass splice would silence it). Compiled
|
||||||
|
// WITHOUT -T (910/997 plain-reject row).
|
||||||
|
|
||||||
|
package data;
|
||||||
|
|
||||||
|
fn keep() i32 = {
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
@test fn undefbody_test() void = {
|
||||||
|
undefined_symbol_xyz();
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user