wcc/ww: round-trip @symbol in the .wwi producer (#47)

The M2 .wwi producer rendered an exported fn carrying @symbol("...")
as a bare prototype, dropping the FFI link-symbol binding. A
sep-compiled consumer reading the .wwi then emitted `CALL malloc`
instead of `CALL rt_malloc` for `@symbol("rt_malloc") export fn
malloc`, breaking the bodies-vs-.wwi byte-id and the link. Affects
every package whose closure reaches rt/os.

Emit codegen/link-relevant attributes through a single named
predicate (wwi_attr_relevant / wwiattrrelevant), today true iff the
name is "symbol" — the only such attribute that exists. @align/@offset
are NOT field attributes in ww (the parser parses no field attrs, the
N_TFIELD node has no attr slot); the predicate is named for the class
so they slot in if ww ever grows them (#51). attr is assigned at
exactly one site per stage (parse.c:1351 / decl.ww:168), both inside
parsefn, so only N_FNDECL carries attrs and the fn-decl render path
covers the whole class.

Both stages, byte-identical (rule 10). 989_m2wwi_run synth gate gains
an @symbol fn + a content assertion that the .wwi carries it verbatim.
This commit is contained in:
2026-06-15 22:56:12 +09:00
parent 13e5e35f81
commit 4622556c62
4 changed files with 124 additions and 1 deletions

View File

@@ -77,6 +77,17 @@ slurp(const char *path, char **outbuf, size_t *outlen)
return 0;
}
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;
}
static int
files_eq(const char *a, const char *b)
{
@@ -246,7 +257,11 @@ static const char *synth_src =
"export fn risky() !i32;\n"
"export fn matrix() [LIMIT]u8;\n"
"export fn pair() (i32, i32);\n"
"export fn opt(p: *color, b: []u8) (i32 | void);\n";
"export fn opt(p: *color, b: []u8) (i32 | void);\n"
/* #47: a codegen/link-relevant attribute (@symbol — the FFI link-
* symbol override) MUST round-trip into the `.wwi`; dropping it makes
* sep-compile emit `CALL ext` for a `@symbol("rt_ext")` fn. */
"@symbol(\"rt_ext\") export fn ext(n: i32) i32;\n";
static int
synth(const char *bin)
@@ -288,6 +303,12 @@ synth(const char *bin)
"/ decl-kind unparse diverges across stages)\n");
goto out;
}
/* #47: the @symbol attribute must survive the round-trip verbatim. */
if (file_contains(cs, "@symbol(\"rt_ext\") export fn ext") != 0) {
fprintf(stderr, "m2wwi FAIL: synth — .wwi dropped @symbol "
"(sep-compile would emit the wrong link symbol)\n");
goto out;
}
snprintf(cmd, sizeof cmd,
"timeout 180 %s/wwdump -a %s >/dev/null 2>&1", bin, cs);
if (runwait(cmd) != 0) {