ww: separate package identity from declared name

This commit is contained in:
2026-08-14 03:07:58 +09:00
parent 028da6323e
commit 6efe9b70d4
37 changed files with 1678 additions and 886 deletions

View File

@@ -58,8 +58,10 @@ lookup_builtin(const char *name)
}
static const char *decl_mod(Node *file, Node *d);
static const char *use_path(Node *file, const char *curmod, const char *alias);
static int src_imports(Node *file, const char *modtag, const char *name);
static const char *use_path(Node *file, const char *curmod, int source,
const char *alias);
static int src_imports(Node *file, const char *modtag, int source,
const char *name);
static Sym *lookup_visible(Checker *c, const char *name);
static Sym *lookup_visible_type(Checker *c, const char *name);
static void resolve_typedecl(Checker *c, Node *d);
@@ -89,6 +91,7 @@ resolve_typename(Checker *c, Node *n)
/* M1 #22: map the qualifier alias to its dotted
* import path (symbols are path-keyed). */
const char *mk = use_path(c->file, c->cur_mod,
c->cur_source,
head);
if (mk != NULL)
s = scope_lookup_in_module(c->cur, mk,
@@ -622,7 +625,8 @@ eval_def_const(Checker *c, Node *n, u64 *out, int depth)
case N_DOT: {
if (n->lhs == NULL || n->lhs->kind != N_IDENT) return 0;
/* M1 #22: map the qualifier alias to its dotted import path. */
const char *mk = use_path(c->file, c->cur_mod, n->lhs->str);
const char *mk = use_path(c->file, c->cur_mod, c->cur_source,
n->lhs->str);
if (mk == NULL) return 0;
Sym *s = scope_lookup_in_module(c->cur, mk, n->str);
if (s == NULL || s->kind != SK_DEF ||
@@ -636,12 +640,15 @@ eval_def_const(Checker *c, Node *n, u64 *out, int depth)
}
int
check_eval_const(Checker *c, Node *n, const char *owner, u64 *out)
check_eval_const(Checker *c, Node *n, const char *owner, int source, u64 *out)
{
const char *saved = c->cur_mod;
int savesource = c->cur_source;
c->cur_mod = owner;
c->cur_source = source;
int ok = eval_def_const(c, n, out, 0);
c->cur_mod = saved;
c->cur_source = savesource;
return ok;
}
@@ -1475,6 +1482,7 @@ cexpr(Checker *c, Node *n)
* import path; map the alias the user wrote to
* that path before looking up the leaf. */
const char *mk = use_path(c->file, c->cur_mod,
c->cur_source,
n->lhs->str);
if (mk == NULL) {
if (ms->kind == SK_USE)
@@ -2854,6 +2862,7 @@ check_init(Checker *c, Arena *a)
c->is_test = 0; /* #15: caller (w6c main) sets it after init */
c->is_test_package = 0;
c->test_module = "test";
c->test_target = NULL;
typesinit(a);
c->top = newscope(a, NULL);
c->cur = c->top;
@@ -2873,6 +2882,10 @@ static const char *
decl_mod(Node *file, Node *d)
{
if (d == NULL || d->module == NULL || file == NULL) return NULL;
/* Separate-compilation interfaces already carry their canonical owner.
* Visibility is checked independently against the referencing source's
* import binding; never erase semantic ownership for a transitive fact. */
if (d->imported) return d->module;
for (Node *u = file->list; u; u = u->next) {
/* M1 #22: a decl is imported iff some `use` directive's full
* dotted import path equals the decl's module (now the path,
@@ -2887,41 +2900,40 @@ decl_mod(Node *file, Node *d)
}
/*
* use_path — map a `use` alias (leaf bareword the user writes, `utf8`)
* to the full dotted import path it binds (`encoding.utf8`), for the
* module-qualified resolution and the codegen hint (M1 #22, §2.4). For
* single-level packages usepath == alias so the result is unchanged.
* use_path — map a source-file default qualifier (the imported package's
* declared name) to the full canonical import path it binds, for
* module-qualified resolution and the codegen hint (M1 #22, §2.4).
*
* The alias→path map is NOT file-global: two modules in the same
* concatenated unit may bind the same leaf alias to different paths
* (sha256's `import crypto.math` and strconv's `import math` both bind
* alias `math`). The import declared in the SAME module as the
* reference (`curmod`) is the authoritative one; requiring it closes both
* cross-module mis-resolution and accidental transitive-import visibility.
* The qualifier→path map is source-file local: two files in the same
* concatenated unit may bind the same declared name to different paths. The
* import with the reference's source ID and owner is authoritative, closing
* both cross-file mis-resolution and accidental transitive visibility.
* Returns NULL if the referencing package has no such `use`.
*/
static const char *
use_path(Node *file, const char *curmod, const char *alias)
use_path(Node *file, const char *curmod, int source, const char *alias)
{
if (file == NULL || alias == NULL) return NULL;
/* Legacy inline multi-package units may spell a package's own
* declarations as `pkg.member`. That is self-qualification, not an
* imported namespace; preserve it without reopening transitive lookup. */
if (curmod != NULL) {
if (source == 0 && curmod != NULL) {
const char *dot = strrchr(curmod, '.');
const char *leaf = dot ? dot + 1 : curmod;
if (strcmp(alias, leaf) == 0) return curmod;
}
for (Node *u = file->list; u; u = u->next) {
if (u->kind != N_USE || u->str == NULL
|| strcmp(u->str, alias) != 0)
|| u->sourceid != source || strcmp(u->str, alias) != 0)
continue;
const char *p = u->usepath ? u->usepath : u->str;
const char *um = decl_mod(file, u);
int same = (um == NULL) ? (curmod == NULL)
: (curmod != NULL && strcmp(um, curmod) == 0);
if (same)
if (same) {
u->used = 1;
return p;
}
}
return NULL;
}
@@ -2947,9 +2959,12 @@ resolve_typedecl(Checker *c, Node *d)
if (t == NULL || t->under != NULL || t->resolving) return;
t->resolving = 1;
const char *save = c->cur_mod;
int savesource = c->cur_source;
c->cur_mod = decl_mod(c->file, d);
c->cur_source = d->sourceid;
Type *under = resolve_type(c, d->lhs);
c->cur_mod = save;
c->cur_source = savesource;
/* Alias-root cycle (`type a = b; type b = a` / `type a = a`):
* checked BEFORE clearing the flag so self-aliases trip on their
* own in-progress mark. ty_err instead of the cyclic under keeps
@@ -2978,11 +2993,11 @@ resolve_typedecl(Checker *c, Node *d)
* (N_USE nodes with module == NULL).
*/
static int
src_imports(Node *file, const char *modtag, const char *name)
src_imports(Node *file, const char *modtag, int source, const char *name)
{
if (file == NULL || name == NULL || name[0] == '\0') return 0;
for (Node *u = file->list; u; u = u->next) {
if (u->kind != N_USE) continue;
if (u->kind != N_USE || u->sourceid != source) continue;
/* Skip self-imports: lib/fmt/fmt_test.ww carries `use fmt;`
* even though its module tag is also "fmt"; that directive
* doesn't introduce a foreign module bareword and lib/fmt's
@@ -3013,10 +3028,18 @@ static int
direct_module_visible(Checker *c, const char *mod)
{
if (c == NULL || mod == NULL || mod[0] == '\0') return 0;
const char *dot = strrchr(mod, '.');
const char *alias = dot ? dot + 1 : mod;
const char *path = use_path(c->file, c->cur_mod, alias);
return path != NULL && strcmp(path, mod) == 0;
for (Node *u = c->file->list; u; u = u->next) {
if (u->kind != N_USE || u->sourceid != c->cur_source) continue;
const char *um = decl_mod(c->file, u);
int same = c->cur_mod == NULL ? um == NULL
: um != NULL && strcmp(um, c->cur_mod) == 0;
const char *path = u->usepath ? u->usepath : u->str;
if (same && path != NULL && strcmp(path, mod) == 0) {
u->used = 1;
return 1;
}
}
return 0;
}
static Sym *
@@ -3035,7 +3058,7 @@ lookup_visible(Checker *c, const char *name)
* source-owned alias map is authoritative: if this package directly
* imports NAME, return the coalesced module marker only as a marker; the
* N_DOT path maps the alias to the correct full path again. */
if (use_path(c->file, c->cur_mod, name) != NULL) {
if (use_path(c->file, c->cur_mod, c->cur_source, name) != NULL) {
for (Scope *p = c->cur; p; p = p->parent)
for (Sym *b = p->first; b; b = b->next)
if (strcmp(b->name, name) == 0
@@ -3101,7 +3124,7 @@ check_module_shadow(Checker *c, const char *name, Pos pos,
}
}
if (!seen_use) return;
if (!src_imports(c->file, c->cur_mod, name)) return;
if (!src_imports(c->file, c->cur_mod, c->cur_source, name)) return;
err(c, pos, "%s '%s' shadows imported module '%s'",
kindstr, name, name);
}
@@ -3132,6 +3155,80 @@ same_import_fact(Checker *c, Node *d, const char *mod, Skind kind)
return s;
}
static int
top_decl_kind(Node *d)
{
return d != NULL && (d->kind == N_TYPEDECL || d->kind == N_DEF
|| d->kind == N_FNDECL || d->kind == N_LET);
}
static void
check_import_alt(Node *d, const char *name)
{
FILE *f = errout ? errout : stderr;
fprintf(f, "\t%s:%d:%d: other declaration of %s\n",
d->pos.file ? d->pos.file : "?", d->pos.line, d->pos.col, name);
}
/* Go's default import binding lives in the importing file's scope. Reject
* only another binding in that same source section; equal names in sibling
* files are independent even though their canonical edges are package-wide. */
static void
check_import_redeclarations(Checker *c, Node *file)
{
if (!c->sep_mode) return;
for (Node *u = file->list; u; u = u->next) {
if (u->kind != N_USE || u->imported || u->str == NULL)
continue;
for (Node *v = file->list; v != u; v = v->next) {
if (v->kind != N_USE || v->imported || v->str == NULL
|| v->sourceid != u->sourceid)
continue;
if (strcmp(v->str, u->str) == 0) {
err(c, u->pos, "%s redeclared in this block", u->str);
check_import_alt(v, u->str);
break;
}
}
}
}
/* Report file-local unused bindings before package-declaration collisions,
* matching the pinned Go resolver's stable ordering. The package declarations
* themselves are package-scoped, so they collide with an equal import name in
* any contributing source file. */
static void
check_import_usage_and_collisions(Checker *c, Node *file)
{
if (!c->sep_mode) return;
for (Node *u = file->list; u; u = u->next) {
if (u->kind != N_USE || u->imported || u->used || u->str == NULL)
continue;
const char *path = u->usepath ? u->usepath : u->str;
const char *dot = strrchr(path, '.');
const char *leaf = dot ? dot + 1 : path;
if (strcmp(u->str, leaf) == 0)
err(c, u->pos, "\"%s\" imported and not used", path);
else
err(c, u->pos, "\"%s\" imported as %s and not used",
path, u->str);
}
for (Node *d = file->list; d; d = d->next) {
if (d->imported || !top_decl_kind(d) || d->str == NULL)
continue;
for (Node *u = file->list; u; u = u->next) {
if (u->kind != N_USE || u->imported || u->str == NULL
|| strcmp(d->str, u->str) != 0)
continue;
const char *path = u->usepath ? u->usepath : u->str;
err(c, d->pos,
"%s already declared through import of package %s (\"%s\")",
d->str, u->str, path);
check_import_alt(u, d->str);
}
}
}
void
check_file(Checker *c, Node *file)
{
@@ -3148,21 +3245,30 @@ check_file(Checker *c, Node *file)
* already emits the qualified call. wwstage twin in check.ww. */
if (c->is_test) {
int present = 0;
for (Node *u = file->list; u; u = u->next)
for (Node *u = file->list; u; u = u->next) {
if (c->test_target != NULL && u->kind == N_USE
&& !u->imported && u->usepath
&& (strcmp(u->usepath, c->test_target) == 0
|| strcmp(u->usepath, c->test_module) == 0))
u->used = 1;
if (u->kind == N_USE && !u->imported
&& u->usepath && strcmp(u->usepath, c->test_module) == 0) {
present = 1;
break;
}
}
if (!present) {
Node *usenode = newnode(c->a, N_USE, file->pos);
usenode->str = c->test_module;
usenode->strlen = strlen(c->test_module);
usenode->usepath = c->test_module;
usenode->pkgname = file->pkgname;
usenode->sourceid = file->sourceid;
if (c->test_target != NULL) usenode->used = 1;
usenode->next = file->list;
file->list = usenode;
}
}
check_import_redeclarations(c, file);
/* pass 1: install names (types first, then defs/fns).
* For self-referential types we install the named-type placeholder
@@ -3172,13 +3278,10 @@ check_file(Checker *c, Node *file)
* references (`strconv.invalid`) resolve when typedecl bodies are
* walked in the next pass. */
for (Node *d = file->list; d; d = d->next) {
c->cur_source = d->sourceid;
if (d->kind == N_USE) {
/* check-(c) self-import: a package may not import
* itself. Pure owner==leaf string compare, package-
* model-independent — sound under ww's filename-keyed
* file-inclusion imports. check-(a) unused and
* (b)/(d) membership DEFERRED to task #8 (filename-
* keyed pulls lack import->file->symbol provenance). */
/* A package may not import its own canonical owner. Import
* usage and membership are checked with source-file provenance. */
const char *owner = decl_mod(file, d);
/* M1 #22: self-import ⟺ the imported path equals the
* use's own (owning) module path. Compares paths, not
@@ -3233,9 +3336,11 @@ check_file(Checker *c, Node *file)
* here. A foldable stub carries type NULL until then. A duplicate
* (prev already bound non-USE) is left for that loop to diagnose. */
c->cur_mod = NULL;
c->cur_source = 0;
for (Node *d = file->list; d; d = d->next) {
if (d->kind != N_DEF) continue;
c->cur_mod = decl_mod(file, d);
c->cur_source = d->sourceid;
const char *mod = decl_mod(file, d);
if (same_import_fact(c, d, mod, SK_DEF) != NULL)
continue;
@@ -3250,13 +3355,16 @@ check_file(Checker *c, Node *file)
}
}
c->cur_mod = NULL;
c->cur_source = 0;
for (Node *d = file->list; d; d = d->next) {
if (d->kind != N_TYPEDECL) continue;
resolve_typedecl(c, d);
}
c->cur_mod = NULL;
c->cur_source = 0;
for (Node *d = file->list; d; d = d->next) {
c->cur_mod = decl_mod(file, d);
c->cur_source = d->sourceid;
switch (d->kind) {
case N_USE:
/* already installed in pass 1; no-op here so the
@@ -3360,6 +3468,7 @@ check_file(Checker *c, Node *file)
}
}
c->cur_mod = NULL;
c->cur_source = 0;
/* Program-global uniqueness on the ENTRY `main`. M1 #32: the entry
* is the ROOT-unit main (imported==0) — it alone lowers to the bare
@@ -3487,8 +3596,15 @@ check_file(Checker *c, Node *file)
nm->strlen = strlen(d->str);
Node *id;
if (d->imported && d->module && d->module[0]) {
const char *dotp = strrchr(d->module, '.');
const char *alias = dotp ? dotp + 1 : d->module;
const char *alias = d->pkgname && d->pkgname[0]
? d->pkgname : d->module;
/* A generated dispatcher cannot bind a command test
* package as `main`: that would collide with its own entry.
* This is a compiler-owned binding, never source alias syntax;
* use the canonical target path as its private qualifier. */
if (c->test_target != NULL
&& strcmp(d->module, c->test_target) == 0)
alias = c->test_target;
id = newnode(c->a, N_DOT, fp);
id->lhs = newnode(c->a, N_IDENT, fp);
id->lhs->str = alias;
@@ -3542,6 +3658,8 @@ check_file(Checker *c, Node *file)
tab = newnode(c->a, N_LET, fp);
tab->op = TK_CONST;
tab->str = "__wwtests";
tab->pkgname = file->pkgname;
tab->sourceid = file->sourceid;
tab->lhs = tsl;
tab->rhs = arr;
/* pass 1 already ran, so install the table's name now —
@@ -3581,10 +3699,15 @@ check_file(Checker *c, Node *file)
Node *m = newnode(c->a, N_FNDECL, fp);
m->str = "main";
m->export = 1;
m->pkgname = file->pkgname;
m->sourceid = file->sourceid;
m->lhs = newnode(c->a, N_TNAME, fp);
m->lhs->str = "i32";
m->body = body;
int savesource = c->cur_source;
c->cur_source = m->sourceid;
m->type = build_fn_type(c, m);
c->cur_source = savesource;
/* pass 1 already ran, so the install loop never stamped m's
* type; set it explicitly (pass 2 below reads d->type).
* Append the table const (if any) then main to file->list. */
@@ -3603,6 +3726,7 @@ check_file(Checker *c, Node *file)
/* pass 2: check def initialisers and fn bodies */
for (Node *d = file->list; d; d = d->next) {
c->cur_mod = decl_mod(file, d);
c->cur_source = d->sourceid;
switch (d->kind) {
case N_DEF: {
if (d->rhs) {
@@ -3786,6 +3910,8 @@ check_file(Checker *c, Node *file)
}
}
c->cur_mod = NULL;
c->cur_source = 0;
check_import_usage_and_collisions(c, file);
/*
* #6 harec-fidelity (ref/harec/src/check.c:3941): a @test fn is

View File

@@ -1327,9 +1327,9 @@ parseuse(Parser *p)
Pos pp = p->cur.pos;
expect(p, TK_USE);
Node *n = newnode(p->a, N_USE, pp);
/* M1 #22: accumulate the full dotted import path (n->module) so the
* checker can match decl identity on the path, while n->str stays the
* leaf alias the user writes (`utf8.x`). */
/* M1 #22: accumulate the full dotted import path in usepath. `str`
* starts as its leaf; direct export metadata later installs the imported
* declaration's default name without changing canonical identity. */
const char *leaf = expectident(p);
const char *path = leaf;
while (accept(p, TK_DOT)) {
@@ -1358,19 +1358,23 @@ parseimports(Parser *p)
* the following package clause and imports. */
if (p->cur.kind == TK_MODPATH) {
sawpackage = 0;
p->sourceid++;
p->pathmod = p->cur.text;
p->curmod = p->cur.text;
p->resetmod = NULL;
p->curpkg = NULL;
advance(p);
continue;
}
if (p->cur.kind == TK_MODRESET) {
const char *rp = p->cur.text;
sawpackage = 0;
p->sourceid++;
advance(p);
p->pathmod = NULL;
p->curmod = rp;
p->resetmod = rp;
p->curpkg = NULL;
continue;
}
if (p->cur.kind == TK_MODULE) {
@@ -1385,9 +1389,13 @@ parseimports(Parser *p)
}
const char *name = expectident(p);
expect(p, TK_SEMI);
p->curmod = name;
p->curpkg = name;
if (p->pathmod == NULL && p->resetmod == NULL)
p->curmod = name;
Node *package = newnode(p->a, N_FILE, pp);
package->module = name;
package->module = p->curmod;
package->pkgname = name;
package->sourceid = p->sourceid;
if (packages == NULL)
packages = package;
else
@@ -1395,6 +1403,8 @@ parseimports(Parser *p)
packagetail = package;
if (!sawpackage) {
file->module = name;
file->pkgname = name;
file->sourceid = p->sourceid;
file->pos = pp;
sawpackage = 1;
}
@@ -1408,6 +1418,8 @@ parseimports(Parser *p)
if (p->cur.kind == TK_USE) {
Node *d = parseuse(p);
d->module = p->curmod;
d->pkgname = p->curpkg;
d->sourceid = p->sourceid;
if (head == NULL)
head = d;
else
@@ -1424,6 +1436,8 @@ parseimports(Parser *p)
p->errs++;
Node *d = parseuse(p);
d->module = p->curmod;
d->pkgname = p->curpkg;
d->sourceid = p->sourceid;
if (head == NULL)
head = d;
else
@@ -1441,6 +1455,8 @@ parseimports(Parser *p)
p->errs++;
Node *d = parseuse(p);
d->module = p->curmod;
d->pkgname = p->curpkg;
d->sourceid = p->sourceid;
if (head == NULL)
head = d;
else
@@ -1546,6 +1562,7 @@ parsefile(Parser *p)
Pos pp = { p->l->file, 1, 1 };
Node *file = newnode(p->a, N_FILE, pp);
Node *head = NULL, *tail = NULL;
Node *packages = NULL, *packagetail = NULL;
int sawpackage = 0;
while (p->cur.kind != TK_EOF) {
/* `package foo;` — directory-as-module declaration. Every
@@ -1555,58 +1572,41 @@ parsefile(Parser *p)
* sep primary-reset (resetmod) regions carry identity
* out-of-band and are exempt. */
if (p->cur.kind == TK_MODULE) {
Pos packagepos = p->cur.pos;
sawpackage = 1;
advance(p);
const char *name = expectident(p);
expect(p, TK_SEMI);
if (p->pathmod != NULL || p->resetmod != NULL) {
/* M1 #22: while an import path is active the
* in-file `package` clause is an ASSERTION — its
* leaf must equal the path's last component; it
* does NOT overwrite the path-derived module.
* #57 extends this to the sep primary-reset path
* (resetmod): the dotted reset path is the
* authoritative identity, the clause asserts. */
const char *active =
p->pathmod ? p->pathmod : p->resetmod;
const char *dot = strrchr(active, '.');
const char *last = dot ? dot + 1 : active;
int testsupport = p->testmodule != NULL
&& strcmp(p->testmodule, "__wwtest") == 0
&& strcmp(active, "__wwtest") == 0
&& strcmp(name, "test") == 0;
int commandpackage = p->commandpackage
&& p->pathmod == NULL && p->resetmod != NULL
&& (strcmp(name, "main") == 0
|| strcmp(name, "main_test") == 0);
if (strcmp(name, last) != 0 && !testsupport
&& !commandpackage) {
errorf(p->cur.pos,
"package %s does not match import path %s",
name, active);
p->errs++;
}
} else {
p->curpkg = name;
if (p->pathmod == NULL && p->resetmod == NULL) {
p->curmod = name;
/* #11: stamp the primary module identity on the
* N_FILE node so wwi_emit can derive the
* `package` leaf even when the body carries zero
* module-tagged decls. Primary identity only;
* never the imported boundary (TK_MODPATH). */
if (file->module == NULL)
file->module = name;
}
Node *package = newnode(p->a, N_FILE, packagepos);
package->module = p->curmod;
package->pkgname = name;
package->sourceid = p->sourceid;
package->imported = p->pathmod != NULL;
if (packages == NULL) packages = package;
else packagetail->next = package;
packagetail = package;
if (file->pkgname == NULL) {
file->pkgname = name;
file->sourceid = p->sourceid;
}
continue;
}
/* `//ww:module <path>` — M1 #22 import boundary. The following
* file's decls mangle on the full dotted import path, not the
* leaf `package` clause, and are flagged imported (gates the
* file's decls mangle on the full dotted import path independently
* of its `package` clause, and are flagged imported (gates the
* root-only bare-`main` rule, #32). */
if (p->cur.kind == TK_MODPATH) {
sawpackage = 0;
p->sourceid++;
p->pathmod = p->cur.text;
p->curmod = p->cur.text;
p->resetmod = NULL;
p->curpkg = NULL;
if (file->module == NULL) file->module = p->cur.text;
advance(p);
continue;
}
@@ -1620,6 +1620,7 @@ parsefile(Parser *p)
* decls to bare — that usage is deliberate-only. */
if (p->cur.kind == TK_MODRESET) {
sawpackage = 0;
p->sourceid++;
/* #57: a path-carrying reset (sep primary body) mangles
* decls on the dotted path so definer == importer, but
* leaves imported==0 (curmod set, pathmod NULL) so -c
@@ -1630,6 +1631,7 @@ parsefile(Parser *p)
const char *rp = p->cur.text;
advance(p);
p->pathmod = NULL;
p->curpkg = NULL;
if (rp != NULL) {
p->curmod = rp;
p->resetmod = rp;
@@ -1679,14 +1681,17 @@ parsefile(Parser *p)
advance(p);
continue;
}
if (d != NULL) {
d->module = p->curmod;
d->imported = (p->pathmod != NULL);
if (d != NULL) {
d->module = p->curmod;
d->pkgname = p->curpkg;
d->sourceid = p->sourceid;
d->imported = (p->pathmod != NULL);
}
if (head == NULL) head = d;
else tail->next = d;
tail = d;
}
file->list = head;
file->body = packages;
return file;
}

View File

@@ -173,7 +173,7 @@ typedef enum {
* that replaces the withdrawn `package main` inject). */
TK_MODPATH, /* `//ww:module <dotted-path>` — driver import boundary:
* the following file's decls mangle on the full import
* path, not the leaf `package` clause (M1 #22). Token
* path independently of the `package` clause (M1 #22). Token
* text carries the dotted path. */
TK_LAST /* sentinel for tables */
@@ -344,10 +344,16 @@ struct Node {
* this is the importing (owning)
* module. */
const char *usepath; /* M1 #22: on an N_USE node, the full
* dotted IMPORT path (`encoding.utf8`)
* vs the leaf alias in `str`. Drives
* canonical import path (`encoding.utf8`);
* `str` is the declared default qualifier. Drives
* the path-keyed decl_mod match and the
* qualified-ref codegen hint. */
* qualified-ref codegen hint. */
const char *pkgname; /* declared package name for this source/export
* section; independent of canonical `module`. */
int sourceid; /* lexical source-file scope within the parsed
* owner unit; module-reset/module boundaries
* advance it deterministically. */
int used; /* N_USE: checker observed this file-local binding. */
int imported; /* M1 #22: decl reached through an
* `//ww:module <path>` import boundary
* (vs root/primary). Gates the root-only
@@ -373,12 +379,12 @@ struct Parser {
* import path; while set, decls stamp
* module=pathmod and imported=1, and the
* in-file `package` clause is an assertion. */
const char *resetmod; /* #57: active `//ww:module-reset <path>` dotted
* path; mangles decls on the path WITHOUT
* imported=1 (primary-ness for -c and the #32
* bare-main rule stay intact), and the in-file
* `package` clause asserts (leaf == last
* component) instead of overwriting curmod. */
const char *resetmod; /* #57: active `//ww:module-reset <path>` canonical
* identity; decls mangle on it without becoming
* imported. The package clause independently
* supplies the declared name. */
const char *curpkg; /* declared name of the active source section. */
int sourceid; /* deterministic lexical source-section ordinal. */
const char *testmodule; /* hidden package-driver alias for toolchain
* `package test`; NULL outside that compile */
int commandpackage; /* selected command family: package main/main_test
@@ -581,8 +587,10 @@ struct Checker {
* currently being checked; NULL for primary
* compilation unit. Drives same-module
* preference in bare-leaf lookups so a bare
* `read` inside lib/os resolves to os.read
* rather than colliding io.read. */
* `read` inside lib/os resolves to os.read
* rather than colliding io.read. */
int cur_source; /* lexical source-file scope of the declaration
* currently being checked. */
Node *file; /* current N_FILE root; used by check_module_shadow
* to consult the declaring source file's own `use`
* directives when refusing param/let names that
@@ -596,6 +604,8 @@ struct Checker {
* bodies and export compiler-private metadata,
* but do not synthesize an entry. */
const char *test_module; /* generated dispatcher support qualifier */
const char *test_target; /* canonical target path used only as the
* compiler-owned generated-main qualifier */
int sep_mode; /* -c package compilation: imported interfaces are
* present, so absent members are hard export errors. */
Node *synth_test_run; /* exact compiler-generated support.run DOT;
@@ -620,6 +630,6 @@ int fold_int_literal(Node*, u64*);
/* Re-evaluate a checked integer constant under the declaration owner's
* import scope. The compiler export writer uses this to canonicalize array
* dimensions without serializing source-level constant dependencies. */
int check_eval_const(Checker*, Node*, const char *owner, u64*);
int check_eval_const(Checker*, Node*, const char *owner, int source, u64*);
#endif /* WW_H */