ww: implement package initialization

This commit is contained in:
2026-08-14 19:02:35 +09:00
parent 72e890d5aa
commit c6bec0914d
24 changed files with 5279 additions and 1009 deletions

View File

@@ -7,6 +7,8 @@
* diagnostics from one run. Nodes get their resolved Type attached.
*/
#include "ww.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
static void cstmt(Checker*, Node*);
@@ -1448,6 +1450,7 @@ cexpr(Checker *c, Node *n)
* here; the DOT case below resolves the qualified symbol. */
if (s->kind == SK_USE)
return n->type = ty_err;
n->refdecl = s->decl;
n->type = s->type;
return s->type;
}
@@ -1499,8 +1502,10 @@ cexpr(Checker *c, Node *n)
return n->type = err(c, n->pos,
"package '%s' has no exported declaration '%s'",
n->lhs->str, n->str);
if (fs)
if (fs) {
n->refdecl = fs->decl;
return n->type = fs->type;
}
/* A bare `w6c -T` intentionally leaves the
* compiler-generated support.run hook external; the
* ordinary driver supplies lib/test. This is the only
@@ -3006,7 +3011,8 @@ 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 || u->sourceid != source) continue;
if (u->kind != N_USE || u->useblank
|| 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
@@ -3132,6 +3138,680 @@ top_decl_kind(Node *d)
|| d->kind == N_FNDECL || d->kind == N_LET);
}
static int
init_private_symbol(Node *d)
{
for (Node *a = d ? d->attr : NULL; a; a = a->next) {
if (a->kind != N_ATTR || a->str == NULL
|| strcmp(a->str, "symbol") != 0 || a->list == NULL
|| a->list->kind != N_STRLIT || a->list->str == NULL)
continue;
if (strncmp(a->list->str, "__ww..", 6) == 0)
return 1;
}
return 0;
}
/* Mutable module lets are WW's Go-variable analogue. Literal data stays in
* the archive's static image; every surviving value computation is moved to
* the package task. `def` and `const` deliberately remain outside this path. */
static Node *
init_strip_cast(Node *n)
{
while (n != NULL && n->kind == N_CAST) n = n->lhs;
return n;
}
static int init_expr_static(Type *, Node *);
static int init_array_static(Type *, Node *, unsigned);
static int
init_fnptr_static(Node *n)
{
Node *r = init_strip_cast(n);
if (r == NULL || r->kind != N_UN || r->op != TK_AMP) return 0;
Node *v = init_strip_cast(r->lhs);
Type *vt = type_chase_named(v ? v->type : NULL);
if (v == NULL || vt == NULL || vt->kind != TY_FN) return 0;
if (v->kind == N_IDENT) return 1;
return v->kind == N_DOT && v->lhs != NULL
&& v->lhs->kind == N_IDENT
&& (v->lhs->type == NULL || v->lhs->type == ty_err);
}
static int
init_float_static(Node *n)
{
Node *r = init_strip_cast(n);
if (r != NULL && r->kind == N_UN
&& (r->op == TK_PLUS || r->op == TK_MINUS))
r = init_strip_cast(r->lhs);
return r != NULL && r->kind == N_FLOATLIT;
}
static int
init_tagged_raw_static(Type *u, Node *n)
{
Node *r = init_strip_cast(n);
Type *ru = type_chase_named(r ? r->type : NULL);
u64 ignored;
return u != NULL && u->kind == TY_TAGGED && !u->nullable
&& r != NULL && variant_present(u->params, r->type)
&& (ru == NULL || (ru->kind != TY_STR && ru->kind != TY_SLICE))
&& fold_int_literal(r, &ignored);
}
static int
init_tuple_static(Type *u, Node *n)
{
Node *r = init_strip_cast(n);
if (u == NULL || u->kind != TY_TUPLE
|| r == NULL || r->kind != N_TUPLE)
return 0;
Tparam *tp = u->params;
for (Node *e = r->list; e; e = e->next) {
if (tp == NULL) return 0;
Node *v = init_strip_cast(e);
Type *et = type_chase_named(tp->type);
if (v == NULL || (et != NULL && et->kind == TY_TAGGED)) return 0;
if (et != NULL && (et->kind == TY_STR || et->kind == TY_SLICE)) {
if (v->kind != N_STRLIT) return 0;
} else if (!init_fnptr_static(v)) {
u64 ignored;
if (!fold_int_literal(v, &ignored)) return 0;
}
tp = tp->next;
}
return tp == NULL;
}
static int
init_struct_static(Type *u, Node *n)
{
Node *r = init_strip_cast(n);
if (u == NULL || u->kind != TY_STRUCT
|| r == NULL || r->kind != N_STRUCTLIT)
return 0;
for (Tfield *f = u->fields; f; f = f->next) {
Node *value = NULL;
for (Node *e = r->list; e; e = e->next)
if (e->str != NULL && f->name != NULL
&& strcmp(e->str, f->name) == 0) {
value = e->lhs;
break;
}
if (value != NULL) {
Type *fu = type_chase_named(f->type);
int ok = 0;
if (fu != NULL && fu->kind == TY_TAGGED && !fu->nullable)
ok = init_tagged_raw_static(fu, value);
else if (fu != NULL && fu->kind == TY_STRUCT)
ok = init_struct_static(fu, value);
else if (fu != NULL && fu->kind == TY_ARRAY)
ok = init_array_static(fu, value, 1);
else if (type_isfloat(f->type))
ok = init_float_static(value);
else {
u64 ignored;
ok = fold_int_literal(init_strip_cast(value), &ignored);
}
if (!ok) return 0;
}
}
return 1;
}
#define INIT_ARR_REPEAT 1u
#define INIT_ARR_STR_RELOC 2u
#define INIT_ARR_TUPLE_ROWS 4u
static int
init_array_static(Type *u, Node *n, unsigned flags)
{
Node *r = init_strip_cast(n);
if (u == NULL || (u->kind != TY_ARRAY && u->kind != TY_SLICE)
|| r == NULL || r->kind != N_ARRLIT)
return 0;
Type *et = u->sub;
Type *eu = type_chase_named(et);
int seen = 0;
for (Node *e = r->list; e; e = e->next) {
if (e->kind == N_FIELD && e->str != NULL
&& strcmp(e->str, "...") == 0)
return (flags & INIT_ARR_REPEAT) != 0 && seen > 0
&& e->next == NULL
&& (eu == NULL || eu->kind != TY_ARRAY);
Node *v = init_strip_cast(e);
if (v == NULL) return 0;
if (eu != NULL && eu->kind == TY_STR) {
if ((flags & INIT_ARR_STR_RELOC) == 0
|| v->kind != N_STRLIT) return 0;
} else if (eu != NULL && eu->kind == TY_STRUCT) {
if (!init_struct_static(eu, v)) return 0;
} else if (eu != NULL && eu->kind == TY_ARRAY) {
if (!init_array_static(eu, v, INIT_ARR_REPEAT)) return 0;
} else if (eu != NULL && eu->kind == TY_TUPLE) {
if ((flags & INIT_ARR_TUPLE_ROWS) == 0
|| !init_tuple_static(eu, v)) return 0;
} else if (eu != NULL && eu->kind == TY_TAGGED) {
if (!init_tagged_raw_static(eu, v)) return 0;
} else if (eu != NULL && (eu->kind == TY_SLICE
|| eu->kind == TY_PTR
|| eu->kind == TY_FN)) {
return 0;
} else if (type_isfloat(et)) {
if (!init_float_static(v)) return 0;
} else {
u64 ignored;
if (!fold_int_literal(v, &ignored)) return 0;
}
seen++;
}
return 1;
}
/* This predicate is the checker's validate-only twin of cgen's static-data
* arms. A true result must be safe to emit; every other valid mutable value
* is zero-backed and evaluated exactly once by the package task. */
static int
init_expr_static(Type *t, Node *n)
{
Node *r = init_strip_cast(n);
if (r == NULL) return 1;
Type *u = type_chase_named(t);
if (type_isfloat(t)) return init_float_static(r);
if (u != NULL && (u->kind == TY_STR || u->kind == TY_UNTYPED_STR))
return r->kind == N_STRLIT || r->kind == N_NIL;
if (u != NULL && u->kind == TY_ARRAY)
return init_array_static(u, r,
INIT_ARR_REPEAT | INIT_ARR_STR_RELOC);
if (u != NULL && u->kind == TY_STRUCT)
return init_struct_static(u, r);
if (u != NULL && u->kind == TY_TUPLE)
return init_tuple_static(u, r);
if (u != NULL && u->kind == TY_SLICE) {
if (r->kind == N_NIL) return 1;
return init_array_static(u, r, INIT_ARR_TUPLE_ROWS);
}
if (u != NULL && u->kind == TY_TAGGED && !u->nullable) {
Type *ru = type_chase_named(r->type);
if (!variant_present(u->params, r->type)) return 0;
if (ru != NULL && (ru->kind == TY_STR || ru->kind == TY_SLICE))
return r->kind == N_STRLIT;
return init_tagged_raw_static(u, r);
}
if (init_fnptr_static(r)) return 1;
u64 ignored;
return fold_int_literal(r, &ignored);
}
struct initwalkitem {
Node *node;
struct initwalkitem *next;
};
struct initwalk {
struct initwalkitem *stack;
struct initwalkitem *seenfn;
};
static int
initwalk_push(struct initwalkitem **head, Node *x)
{
if (x == NULL) return 0;
struct initwalkitem *p = malloc(sizeof *p);
if (p == NULL) return -1;
p->node = x;
p->next = *head;
*head = p;
return 0;
}
static int
initwalk_seen_fn(struct initwalk *w, Node *fn)
{
for (struct initwalkitem *p = w->seenfn; p; p = p->next)
if (p->node == fn) return 1;
if (initwalk_push(&w->seenfn, fn) < 0) return -1;
return 0;
}
static void
initwalk_free(struct initwalkitem *p)
{
while (p != NULL) {
struct initwalkitem *next = p->next;
free(p);
p = next;
}
}
/* Does variable `from` depend on `target`? Function nodes are transparent,
* as in go/types initOrder: references in their bodies become variable edges. */
static int
init_refers(Node *from, Node *target)
{
struct initwalk w = {0};
int result = 0;
if (initwalk_push(&w.stack, from->rhs) < 0)
result = -1;
while (result == 0 && w.stack != NULL) {
struct initwalkitem *top = w.stack;
Node *n = top->node;
w.stack = top->next;
free(top);
if (n->refdecl == target) {
result = 1;
break;
}
Node *r = n->refdecl;
if (r != NULL && r->kind == N_FNDECL && !r->imported
&& !r->initfn && r->body != NULL) {
int seen = initwalk_seen_fn(&w, r);
if (seen < 0) { result = -1; break; }
if (!seen && initwalk_push(&w.stack, r->body) < 0) {
result = -1;
break;
}
}
Node *child[] = { n->attr, n->lhs, n->rhs, n->cond,
n->body, n->els, n->list, n->next };
for (size_t i = 0; result == 0 && i < nelem(child); i++)
if (initwalk_push(&w.stack, child[i]) < 0)
result = -1;
}
initwalk_free(w.stack);
initwalk_free(w.seenfn);
return result;
}
static void
init_cycle_note(Node *from, Node *to)
{
FILE *f = errout ? errout : stderr;
fprintf(f, "\t%s:%d:%d: %s refers to %s\n",
from->pos.file ? from->pos.file : "?", from->pos.line,
from->pos.col, from->str, to->str);
}
/* Go's findPath, iteratively: dependencies are visited in source order and a
* global seen set prevents a side cycle from consuming the native stack. */
static int
init_find_cycle(Checker *c, Node **vars, size_t nvar, size_t start)
{
unsigned char *seen = calloc(nvar, 1);
size_t *path = malloc(nvar * sizeof *path);
size_t *next = calloc(nvar, sizeof *next);
if (seen == NULL || path == NULL || next == NULL) {
free(next); free(path); free(seen);
err(c, vars[start]->pos,
"out of memory while ordering package initialization");
return -1;
}
size_t depth = 1;
path[0] = start;
seen[start] = 1;
while (depth > 0) {
size_t from = path[depth - 1];
int descended = 0;
while (next[depth - 1] < nvar) {
size_t to = next[depth - 1]++;
int dep = init_refers(vars[from], vars[to]);
if (dep < 0) {
err(c, vars[start]->pos,
"out of memory while ordering package initialization");
free(next); free(path); free(seen);
return -1;
}
if (!dep) continue;
if (to == start) {
if (depth == 1) {
err(c, vars[start]->pos,
"initialization cycle: %s refers to itself",
vars[start]->str);
} else {
err(c, vars[start]->pos,
"initialization cycle for %s",
vars[start]->str);
for (size_t i = 1; i < depth; i++)
init_cycle_note(vars[path[i - 1]],
vars[path[i]]);
init_cycle_note(vars[path[depth - 1]],
vars[start]);
}
free(next); free(path); free(seen);
return 1;
}
if (seen[to]) continue;
seen[to] = 1;
path[depth] = to;
next[depth] = 0;
depth++;
descended = 1;
break;
}
if (!descended) depth--;
}
free(next); free(path); free(seen);
return 0;
}
static Node *
init_make_call(Checker *c, Node *fn, Pos pos)
{
Node *id = newnode(c->a, N_IDENT, pos);
id->str = fn->str;
id->strlen = strlen(fn->str);
id->type = fn->type;
id->refdecl = fn;
Node *call = newnode(c->a, N_CALL, pos);
call->lhs = id;
call->type = ty_void;
Node *stmt = newnode(c->a, N_EXPRSTMT, pos);
stmt->lhs = call;
return stmt;
}
static u64
init_arrlit_count(Node *lit)
{
u64 count = 0;
for (Node *e = lit ? lit->list : NULL; e; e = e->next) {
if (e->kind == N_FIELD && e->str != NULL
&& strcmp(e->str, "...") == 0)
continue;
count++;
}
return count;
}
/* Runtime slice literals cannot use the ordinary local-literal backing: that
* storage dies when the compiler-generated variable helper returns. Attach a
* canonical package-owned backing symbol to every slice literal contained in
* the value being published. The cgen emits zeroed writable storage for the
* symbol and fills it at the literal's exact evaluation point.
*
* The name is derived only from the action-owned package-init symbol, the
* Go-ordered variable ordinal, and literal preorder. Source names, import
* aliases, declared package names, and path leaves never enter the identity. */
static void
init_mark_slice_backings(Checker *c, Type *want, Node *expr,
const char *base, u64 order, u64 *preorder)
{
Node *r = init_strip_cast(expr);
Type *u = type_chase_named(want);
if (r == NULL || u == NULL) return;
if (u->kind == TY_SLICE && r->kind == N_ARRLIT) {
u64 count = init_arrlit_count(r);
(*preorder)++;
r->linksym = aprintf(c->a, "%s.v.%llu.b.%llu", base,
(unsigned long long)order,
(unsigned long long)*preorder);
r->type = type_array(c->a, u->sub, count);
for (Node *e = r->list; e; e = e->next) {
if (e->kind == N_FIELD && e->str != NULL
&& strcmp(e->str, "...") == 0)
continue;
init_mark_slice_backings(c, u->sub, e, base, order,
preorder);
}
return;
}
if (u->kind == TY_ARRAY && r->kind == N_ARRLIT) {
for (Node *e = r->list; e; e = e->next) {
if (e->kind == N_FIELD && e->str != NULL
&& strcmp(e->str, "...") == 0)
continue;
init_mark_slice_backings(c, u->sub, e, base, order,
preorder);
}
return;
}
if (u->kind == TY_STRUCT && r->kind == N_STRUCTLIT) {
for (Node *e = r->list; e; e = e->next) {
Tfield *field = NULL;
for (Tfield *f = u->fields; f; f = f->next)
if (e->str != NULL && f->name != NULL
&& strcmp(e->str, f->name) == 0) {
field = f;
break;
}
if (field != NULL)
init_mark_slice_backings(c, field->type, e->lhs,
base, order, preorder);
}
return;
}
if (u->kind == TY_TUPLE && r->kind == N_TUPLE) {
Tparam *p = u->params;
for (Node *e = r->list; e && p; e = e->next, p = p->next)
init_mark_slice_backings(c, p->type, e, base, order,
preorder);
}
}
static Node *
init_make_helper(Checker *c, Node *d, const char *base)
{
Node *fn = newnode(c->a, N_FNDECL, d->pos);
fn->str = aprintf(c->a, "__ww_init_var_%llu",
(unsigned long long)d->initorder);
fn->strlen = strlen(fn->str);
fn->module = d->module;
fn->pkgname = d->pkgname;
fn->sourceid = d->sourceid;
fn->initsynthetic = 1;
fn->linksym = aprintf(c->a, "%s.v.%llu", base,
(unsigned long long)d->initorder);
Type *ft = newtype(c->a, TY_FN);
ft->size = 8; ft->align = 8; ft->ret = ty_void;
fn->type = ft;
/* Evaluate into an addressable local first. The established local-let and
* full-value assignment paths cover arrays, structs, tuples, calls, and
* allocation without asking the static-data emitter to understand them. */
Node *tmp = newnode(c->a, N_LET, d->pos);
tmp->str = aprintf(c->a, "__ww_init_tmp_%llu",
(unsigned long long)d->initorder);
tmp->strlen = strlen(tmp->str);
tmp->lhs = d->lhs;
tmp->rhs = d->rhs;
tmp->type = d->type;
tmp->initsynthetic = 1;
Node *id = newnode(c->a, N_IDENT, d->pos);
id->str = d->str;
id->strlen = strlen(d->str);
id->type = d->type;
id->refdecl = d;
Node *value = newnode(c->a, N_IDENT, d->pos);
value->str = tmp->str;
value->strlen = tmp->strlen;
value->type = d->type;
value->refdecl = tmp;
Node *assign = newnode(c->a, N_ASSIGN, d->pos);
assign->op = TK_ASSIGN;
assign->lhs = id;
assign->rhs = value;
assign->type = d->type;
Node *stmt = newnode(c->a, N_EXPRSTMT, d->pos);
stmt->lhs = assign;
Node *body = newnode(c->a, N_BLOCK, d->pos);
body->list = tmp;
tmp->next = stmt;
fn->body = body;
d->rhs = NULL;
return fn;
}
static int
init_lower_package(Checker *c, Node *file)
{
int nvar = 0;
u64 nruntime = 0;
u64 ninit = 0;
Node *firstinit = NULL;
for (Node *d = file->list; d; d = d->next) {
if (d->initfn && !d->imported) {
ninit++;
if (firstinit == NULL) firstinit = d;
}
if (d->kind != N_LET || d->imported || d->rhs == NULL)
continue;
if (d->op == TK_CONST) continue;
if (nvar == INT_MAX) {
err(c, d->pos,
"out of memory while ordering package initialization");
return -1;
}
nvar++;
if (!init_expr_static(d->type, d->rhs)) {
d->runtimeinit = 1;
nruntime++;
if (firstinit == NULL) firstinit = d;
}
}
if (c->errs) return -1;
/* A separate package with executable initialization must have the exact
* action-owned symbol supplied by its driver. Legacy non-separate mode
* retains its historical private fallback. */
if (c->sep_mode && (c->package_init_symbol == NULL
|| c->package_init_symbol[0] == '\0')
&& (nruntime != 0 || ninit != 0)) {
err(c, firstinit ? firstinit->pos : file->pos,
"package initialization requires --package-init-symbol under -c");
return -1;
}
if ((c->package_init_symbol == NULL
|| c->package_init_symbol[0] == '\0') && nruntime == 0 && ninit == 0)
return 0;
Node **vars = NULL;
if (nvar != 0) {
if ((size_t)nvar > (size_t)-1 / sizeof *vars
|| (vars = malloc((size_t)nvar * sizeof *vars)) == NULL) {
err(c, file->pos,
"out of memory while ordering package initialization");
return -1;
}
int vi = 0;
for (Node *d = file->list; d; d = d->next)
if (d->kind == N_LET && !d->imported && d->rhs != NULL
&& d->op != TK_CONST)
vars[vi++] = d;
}
int done = 0;
while (done < nvar) {
size_t best = (size_t)-1;
size_t bestdeps = (size_t)-1;
for (int i = 0; i < nvar; i++) {
if (vars[i]->initorder != 0) continue;
size_t ndeps = 0;
for (int j = 0; j < nvar; j++) {
if (vars[j]->initorder != 0) continue;
int dep = init_refers(vars[i], vars[j]);
if (dep < 0) {
err(c, vars[i]->pos,
"out of memory while ordering package initialization");
free(vars);
return -1;
}
if (dep) ndeps++;
}
if (best == (size_t)-1 || ndeps < bestdeps) {
best = (size_t)i;
bestdeps = ndeps;
}
}
if (best == (size_t)-1) break;
if (bestdeps != 0) {
int cycle = init_find_cycle(c, vars, (size_t)nvar, best);
if (cycle < 0) { free(vars); return -1; }
/* A reported cycle is broken by removing this node, exactly
* like go/types' priority-queue walk. Continue so disjoint or
* overlapping later cycles retain their deterministic errors. */
}
vars[best]->initorder = (u64)++done;
}
free(vars);
if (c->errs) return -1;
const char *base = c->package_init_symbol;
if (base == NULL || base[0] == '\0')
base = "__ww..pkg.v0.r0.e.init";
Node *tail = file->list;
while (tail && tail->next) tail = tail->next;
Node *helpers = NULL, *helpertail = NULL;
for (u64 order = 1; order <= (u64)nvar; order++)
for (Node *d = file->list; d; d = d->next)
if (d->runtimeinit && d->initorder == order) {
u64 preorder = 0;
init_mark_slice_backings(c, d->type, d->rhs,
base, order, &preorder);
Node *fn = init_make_helper(c, d, base);
if (helpers == NULL) helpers = fn;
else helpertail->next = fn;
helpertail = fn;
break;
}
if (tail) tail->next = helpers;
else file->list = helpers;
if (helpertail) tail = helpertail;
Node *task = newnode(c->a, N_FNDECL, file->pos);
task->str = "__ww_init_task";
task->strlen = strlen(task->str);
task->initsynthetic = 1;
task->linksym = base;
Type *tt = newtype(c->a, TY_FN);
tt->size = 8; tt->align = 8; tt->ret = ty_void;
task->type = tt;
Node *body = newnode(c->a, N_BLOCK, file->pos);
Node *stail = NULL;
for (Node *fn = helpers; fn; fn = fn->next) {
Node *s = init_make_call(c, fn, fn->pos);
if (body->list == NULL) body->list = s;
else stail->next = s;
stail = s;
}
for (Node *d = file->list; d; d = d->next) {
if (!d->initfn || d->imported) continue;
Node *s = init_make_call(c, d, d->pos);
if (body->list == NULL) body->list = s;
else stail->next = s;
stail = s;
}
task->body = body;
if (tail) tail->next = task;
else file->list = task;
return 0;
}
static void
classify_init_decls(Checker *c, Node *file)
{
u64 ordinal = 0;
for (Node *d = file->list; d; d = d->next) {
if (init_private_symbol(d))
err(c, d->pos, "@symbol name uses reserved prefix __ww..");
if (d->str == NULL || strcmp(d->str, "init") != 0
|| !top_decl_kind(d))
continue;
if (d->kind != N_FNDECL) {
err(c, d->pos, "cannot declare init - must be func");
continue;
}
d->initfn = 1;
d->initorder = ++ordinal;
if (d->export)
err(c, d->pos, "func init cannot be exported");
if (d->body == NULL)
err(c, d->pos, "func init must have a body");
if (d->attr != NULL)
err(c, d->pos, "func init cannot have attributes");
}
}
/* Import usage is a property of the file-local qualifier occurrence. Record
* qualified syntax before resolving declaration bodies so import diagnostics
* retain production Go's source order without making a failed bare lookup a
@@ -3186,10 +3866,12 @@ 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)
if (u->kind != N_USE || u->useblank
|| 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
if (v->kind != N_USE || v->useblank
|| v->imported || v->str == NULL
|| v->sourceid != u->sourceid)
continue;
if (strcmp(v->str, u->str) == 0) {
@@ -3210,7 +3892,8 @@ 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)
if (u->kind != N_USE || u->useblank
|| u->imported || u->used || u->str == NULL)
continue;
const char *path = u->usesource ? u->usesource
: u->usepath ? u->usepath : u->str;
@@ -3226,7 +3909,8 @@ check_import_usage_and_collisions(Checker *c, Node *file)
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
if (u->kind != N_USE || u->useblank
|| u->imported || u->str == NULL
|| strcmp(d->str, u->str) != 0)
continue;
const char *path = u->usesource ? u->usesource
@@ -3287,6 +3971,7 @@ check_file(Checker *c, Node *file)
mark_import_uses(c, file);
check_import_redeclarations(c, file);
check_import_usage_and_collisions(c, file);
classify_init_decls(c, file);
/* pass 1: install names (types first, then defs/fns).
* For self-referential types we install the named-type placeholder
@@ -3308,6 +3993,12 @@ check_file(Checker *c, Node *file)
&& strcmp(d->usepath, owner) == 0)
err(c, d->pos, "self-import: package "
"'%s' cannot import itself", owner);
if (d->useblank)
continue;
if (d->str != NULL && strcmp(d->str, "init") == 0) {
err(c, d->pos, "cannot import package as init - init must be a func");
continue;
}
Sym *prev = scope_lookup_local(c->cur, d->str);
if (prev != NULL) {
/* Self-import: the driver concatenates the
@@ -3323,6 +4014,7 @@ check_file(Checker *c, Node *file)
continue;
}
if (d->kind != N_TYPEDECL) continue;
if (d->str != NULL && strcmp(d->str, "init") == 0) continue;
const char *mod = decl_mod(file, d);
Sym *fact = same_import_fact(c, d, mod, SK_TYPE);
if (fact != NULL) {
@@ -3357,6 +4049,7 @@ check_file(Checker *c, Node *file)
c->cur_source = 0;
for (Node *d = file->list; d; d = d->next) {
if (d->kind != N_DEF) continue;
if (d->str != NULL && strcmp(d->str, "init") == 0) continue;
c->cur_mod = decl_mod(file, d);
c->cur_source = d->sourceid;
const char *mod = decl_mod(file, d);
@@ -3390,6 +4083,10 @@ check_file(Checker *c, Node *file)
break;
case N_DEF: {
Type *t = resolve_type(c, d->lhs);
if (d->str != NULL && strcmp(d->str, "init") == 0) {
d->type = t;
break;
}
const char *mod = decl_mod(file, d);
Sym *fact = same_import_fact(c, d, mod, SK_DEF);
if (fact != NULL) {
@@ -3419,6 +4116,18 @@ check_file(Checker *c, Node *file)
case N_FNDECL: {
Type *t = build_fn_type(c, d);
d->type = t;
if (d->initfn) {
Type *ret = type_chase_named(t->ret);
if (d->list != NULL || ret != ty_void)
err(c, d->pos, "func init must have no arguments and no return values");
const char *base = c->package_init_symbol;
if ((base == NULL || base[0] == '\0') && !c->sep_mode)
base = "__ww..pkg.v0.r0.e.init";
if (base != NULL && base[0] != '\0')
d->linksym = aprintf(c->a, "%s.f.%llu", base,
(unsigned long long)d->initorder);
break;
}
Sym *prev = scope_lookup_local(c->cur, d->str);
const char *mod = decl_mod(file, d);
if (prev && prev->kind == SK_USE) {
@@ -3444,26 +4153,8 @@ check_file(Checker *c, Node *file)
if (t)
require_sized(c, t, d->pos, "a variable");
d->type = t;
/* A module-scope initializer must be link-time data:
* an alloc/call rhs runs code, emit_lets' fold-fail
* skipped the DATAW slot silently, and every
* reference died at LINK time ("undefined reference")
* — reject at the declaration instead (rule 7).
* Hare rejects at check time too (ref/harec/src/
* check.c:4360 "Unable to evaluate initializer at
* compile time"); ww has no @init path. */
{
Node *r = d->rhs;
while (r != NULL && (r->kind == N_CAST
|| r->kind == N_TRYPROP
|| r->kind == N_TRYUNW))
r = r->lhs;
if (r != NULL && (r->kind == N_ALLOC
|| r->kind == N_CALL))
err(c, d->pos, "module-scope let %s: "
"runtime initializer unsupported "
"(alloc/call; rule 7)", d->str);
}
if (d->str != NULL && strcmp(d->str, "init") == 0)
break;
if (d->str && d->str[0]) {
Sym *prev = scope_lookup_local(c->cur, d->str);
const char *mod = decl_mod(file, d);
@@ -3929,6 +4620,7 @@ check_file(Checker *c, Node *file)
}
c->cur_mod = NULL;
c->cur_source = 0;
(void)init_lower_package(c, file);
/*
* #6 harec-fidelity (ref/harec/src/check.c:3941): a @test fn is
* fully checked above — pass 2 walked its body like every fn — but