wcc/ww: reject a duplicate top-level main (F-D)
A second top-level decl named `main` (fn/let/def/type) collides with the entry main on the single bare `main` symbol: today both lower to a bare `main`, w6l silently accepts the duplicate, and the program links rc=0 then segfaults (or runs wrong), in both stages. The existing duplicate-decl rejects key on (name, module), so a cross-module `foo.main` vs the bare entry `main` read as distinct and slip through. Add a program-global, name-only, cross-module uniqueness check on `main` in the checker (both stages), colocated with the duplicate-decl rejects and counting user decls before the -T synthesized test main. Corpus-safe: a lone `fn main` in any package stays legal (ww has no package-main convention -- cmatrix/lisp/mandelbrot are non-main-package entries and keep building). This converts the silent segfault to a loud compile error and subsumes the w6l silent-dup-main case (#31); correct package-aware mangling of a non-entry main is deferred to the root-unit entry-detection work (#22/#32). Regenerates the w6c and wwdump combined.ww. Table-driven 842 test: reject rows for let/fn/def/type main (genuine cross-module import form) plus a negative single-main corpus-safe row that must still build+run.
This commit is contained in:
@@ -2962,6 +2962,33 @@ check_file(Checker *c, Node *file)
|
||||
}
|
||||
c->cur_mod = NULL;
|
||||
|
||||
/* Program-global, name-only, cross-module uniqueness on `main`.
|
||||
* `main` lowers to ONE bare entry symbol, so a second top-level
|
||||
* decl named `main` (any kind, any package) collides with the
|
||||
* entry at link time — today a silent segfault / link-fail in
|
||||
* both stages. The (name, module) duplicate rejects above read a
|
||||
* cross-package `foo.main` and the bare entry as distinct, so they
|
||||
* miss this. Correct multi-main mangling (entry stays bare, the
|
||||
* rest qualify) is deferred (task #32); reject loudly meanwhile
|
||||
* (rule 7). Walks USER decls only — runs before the -T synth main
|
||||
* is appended below — so a hosted-test build never false-counts. */
|
||||
{
|
||||
Node *firstmain = NULL;
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
if (d->str == NULL || strcmp(d->str, "main") != 0)
|
||||
continue;
|
||||
if (d->kind != N_FNDECL && d->kind != N_LET
|
||||
&& d->kind != N_DEF && d->kind != N_TYPEDECL)
|
||||
continue;
|
||||
if (firstmain == NULL) {
|
||||
firstmain = d;
|
||||
continue;
|
||||
}
|
||||
err(c, d->pos, "duplicate top-level main: only the "
|
||||
"entry main may exist (task #32)");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* #15 @test harness — under `w6c -T`, synthesize the entry the
|
||||
* driver would otherwise hand-wire. We sit at the seam between
|
||||
|
||||
Reference in New Issue
Block a user