Files
ww/test/wcc/989_declns_sep.c
Hojun-Cho 939d0938e2 test: re-cite retired-mechanism comments in surviving carriers
The migrate-and-retire audit left the surviving carriers as the only
files still describing the retired combined.ww amalgamator as live:
m2wwi/m3sep headers claimed .wwi was dead code, four byteid carriers
carried dead cleanup of never-written .combined.ww intermediates, and
sepbuild's enumeration mirror plus lib_byteid's completeness scan
still skipped the retired artifact form (the same silent
accommodation just removed from both drivers — dropping it here
surfaced and flushed 39 stale untracked amalgamator outputs across
lib/, which the gates now reject loudly). Remaining mentions are
past-tense history or quoted diagnostics.
2026-08-08 01:34:05 +09:00

309 lines
10 KiB
C

/*
* 989_declns_sep — #23/#30 declaration-namespace + module-fn coexistence
* under the directory-package model (task #83, M4 E2-C2c).
* The flat duplicate and builtin runtime rows now belong to r989_declns_*
* (with duplicate-type already owned by runww_dup_type_reject). This
* residual retains only the modfn separate-package artifact leg.
*
* Re-hosts every decl-namespace assertion that once lived ONLY in
* 910/997 onto a gate that survives the M4 flip (910/997 were deleted
* at the flip — they fed the retired amalgamation inputs). Three legs:
*
* dup family — a duplicate top-level fn/type/def/let in one (flat)
* module must build-FAIL on BOTH stages. The fixtures
* are flat single-package (the surviving shape) and feed
* `w6c` / `w6c_ww` DIRECTLY — no driver resolution; the
* dup is an install-pass property, not a build-model one.
* 712 covered the cstage let arm; 997 covered the wwstage
* arm — this gate asserts BOTH stages reject every kind,
* closing the ww arm that dies with 997.
* builtin_redecl— the carve-out: a user redecl of a pre-seeded builtin
* name (`nomem`) is NOT a duplicate. cstage keeps no
* builtins in scope (lookup_builtin wins first), wwstage
* seeds them into c.top so installtop must DROP the redecl
* rather than error. Both stages accept + run to 7.
* modfn coexist — a top-level value-namespace `fn aa` and an imported
* MODULE `aa` coexist: bare `aa()` binds the fn (a module
* is not callable), `aa.helper()` binds through the module.
* RESHAPED from the dying multi-`package`-single-file
* 910/997 fixtures to dir-packages (696 precedent), built
* via `ww build` + run. The _vbu twin flips the decl
* order (`fn aa` BEFORE `import aa`): cstage installs every
* SK_USE in an order-independent first pass, wwstage in
* source order, so vbu exercises a DISTINCT promote path —
* its asm must be byte-identical to the use-before-value
* order, the teeth against silent order-dependence.
*
* dup_xpkg (same leaf in distinct packages = legal) RIDES on 696/697's
* dir-package coverage — no port (spec C2c).
*
* Every modfn intermediate is `-o`-redirected to /tmp for isolation; the dup +
* builtin legs feed w6c directly with `-o /tmp`. Models 989_sepbuild_run +
* 911_attest_drop conventions; 989 prefix per the sep-gate precedent (the
* 7xx cgen/check range is exhausted).
*/
#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;
}
/* 0 if `needle` occurs in the file at `path`, else 1 (or -1 on read err). */
static int
file_contains(const char *path, const char *needle)
{
char *b = NULL;
size_t n = 0;
if (slurp(path, &b, &n) < 0) return -1;
int found = (strstr(b, needle) != NULL);
free(b);
return found ? 0 : 1;
}
/*
* Duplicate-declaration and builtin-redeclaration runtime ownership moved to
* r989_declns_* fixtures. This wrapper retains only sep/package artifacts.
*/
/*
* modfn coexist — the two dir-package layouts. Build each via `ww build
* ` + run, both driver stages, expect exit 6 (aa()=1 + aa.helper()=5).
* `ww build` (NOT `ww run`) per spec: run is not sep-wired until the driver
* flip. Returns 0 on success; leaves <prog>.sepwork in `td` for the byte-id
* compares below.
*/
struct modfn { const char *lay; const char *fixture; };
static const struct modfn modfn_lays[] = {
{ "coexist", "test/wcc/data/modfn_coexist/main.ww" },
{ "coexist_vbu", "test/wcc/data/modfn_coexist_vbu/main.ww" },
};
/* per-package output the sep driver materializes; the root carries no .wwi
* (post-#69 it is compiled without -I), so its suffix set omits .wwi. */
static const struct {
const char *pkg;
const char *suf;
} parts[] = {
{ "aa", ".s" },
{ "aa", ".wwi" },
{ "aa", ".unit.ww" },
{ "__root", ".s" },
{ "__root", ".unit.ww" },
};
static int
modfn_build(const char *bin, const char *td)
{
char cmd[4096], prog[1024];
int rc = 0;
for (size_t l = 0; l < sizeof modfn_lays / sizeof modfn_lays[0]; l++) {
for (size_t s = 0; s < 2; s++) {
const char *drv = (s == 0) ? "ww" : "ww_ww";
snprintf(prog, sizeof prog, "%s/%s.%s", td, modfn_lays[l].lay,
(s == 0) ? "cs" : "ww");
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s 2>/dev/null",
bin, drv, prog, modfn_lays[l].fixture);
if (runwait(cmd) != 0) {
fprintf(stderr, "declns FAIL: %s build %s\n", drv,
modfn_lays[l].lay);
rc = 1;
continue;
}
int got = runwait(prog);
if (got != 6) {
fprintf(stderr, "declns FAIL: modfn %s (%s) exit=%d want=6 "
"(coexistence mis-resolved)\n", modfn_lays[l].lay, drv, got);
rc = 1;
}
}
}
return rc;
}
/* cs==ww (rule 10): for one layout, every per-package .s/.wwi/.unit.ww the
* cstage and wwstage sep drivers emitted must be byte-identical. */
static int
modfn_cs_eq_ww(const char *td, const char *lay)
{
int rc = 0;
for (size_t p = 0; p < sizeof parts / sizeof parts[0]; p++) {
char a[1024], b[1024];
snprintf(a, sizeof a, "%s/%s.cs.sepwork/%s%s", td, lay, parts[p].pkg, parts[p].suf);
snprintf(b, sizeof b, "%s/%s.ww.sepwork/%s%s", td, lay, parts[p].pkg, parts[p].suf);
if (files_eq(a, b) != 0) {
fprintf(stderr, "declns FAIL: %s cs!=ww for %s%s (rule 10)\n",
lay, parts[p].pkg, parts[p].suf);
rc = 1;
}
}
return rc;
}
/* cross-order byte-id: the value-before-use layout must emit asm/interface
* byte-identical to the use-before-value layout (same stage). Silent
* order-dependence in the SK_USE/value-sym promote is exactly what regresses;
* a divergence here is the teeth. */
static int
modfn_order_id(const char *td)
{
int rc = 0;
const char *tags[] = { "cs", "ww" };
for (size_t t = 0; t < sizeof tags / sizeof tags[0]; t++) {
for (size_t p = 0; p < sizeof parts / sizeof parts[0]; p++) {
/* `.unit.ww` is the driver's verbatim source-assembly
* intermediate — it embeds the two layouts' differently
* ORDERED source text, so it differs by construction. The
* order-independence claim is about CODEGEN, so it is tested
* on the compiled `.s`/`.wwi` only. */
if (strcmp(parts[p].suf, ".unit.ww") == 0) continue;
char a[1024], b[1024];
snprintf(a, sizeof a, "%s/coexist.%s.sepwork/%s%s",
td, tags[t], parts[p].pkg, parts[p].suf);
snprintf(b, sizeof b, "%s/coexist_vbu.%s.sepwork/%s%s",
td, tags[t], parts[p].pkg, parts[p].suf);
if (files_eq(a, b) != 0) {
fprintf(stderr, "declns FAIL: vbu!=non-vbu for %s%s (%s) — "
"silent decl-order dependence\n", parts[p].pkg,
parts[p].suf, tags[t]);
rc = 1;
}
}
}
return rc;
}
/* non-vacuity: __root.s must carry BOTH calls distinctly — through the
* module (`aa.helper`) and to the local value fn (`main.aa`) — proving the
* two namespaces resolved to different symbols rather than collapsing. */
static int
modfn_coexist_present(const char *td)
{
int rc = 0;
const char *tags[] = { "cs", "ww" };
for (size_t t = 0; t < sizeof tags / sizeof tags[0]; t++) {
char rs[1024];
snprintf(rs, sizeof rs, "%s/coexist.%s.sepwork/__root.s", td, tags[t]);
if (file_contains(rs, "CALL\taa.helper(SB)") != 0) {
fprintf(stderr, "declns FAIL: __root.s (%s) lacks module-qualified "
"`CALL aa.helper(SB)`\n", tags[t]);
rc = 1;
}
if (file_contains(rs, "CALL\tmain.aa(SB)") != 0) {
fprintf(stderr, "declns FAIL: __root.s (%s) lacks local-fn "
"`CALL main.aa(SB)`\n", tags[t]);
rc = 1;
}
}
return rc;
}
int
main(void)
{
const char *bin = absbin();
if (!bin) { fprintf(stderr, "declns FAIL: getcwd\n"); return 1; }
int fail = 0;
char td[64], cmd[256];
snprintf(td, sizeof td, "/tmp/wwdeclns_%d", getpid());
if (mkdir(td, 0755) != 0) {
perror(td);
return 1;
}
fail += modfn_build(bin, td);
fail += modfn_cs_eq_ww(td, "coexist");
fail += modfn_cs_eq_ww(td, "coexist_vbu");
fail += modfn_order_id(td);
fail += modfn_coexist_present(td);
const char *tags[] = { "cs", "ww" };
for (size_t l = 0; l < sizeof modfn_lays / sizeof modfn_lays[0]; l++) {
for (size_t s = 0; s < sizeof tags / sizeof tags[0]; s++) {
char prog[1024], work[1100];
snprintf(prog, sizeof prog, "%s/%s.%s", td,
modfn_lays[l].lay, tags[s]);
if (unlink(prog) != 0 && errno != ENOENT) {
perror(prog);
fail++;
}
snprintf(work, sizeof work, "%s.sepwork", prog);
snprintf(cmd, sizeof cmd, "rm -rf %s", work);
if (runwait(cmd) != 0) {
fprintf(stderr, "declns: cleanup failed: %s\n", work);
fail++;
}
}
}
if (rmdir(td) != 0) {
perror(td);
fail++;
}
if (fail) {
fprintf(stderr, "declns: %d check(s) failed\n", fail);
return 1;
}
printf("declns: modfn dir-pkg coexist→6 (cs==ww, "
"vbu byte-id to use-before-value), both stages (#23/#30)\n");
return 0;
}