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

@@ -310,11 +310,45 @@ wwi_expr(FILE *of, Node *e)
}
}
/* Only codegen/link-relevant attributes round-trip into the `.wwi`. Today
* that is exactly @symbol (the FFI link-symbol override, read back at cgen
* fficollect — dropping it makes sep-compile emit `CALL malloc` for a
* `@symbol("rt_malloc")` fn). Named for the class so @align/@offset would
* slot in here IF ww ever grows field-layout attributes — it has none
* today (task #47 report). @test never reaches a `.wwi` (test fns are not
* export-marked), so it needs no exclusion arm. */
static int
wwi_attr_relevant(const char *nm)
{
return nm && strcmp(nm, "symbol") == 0;
}
static void
wwi_attrs(FILE *of, Node *d)
{
for (Node *a = d->attr; a; a = a->next) {
if (a->kind != N_ATTR || !wwi_attr_relevant(a->str))
continue;
fputc('@', of);
fputs(a->str, of);
if (a->list) {
fputc('(', of);
for (Node *arg = a->list; arg; arg = arg->next) {
if (arg != a->list) fputs(", ", of);
wwi_expr(of, arg);
}
fputc(')', of);
}
fputc(' ', of);
}
}
static void
wwi_decl(FILE *of, Node *d)
{
switch (d->kind) {
case N_FNDECL:
wwi_attrs(of, d);
fputs("export fn ", of);
fputs(d->str, of);
fputc('(', of);