/* * 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 #include #include #include #include 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 pid = getpid(); char dir[96], bard[160], p[224], cmd[1024], rm[256]; snprintf(dir, sizeof dir, "/tmp/fnpc_%d_%d", pid, tag); snprintf(bard, sizeof bard, "%s/bar", dir); mkdir(dir, 0755); mkdir(bard, 0755); snprintf(p, sizeof p, "%s/bar.ww", bard); FILE *f = fopen(p, "wb"); if (!f) return -1; fputs("package bar;\n" "export fn slot() i64 = { return 99; };\n", f); fclose(f); snprintf(p, sizeof p, "%s/main.ww", dir); f = fopen(p, "wb"); if (!f) return -1; 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); fclose(f); snprintf(cmd, sizeof cmd, "cd %s && %s build -I bar main.ww >/dev/null 2>&1", dir, drv); int rc = runwait(cmd); snprintf(rm, sizeof rm, "rm -rf %s", dir); runwait(rm); 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++; if (build_must_fail(cdrv, 1) != 0) { fprintf(stderr, "fnptrcollide_run[cstage]: built ok, expected " "the leaf-name collision to be rejected\n"); fail++; } if (access(wdrv, X_OK) == 0) { /* wwstage gated */ total++; if (build_must_fail(wdrv, 2) != 0) { fprintf(stderr, "fnptrcollide_run[wwstage]: built ok, " "expected reject (#14 — &slot mis-folded to the " "fn TEXT reloc by the name-keyed nodefnptr)\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; }