cstage+test: SK_USE→SK_X promotion sets use_alias

In cmd/wcc/check.c the pass-1.5 SK_USE→SK_DEF/SK_FN/SK_VAR promotion
sites forgot to set prev->use_alias = 1 when the imported module's
top-level decl shadowed the SK_USE leaf in flat scope. Downstream
dot-prefixed lookups (resolve_typename L77, N_DOT L709) gate the
module-head walk on (SK_USE || use_alias), so `mod.flag` resolution
fell through to "unknown type". The SK_TYPE precedent at L1660 had
the line; the three sister sites at L1709/L1722/L1736 now do too,
in the same one-line shape and field-set order.

The wwstage selfhost/cmd/wcc/check.ww uses coexistence rather than
in-place promotion: SK_USE and same-leaf SK_TYPE/FN/DEF/VAR live as
separate entries differentiated by sym.mod, and scopelookupinmodule's
mod-filter already disambiguates dotted lookups — no use_alias flag
needed, so the cstage bug is structurally non-reachable there. An
architectural note at installdecl documents this divergence-by-design
and warns against porting the flag (adding a field to `sym` changes
its size and risks the wwstage cgen amalloc-undersize trap).

Audit covered every SK_USE→SK_X promotion path in check.c (4 sites:
SK_TYPE already-correct as precedent, SK_DEF/SK_FN/SK_VAR fixed). The
surfacing case was lib/fnmatch: `fn fnmatch(...)` shadows the SK_USE
leaf, so `fnmatch.flag` failed in worker-fnmatch's WIP — that test
(972_fnmatch_run) now flips PASS as live integration proof.
test/wcc/699_use_promote_alias.c pins all four rows with a single
table-driven driver (type/fn/def/var → use mod; let m: mod.flag =
mod.flag.A; return m: i32, expecting exit 42 per row). 995_self_rebuild
byte-identity holds.
This commit is contained in:
2026-05-15 15:06:37 +09:00
parent 86de58bc6c
commit 7f60ebbe44
11 changed files with 261 additions and 0 deletions

View File

@@ -1707,7 +1707,13 @@ check_file(Checker *c, Node *file)
Sym *prev = scope_lookup_local(c->cur, d->str);
const char *mod = decl_mod(file, d);
if (prev && prev->kind == SK_USE) {
/* `use mod; ... def mod = ...;` — promote the
* SK_USE to the def symbol but remember it was
* also a module name so dotted qualifiers
* (`mod.x`) keep resolving via the N_DOT path's
* use_alias branch. Mirrors L1677. */
prev->kind = SK_DEF; prev->type = t; prev->decl = d;
prev->use_alias = 1;
if (mod && prev->mod == NULL) prev->mod = mod;
} else if (!scope_define_in_module(c->cur, d->str, mod,
SK_DEF, t, d))
@@ -1720,7 +1726,15 @@ check_file(Checker *c, Node *file)
Sym *prev = scope_lookup_local(c->cur, d->str);
const char *mod = decl_mod(file, d);
if (prev && prev->kind == SK_USE) {
/* `use mod; ... fn mod(...) ...;` — promote
* but remember the module-alias so dotted
* qualifiers (`mod.x`) keep resolving. The
* lib/fnmatch case: `fn fnmatch(...)` shadows
* the SK_USE leaf, and without use_alias the
* dot-prefix path in resolve_typename loses
* the `fnmatch.flag` lookup. */
prev->kind = SK_FN; prev->type = t; prev->decl = d;
prev->use_alias = 1;
if (mod && prev->mod == NULL) prev->mod = mod;
} else if (!scope_define_in_module(c->cur, d->str, mod,
SK_FN, t, d))
@@ -1734,8 +1748,12 @@ check_file(Checker *c, Node *file)
Sym *prev = scope_lookup_local(c->cur, d->str);
const char *mod = decl_mod(file, d);
if (prev && prev->kind == SK_USE) {
/* `use mod; ... let mod: T = ...;` —
* same promote-and-alias shape as the
* SK_DEF / SK_FN cases above. */
prev->kind = SK_VAR; prev->type = t;
prev->decl = d;
prev->use_alias = 1;
if (mod && prev->mod == NULL) prev->mod = mod;
} else
scope_define_in_module(c->cur, d->str,