wcc/ww: path-qualify exported decls + drop exact-or-bare value mangle (#53)
Under M1 mangling, EXPORTED non-fn decls (let/def/type) skipped path- qualification and emitted a BARE symbol (`types.I64_MAX` -> `I64_MAX`). Under separate compilation two packages exporting the same data leaf would then collide at w6l. Masked in-tree only because no two packages export the same non-fn leaf. §7-A (USER-locked, harec's model): path-qualify EVERY exported decl (fn AND data) at the single mangle choke-point — mod_collect / collectmods. Retire the `!isfn && d->export` (cstage) and `exported==0` (wwstage) skips: every decl with a module now mangles `<mod>.<name>`. The ONLY bare symbols left are @symbol FFI overrides (ffi_resolve at emit) and the ROOT unit's `main` — both already carved out before the map insert. With exported decls in the map the exact-(name,hint)-or-bare value dance is dead — its sole purpose was the bare-exported case. Delete mod_lookup_value / mod_mangle_value / mahint (cstage) and modlookupvalue / emitsymnamehint (wwstage); the value-global sites now route through the same hint-aware-with-fallback lookup as fns (mod_mangle_fn/mafn, emitfnname). Net negative LOC in the mangler. Transparent rename on the live combined path: ref and def move in lockstep, so cs==ww byte-id holds and the self-host still builds + runs (fixed-point/995). Byte-id REBASELINE — all 5 ww binaries shift. The w6c/wwdump combined.ww embed wcc cgen and are regenerated.
This commit is contained in:
@@ -208,6 +208,34 @@ static const char *sroot_src =
|
||||
" return (a[0]: i32) + (b[4]: i32);\n"
|
||||
"};\n";
|
||||
|
||||
/* #53 LINK leg: two packages each EXPORT the SAME-named non-fn leaf (`let v`,
|
||||
* `def K`). Pre-#53 exported non-fn decls skipped path-qualification and
|
||||
* emitted a BARE symbol, so linking cea+ceb clashed (w6l: duplicate symbol
|
||||
* v/K) — the rest of the suite never caught it because no two in-tree packages
|
||||
* export the same non-fn leaf. With §7-A every exported decl path-qualifies
|
||||
* (cea.v/ceb.v, cea.K/ceb.K) so the link is clean and each getter reads its
|
||||
* OWN module's data. A collided leaf resolving to the wrong package's data
|
||||
* would corrupt the exit:
|
||||
* cea.val() = 40 + 3 = 43
|
||||
* ceb.val() = 90 + 7 = 97
|
||||
* => 43 + 97 = 140. */
|
||||
#define EXPECT_CXLINK 140
|
||||
static const char *cea_src =
|
||||
"package cea;\n"
|
||||
"export let v: i64 = 40;\n"
|
||||
"export def K: i64 = 3;\n"
|
||||
"export fn val() i64 = { return v + K; };\n";
|
||||
static const char *ceb_src =
|
||||
"package ceb;\n"
|
||||
"export let v: i64 = 90;\n"
|
||||
"export def K: i64 = 7;\n"
|
||||
"export fn val() i64 = { return v + K; };\n";
|
||||
static const char *cxroot_src =
|
||||
"package main;\n"
|
||||
"import cea;\n"
|
||||
"import ceb;\n"
|
||||
"fn main() i32 = { return (cea.val(): i32) + (ceb.val(): i32); };\n";
|
||||
|
||||
/* A package's sep-unit + bodies-unit composition. `name` is the dotted package
|
||||
* path; deps are spliced ahead under //ww:module <deppath>, then the target's
|
||||
* own source under //ww:module-reset. */
|
||||
@@ -542,6 +570,105 @@ main(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- #53 LINK leg: same-leaf exported non-fn decls path-qualify ----- */
|
||||
/* cea and ceb each `export let v` + `export def K` (see cea_src note).
|
||||
* Pre-#53 both emitted BARE v/K -> w6l duplicate-symbol clash; with §7-A
|
||||
* path-qualification (cea.v/ceb.v, cea.K/ceb.K) the link is clean and each
|
||||
* getter reads its OWN module's data. */
|
||||
{
|
||||
char ceaww[1024], cebww[1024], cxrootww[1024];
|
||||
char ceawwi[1024], cebwwi[1024], cxrootsep[1024];
|
||||
snprintf(ceaww, sizeof ceaww, "%s/cea.ww", td);
|
||||
snprintf(cebww, sizeof cebww, "%s/ceb.ww", td);
|
||||
snprintf(cxrootww, sizeof cxrootww, "%s/cxroot.ww", td);
|
||||
snprintf(ceawwi, sizeof ceawwi, "%s/cea.wwi", td);
|
||||
snprintf(cebwwi, sizeof cebwwi, "%s/ceb.wwi", td);
|
||||
snprintf(cxrootsep, sizeof cxrootsep, "%s/cxroot.sep.ww", td);
|
||||
if (write_file(ceaww, cea_src) || write_file(cebww, ceb_src) ||
|
||||
write_file(cxrootww, cxroot_src)) { fail++; goto out; }
|
||||
/* cea, ceb have no deps: produce their .wwi directly. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/w6c -I %s -o /dev/null %s >/dev/null 2>&1", bin, ceawwi, ceaww);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: cea.wwi produce (#53)\n"); fail++; goto out; }
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/w6c -I %s -o /dev/null %s >/dev/null 2>&1", bin, cebwwi, cebww);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "m3sep FAIL: ceb.wwi produce (#53)\n"); fail++; goto out; }
|
||||
/* cxroot imports cea + ceb: scope = both .wwi, then cxroot's body. */
|
||||
{
|
||||
FILE *u = fopen(cxrootsep, "wb");
|
||||
if (!u) { fail++; goto out; }
|
||||
if (append_section(u, "//ww:module cea", ceawwi) ||
|
||||
append_section(u, "//ww:module ceb", cebwwi) ||
|
||||
append_section(u, "//ww:module-reset", cxrootww)) { fclose(u); fail++; goto out; }
|
||||
fclose(u);
|
||||
}
|
||||
|
||||
struct { const char *tool_c, *tool_a, *tool_l; const char *tag; } tc[] = {
|
||||
{ "w6c", "w6a", "w6l", "cs" },
|
||||
{ "w6c_ww", "w6a_ww", "w6l_ww", "ww" },
|
||||
};
|
||||
char cxexe[2][1024];
|
||||
for (int t = 0; t < 2; t++) {
|
||||
char as[1024], ao[1024], bs[1024], bo[1024], rs[1024], ro[1024];
|
||||
snprintf(as, sizeof as, "%s/cea.%s.s", td, tc[t].tag);
|
||||
snprintf(ao, sizeof ao, "%s/cea.%s.o", td, tc[t].tag);
|
||||
snprintf(bs, sizeof bs, "%s/ceb.%s.s", td, tc[t].tag);
|
||||
snprintf(bo, sizeof bo, "%s/ceb.%s.o", td, tc[t].tag);
|
||||
snprintf(rs, sizeof rs, "%s/cxroot.%s.s", td, tc[t].tag);
|
||||
snprintf(ro, sizeof ro, "%s/cxroot.%s.o", td, tc[t].tag);
|
||||
snprintf(cxexe[t], sizeof cxexe[t], "%s/cxprog.%s", td, tc[t].tag);
|
||||
|
||||
int ok = 1;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, as, ceaww, bin, tc[t].tool_a, ao, as);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, bs, cebww, bin, tc[t].tool_a, bo, bs);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -c -o %s %s >/dev/null 2>&1 && timeout 180 %s/%s -o %s %s >/dev/null 2>&1",
|
||||
bin, tc[t].tool_c, rs, cxrootsep, bin, tc[t].tool_a, ro, rs);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
/* #53 structural proof (cstage .s once): each package's exported
|
||||
* non-fn leaf is MODULE-PREFIXED, never a shared bare v/K. */
|
||||
if (t == 0) {
|
||||
char *sa = NULL, *sb = NULL; size_t na = 0, nb = 0;
|
||||
if (slurp(as, &sa, &na) == 0 && slurp(bs, &sb, &nb) == 0) {
|
||||
if (strstr(sa, "cea.v(SB)") == NULL || strstr(sa, "cea.K(SB)") == NULL ||
|
||||
strstr(sb, "ceb.v(SB)") == NULL || strstr(sb, "ceb.K(SB)") == NULL) {
|
||||
fprintf(stderr, "m3sep FAIL: #53 exported non-fn leaf not module-prefixed\n");
|
||||
fail++;
|
||||
}
|
||||
if (strstr(sa, "DATAW v(SB)") != NULL || strstr(sa, "DATA K(SB)") != NULL ||
|
||||
strstr(sb, "DATAW v(SB)") != NULL || strstr(sb, "DATA K(SB)") != NULL) {
|
||||
fprintf(stderr, "m3sep FAIL: #53 bare exported leaf survives (cross-unit link collision)\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
free(sa); free(sb);
|
||||
}
|
||||
/* link cxroot + cea + ceb + runtime: clean iff leaves are unique. */
|
||||
snprintf(cmd, sizeof cmd, "timeout 180 %s/%s -o %s %s %s %s %s/../lib/libwwrt.a >/dev/null 2>&1",
|
||||
bin, tc[t].tool_l, cxexe[t], ro, ao, bo, bin);
|
||||
if (runwait(cmd) != 0) ok = 0;
|
||||
if (!ok) {
|
||||
fprintf(stderr, "m3sep FAIL: %s export-leaf sep build/link failed (#53)\n", tc[t].tag);
|
||||
fail++;
|
||||
cxexe[t][0] = '\0';
|
||||
continue;
|
||||
}
|
||||
int rc = runwait(cxexe[t]);
|
||||
if (rc != EXPECT_CXLINK) {
|
||||
fprintf(stderr, "m3sep FAIL: %s export-leaf link program exit=%d, expected %d "
|
||||
"(#53 same-leaf export collision corrupts the linked data)\n", tc[t].tag, rc, EXPECT_CXLINK);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
if (cxexe[0][0] && cxexe[1][0] && files_eq(cxexe[0], cxexe[1]) != 0) {
|
||||
fprintf(stderr, "m3sep FAIL: cs export-leaf exe != ww exe (rule 10, #53)\n");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
|
||||
runwait(cmd);
|
||||
@@ -551,7 +678,8 @@ out:
|
||||
}
|
||||
printf("m3sep: synth leaf->mid->root — per-pkg bodies==.wwi (-c) + cs==ww "
|
||||
".s/exe + determinism + value-global guard + behavioral (exit %d) + "
|
||||
"#49 str-pkg sep-LINK (sleaf+smid, exit 160)\n",
|
||||
"#49 str-pkg sep-LINK (sleaf+smid, exit 160) + "
|
||||
"#53 export-leaf sep-LINK (cea+ceb same-leaf v/K, exit 140)\n",
|
||||
EXPECT_EXIT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user