ww: implement package initialization
This commit is contained in:
283
cmd/w6c/cgen.c
283
cmd/w6c/cgen.c
@@ -1189,6 +1189,7 @@ ffi_collect(Cg *c, Node *file)
|
||||
if (file == NULL) return;
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
if (d->kind != N_FNDECL) continue;
|
||||
if (d->initfn || d->initsynthetic) continue;
|
||||
for (Node *a = d->attr; a; a = a->next) {
|
||||
if (a->kind != N_ATTR) continue;
|
||||
if (strcmp(a->str, "symbol") != 0) continue;
|
||||
@@ -1487,6 +1488,7 @@ mod_collect(Cg *c, Node *file)
|
||||
use_map = u;
|
||||
continue;
|
||||
}
|
||||
if (d->initfn || d->initsynthetic) continue;
|
||||
int isfn = (d->kind == N_FNDECL);
|
||||
int track = isfn || (d->kind == N_TYPEDECL)
|
||||
|| (d->kind == N_DEF) || (d->kind == N_LET);
|
||||
@@ -4196,6 +4198,238 @@ cg_arrlit_fill_bp(Cg *c, Local **locals, Type *lu, Node *arrlit, int off)
|
||||
}
|
||||
}
|
||||
|
||||
/* Package-variable helpers publish their local only after the complete value
|
||||
* has been evaluated. Composite literals need a memory-directed path here:
|
||||
* the ordinary tuple cursor has finite register capacity, nested aggregates
|
||||
* are not scalar expressions, and an ordinary slice literal points at stack
|
||||
* storage that dies when the helper returns. This path is gated by the
|
||||
* checker-owned N_LET.initsynthetic bit and therefore cannot perturb normal
|
||||
* user locals or any statically emitted initializer. */
|
||||
static void cg_init_value_bp(Cg*, Local**, Type*, Node*, int);
|
||||
|
||||
static void
|
||||
cg_init_zero_bp(Cg *c, int off, int sz)
|
||||
{
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
int k = 0;
|
||||
for (; k + 8 <= sz; k += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + k));
|
||||
if (k + 4 <= sz) {
|
||||
ins2(c, A_MOVL, areg(D_AX), amem(D_BP, off + k));
|
||||
k += 4;
|
||||
}
|
||||
if (k + 2 <= sz) {
|
||||
ins2(c, A_MOVW, areg(D_AX), amem(D_BP, off + k));
|
||||
k += 2;
|
||||
}
|
||||
if (k + 1 <= sz)
|
||||
ins2(c, A_MOVB, areg(D_AX), amem(D_BP, off + k));
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_copy_bp(Cg *c, int src, int dst, int sz)
|
||||
{
|
||||
ins2(c, A_LEAQ, amem(D_BP, src), areg(D_SI));
|
||||
ins2(c, A_LEAQ, amem(D_BP, dst), areg(D_BX));
|
||||
cg_aggcopy(c, sz);
|
||||
}
|
||||
|
||||
static Node *
|
||||
cg_init_strip_cast(Node *n)
|
||||
{
|
||||
while (n != NULL && n->kind == N_CAST) n = n->lhs;
|
||||
return n;
|
||||
}
|
||||
|
||||
static Tfield *
|
||||
cg_init_field(Type *u, const char *name)
|
||||
{
|
||||
for (Tfield *f = u ? u->fields : NULL; f; f = f->next)
|
||||
if (name != NULL && f->name != NULL
|
||||
&& strcmp(name, f->name) == 0)
|
||||
return f;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_array_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
|
||||
{
|
||||
Type *et = u->sub;
|
||||
int esz = et ? (int)type_chase_named(et)->size : 1;
|
||||
int total = (int)u->alen;
|
||||
cg_init_zero_bp(c, off, (int)u->size);
|
||||
int idx = 0, lastoff = 0;
|
||||
for (Node *e = lit->list; e; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str != NULL
|
||||
&& strcmp(e->str, "...") == 0) {
|
||||
if (idx == 0)
|
||||
fatal("package initializer array repeat has no value");
|
||||
while (idx < total) {
|
||||
cg_init_copy_bp(c, lastoff, off + idx * esz, esz);
|
||||
idx++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (idx >= total)
|
||||
fatal("package initializer array literal exceeds destination");
|
||||
lastoff = off + idx * esz;
|
||||
cg_init_value_bp(c, locals, et, e, lastoff);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_struct_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
|
||||
{
|
||||
cg_init_zero_bp(c, off, (int)u->size);
|
||||
for (Node *e = lit->list; e; e = e->next) {
|
||||
Tfield *f = cg_init_field(u, e->str);
|
||||
if (f == NULL) continue; /* checker owns unknown/ellipsis errors */
|
||||
cg_init_value_bp(c, locals, f->type, e->lhs,
|
||||
off + (int)f->offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_tuple_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
|
||||
{
|
||||
cg_init_zero_bp(c, off, (int)u->size);
|
||||
Node *e = lit->list;
|
||||
int eoff = 0;
|
||||
for (Tparam *p = u->params; p && e; p = p->next, e = e->next) {
|
||||
cg_init_value_bp(c, locals, p->type, e, off + eoff);
|
||||
eoff += tuple_eslot(p->type);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_slice_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
|
||||
{
|
||||
if (lit->linksym == NULL || lit->linksym[0] == '\0')
|
||||
fatal("runtime package slice literal has no canonical backing");
|
||||
Type *at = type_chase_named(lit->type);
|
||||
if (at == NULL || at->kind != TY_ARRAY || at->sub == NULL)
|
||||
fatal("runtime package slice literal has no backing type");
|
||||
int count = (int)at->alen;
|
||||
int bsz = (int)at->size;
|
||||
if (bsz != 0) {
|
||||
int scr = local_alloc(c, locals, "@initbacking", bsz, cg_frame);
|
||||
cg_init_array_bp(c, locals, at, lit, scr);
|
||||
ins2(c, A_LEAQ, amem(D_BP, scr), areg(D_SI));
|
||||
ins2(c, A_LEAQ, asym(lit->linksym), areg(D_BX));
|
||||
cg_aggcopy(c, bsz);
|
||||
}
|
||||
ins2(c, A_LEAQ, asym(lit->linksym), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off));
|
||||
ins2(c, A_MOVQ, aimm(count), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, aimm(count), amem(D_BP, off + 16));
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_aggregate_expr_bp(Cg *c, Local **locals, Type *t, Type *u,
|
||||
Node *expr, int off)
|
||||
{
|
||||
int sz = (int)u->size;
|
||||
if (expr->kind == N_CALL && cg_sret_retsize(t) > 0) {
|
||||
cg_sret_dest_off = off;
|
||||
cgexpr(c, expr, *locals);
|
||||
cg_sret_dest_off = 0;
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_TUPLE && cg_sret_retsize(t) == 0) {
|
||||
cgexpr(c, expr, *locals);
|
||||
int gpcur = 0, ssecur = 0, eoff = 0, f32;
|
||||
for (Tparam *p = u->params; p; p = p->next) {
|
||||
int isfloat = fld_isfloat(p->type, &f32);
|
||||
tuple_store(c, p->type, gpcur, ssecur, off + eoff);
|
||||
if (isfloat) ssecur++;
|
||||
else gpcur += tuple_eslot(p->type) / 8;
|
||||
eoff += tuple_eslot(p->type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (expr->kind == N_CALL && u->kind == TY_STRUCT) {
|
||||
int cls[2], nb = struct_float_class(u, cls);
|
||||
if (nb > 0) {
|
||||
cgexpr(c, expr, *locals);
|
||||
int gp = 0, sse = 0;
|
||||
for (int i = 0; i < nb; i++) {
|
||||
if (cls[i]) {
|
||||
ins2(c, A_MOVSD, areg(tuple_sse_seq[sse++]),
|
||||
amem(D_BP, off + i * 8));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[gp++]),
|
||||
amem(D_BP, off + i * 8));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (expr->kind == N_CALL && sz <= 24) {
|
||||
cgexpr(c, expr, *locals);
|
||||
cg_agg_reg_store(c, locals, D_BP, off, sz, 1);
|
||||
return;
|
||||
}
|
||||
if (aggarg_srcaddr(c, expr, D_SI, *locals)) {
|
||||
ins2(c, A_LEAQ, amem(D_BP, off), areg(D_BX));
|
||||
cg_aggcopy(c, sz);
|
||||
return;
|
||||
}
|
||||
fatal("package initializer aggregate expression shape unsupported");
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_value_bp(Cg *c, Local **locals, Type *t, Node *expr, int off)
|
||||
{
|
||||
Type *u = type_chase_named(t);
|
||||
Node *r = cg_init_strip_cast(expr);
|
||||
if (u == NULL || r == NULL)
|
||||
fatal("package initializer value has no type or expression");
|
||||
if (u->kind == TY_ARRAY && r->kind == N_ARRLIT) {
|
||||
cg_init_array_bp(c, locals, u, r, off);
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_STRUCT && r->kind == N_STRUCTLIT) {
|
||||
cg_init_struct_bp(c, locals, u, r, off);
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_TUPLE && r->kind == N_TUPLE) {
|
||||
cg_init_tuple_bp(c, locals, u, r, off);
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_SLICE && r->kind == N_ARRLIT) {
|
||||
cg_init_slice_bp(c, locals, u, r, off);
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_TAGGED) {
|
||||
cg_widen_tagged_store(c, locals, u, expr, D_BP, off,
|
||||
(int)u->size);
|
||||
return;
|
||||
}
|
||||
if (type_isstr(t) || u->kind == TY_SLICE) {
|
||||
cgexpr(c, expr, *locals);
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_ARRAY || u->kind == TY_STRUCT
|
||||
|| u->kind == TY_TUPLE) {
|
||||
cg_init_aggregate_expr_bp(c, locals, t, u, r, off);
|
||||
return;
|
||||
}
|
||||
cgexpr(c, expr, *locals);
|
||||
int f32 = 0;
|
||||
if (fld_isfloat(t, &f32)) {
|
||||
ins2(c, f32 ? A_MOVSS : A_MOVSD, areg(D_X0),
|
||||
amem(D_BP, off));
|
||||
return;
|
||||
}
|
||||
int sz = (int)u->size;
|
||||
if (sz != 1 && sz != 2 && sz != 4) sz = 8;
|
||||
ins2(c, fldstoreop(t, sz), areg(D_AX), amem(D_BP, off));
|
||||
}
|
||||
|
||||
/* cg_dotfield_combine — single-dot field compound combine. The old
|
||||
* field value is in BX, the rhs in AX; the result is left in AX.
|
||||
* PLUSEQ/MINUSEQ preserve the pre-#34 emission (byte-id); the other 8
|
||||
@@ -10941,7 +11175,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
else
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
}
|
||||
if (n->lhs->kind == N_IDENT) {
|
||||
if (n->lhs->kind == N_IDENT && n->lhs->refdecl != NULL
|
||||
&& n->lhs->refdecl->linksym != NULL) {
|
||||
ins1(c, A_CALL, asym(n->lhs->refdecl->linksym));
|
||||
} else if (n->lhs->kind == N_IDENT) {
|
||||
/* If the callee names a local variable holding a
|
||||
* function pointer, load it and call indirect. Without
|
||||
* this check `CALL fp(SB)` is emitted as if `fp` were
|
||||
@@ -13506,6 +13743,14 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
int off = letloc->off;
|
||||
int isf = cg_isfloat(lt);
|
||||
int isf32 = type_isf32(lt);
|
||||
Node *initlit = cg_init_strip_cast(n->rhs);
|
||||
if (n->initsynthetic && initlit != NULL
|
||||
&& (initlit->kind == N_ARRLIT
|
||||
|| initlit->kind == N_STRUCTLIT
|
||||
|| initlit->kind == N_TUPLE)) {
|
||||
cg_init_value_bp(c, locals, lt, n->rhs, off);
|
||||
goto letlink;
|
||||
}
|
||||
/* alloc([], n) initialiser for a slice local: allocate
|
||||
* n*esize bytes, build the {ptr, 0, n} header in the slot.
|
||||
* Element size comes from the declared slice type.
|
||||
@@ -15843,7 +16088,9 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
* !sep_isdep — under sep a dep unit's main is imported==0 (path-
|
||||
* carrying `//ww:module-reset`, #57); only the root/link-entry unit
|
||||
* (wwiout==NULL, #69) keeps the bare label. */
|
||||
if (fn->str && strcmp(fn->str, "main") == 0 && !fn->imported
|
||||
if (fn->linksym != NULL)
|
||||
text->to = asym(fn->linksym);
|
||||
else if (fn->str && strcmp(fn->str, "main") == 0 && !fn->imported
|
||||
&& !c->sep_isdep)
|
||||
text->to = asym("main");
|
||||
else
|
||||
@@ -16098,6 +16345,15 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
fatal("#38b: >48B tagged param mixed with stack-spilled "
|
||||
"params unwired");
|
||||
|
||||
/* The package coordinator owns one complete dependency-first schedule.
|
||||
* Only the selected source/generated main invokes it; package tasks never
|
||||
* recurse through dependencies, so independent-package tie order cannot
|
||||
* accidentally become linker or DFS order. */
|
||||
if (c->init_dispatch_symbol != NULL && fn->str != NULL
|
||||
&& strcmp(fn->str, "main") == 0 && !fn->imported
|
||||
&& !c->sep_isdep)
|
||||
ins1(c, A_CALL, asym(c->init_dispatch_symbol));
|
||||
|
||||
/* Iterate the fn body's statements directly rather than dispatching
|
||||
* the outermost N_BLOCK through cgstmt — N_BLOCK now save/restores
|
||||
* the locals head to scope inner shadows, but the function body is
|
||||
@@ -17674,6 +17930,28 @@ let_pre_intern(Cg *c, Node *file)
|
||||
c->cur_source = save_source;
|
||||
}
|
||||
|
||||
static void
|
||||
emit_init_backings(FILE *out, Node *n)
|
||||
{
|
||||
for (; n; n = n->next) {
|
||||
if (n->kind == N_ARRLIT && n->linksym != NULL
|
||||
&& n->linksym[0] != '\0') {
|
||||
Type *u = type_chase_named(n->type);
|
||||
if (u == NULL || u->kind != TY_ARRAY)
|
||||
fatal("runtime package slice backing has no array type");
|
||||
emit_data_row_zero(out, "DATAW", n->linksym,
|
||||
u->size == 0 ? 1 : (int)u->size);
|
||||
}
|
||||
emit_init_backings(out, n->attr);
|
||||
emit_init_backings(out, n->lhs);
|
||||
emit_init_backings(out, n->rhs);
|
||||
emit_init_backings(out, n->cond);
|
||||
emit_init_backings(out, n->body);
|
||||
emit_init_backings(out, n->els);
|
||||
emit_init_backings(out, n->list);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cg_file(Cg *c, FILE *out, Node *file)
|
||||
{
|
||||
@@ -17697,5 +17975,6 @@ cg_file(Cg *c, FILE *out, Node *file)
|
||||
let_pre_intern(c, file);
|
||||
emit_data(c, out);
|
||||
emit_defs(c, out, file);
|
||||
emit_init_backings(out, file->list);
|
||||
emit_lets(c, out, file);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ struct Cg {
|
||||
* the bare-`main` carve-out: a dep's `fn main` must
|
||||
* mangle on its path like any decl; only the root
|
||||
* entry stays bare. */
|
||||
const char *init_dispatch_symbol; /* package-mode entry hook; the
|
||||
* root-owned dispatcher runs the complete reachable
|
||||
* package-init schedule before source main/test main. */
|
||||
};
|
||||
|
||||
/* cgen.c */
|
||||
|
||||
262
cmd/w6c/main.c
262
cmd/w6c/main.c
@@ -1,7 +1,167 @@
|
||||
#include "gc.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct publication {
|
||||
const char *dst;
|
||||
char *stage;
|
||||
char *backup;
|
||||
int had_old;
|
||||
int installed;
|
||||
};
|
||||
|
||||
static char *
|
||||
publish_path(const char *dst, const char *kind)
|
||||
{
|
||||
int n = snprintf(NULL, 0, "%s.w6c.%ld.%s", dst, (long)getpid(), kind);
|
||||
if (n < 0 || (size_t)n == (size_t)-1) return NULL;
|
||||
char *p = malloc((size_t)n + 1);
|
||||
if (p == NULL) return NULL;
|
||||
if (snprintf(p, (size_t)n + 1, "%s.w6c.%ld.%s", dst,
|
||||
(long)getpid(), kind) != n) {
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static int
|
||||
materialize_stream(FILE *src, struct publication *p)
|
||||
{
|
||||
p->stage = publish_path(p->dst, "new");
|
||||
p->backup = publish_path(p->dst, "old");
|
||||
if (p->stage == NULL || p->backup == NULL) {
|
||||
fputs("w6c: out of memory\n", stderr);
|
||||
return -1;
|
||||
}
|
||||
int fd = open(p->stage, O_WRONLY | O_CREAT | O_EXCL, 0644);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "w6c: cannot stage %s\n", p->dst);
|
||||
return -1;
|
||||
}
|
||||
FILE *out = fdopen(fd, "wb");
|
||||
if (out == NULL) {
|
||||
close(fd);
|
||||
unlink(p->stage);
|
||||
return -1;
|
||||
}
|
||||
int bad = fflush(src) != 0 || fseek(src, 0, SEEK_SET) != 0;
|
||||
unsigned char buf[65536];
|
||||
while (!bad) {
|
||||
size_t n = fread(buf, 1, sizeof buf, src);
|
||||
if (n != 0 && fwrite(buf, 1, n, out) != n) bad = 1;
|
||||
if (n < sizeof buf) {
|
||||
if (ferror(src)) bad = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fclose(out) != 0) bad = 1;
|
||||
if (bad) {
|
||||
unlink(p->stage);
|
||||
fprintf(stderr, "w6c: cannot stage %s\n", p->dst);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
publication_free(struct publication *p)
|
||||
{
|
||||
free(p->backup);
|
||||
free(p->stage);
|
||||
}
|
||||
|
||||
/* A publication rollback name is occupied by any terminal directory entry,
|
||||
* including a dangling symlink. Never follow it while deciding whether the
|
||||
* compiler may park an existing destination there. */
|
||||
static int
|
||||
path_exists_nofollow(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path, &st) == 0) return 1;
|
||||
return errno == ENOENT ? 0 : -1;
|
||||
}
|
||||
|
||||
static int
|
||||
path_is_regular_nofollow(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
return lstat(path, &st) == 0 && S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
/* Publish assembly and interface as one rollback group. Named output bytes
|
||||
* never change before checking and lowering have both succeeded. */
|
||||
static int
|
||||
publish_all(struct publication *p, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (!path_is_regular_nofollow(p[i].stage)) {
|
||||
fprintf(stderr, "w6c: publication stage is not a regular file for %s\n",
|
||||
p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
struct stat st;
|
||||
if (lstat(p[i].dst, &st) == 0) {
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
fprintf(stderr,
|
||||
"w6c: publication destination is not a regular file: %s\n",
|
||||
p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
} else if (errno != ENOENT) {
|
||||
fprintf(stderr, "w6c: cannot inspect publication destination %s\n",
|
||||
p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
int exists = path_exists_nofollow(p[i].backup);
|
||||
if (exists != 0) {
|
||||
if (exists < 0)
|
||||
fprintf(stderr, "w6c: cannot inspect publication backup for %s\n",
|
||||
p[i].dst);
|
||||
else
|
||||
fprintf(stderr, "w6c: publication backup exists for %s\n",
|
||||
p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (rename(p[i].dst, p[i].backup) == 0)
|
||||
p[i].had_old = 1;
|
||||
else if (errno != ENOENT) {
|
||||
fprintf(stderr, "w6c: cannot preserve %s\n", p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (rename(p[i].stage, p[i].dst) != 0) {
|
||||
fprintf(stderr, "w6c: cannot publish %s\n", p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
p[i].installed = 1;
|
||||
}
|
||||
/* Installation is the commit point. Backup cleanup cannot truthfully
|
||||
* turn a completely installed pair into a rejected compilation. */
|
||||
for (int i = 0; i < n; i++)
|
||||
if (p[i].had_old && unlink(p[i].backup) != 0) {
|
||||
fprintf(stderr, "w6c: cannot remove backup for %s\n", p[i].dst);
|
||||
}
|
||||
return 0;
|
||||
|
||||
rollback:
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
if (p[i].installed) (void)unlink(p[i].dst);
|
||||
if (p[i].had_old) (void)rename(p[i].backup, p[i].dst);
|
||||
if (p[i].stage != NULL) (void)unlink(p[i].stage);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp(const char *path, char **outbuf, u64 *outlen)
|
||||
@@ -148,8 +308,10 @@ bind_import_names(Node *list, struct importin *imports, int nimports,
|
||||
}
|
||||
if (name != NULL) {
|
||||
u->usepkgname = name;
|
||||
u->str = u->usealias ? u->usealias : name;
|
||||
u->strlen = strlen(u->str);
|
||||
if (!u->useblank) {
|
||||
u->str = u->usealias ? u->usealias : name;
|
||||
u->strlen = strlen(u->str);
|
||||
}
|
||||
} else if (!u->imported) {
|
||||
fprintf(stderr,
|
||||
"w6c: import %s has no declared package name in direct export data\n",
|
||||
@@ -159,8 +321,10 @@ bind_import_names(Node *list, struct importin *imports, int nimports,
|
||||
/* A closure-only import need not contribute declarations to this
|
||||
* interface. Keep it canonical-path keyed without reinstalling
|
||||
* the historical path-leaf qualifier. */
|
||||
u->str = u->usealias ? u->usealias : u->usepath;
|
||||
u->strlen = strlen(u->str);
|
||||
if (!u->useblank) {
|
||||
u->str = u->usealias ? u->usealias : u->usepath;
|
||||
u->strlen = strlen(u->str);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -184,6 +348,8 @@ main(int argc, char **argv)
|
||||
const char *wwiout = NULL; /* -I <out.wwi>: M2 export-data producer */
|
||||
const char *testsupport = NULL;
|
||||
const char *testtarget = NULL;
|
||||
const char *packageinit = NULL;
|
||||
const char *initdispatch = NULL;
|
||||
int testmode = 0;
|
||||
int testpackage = 0;
|
||||
int commandpackage = 0;
|
||||
@@ -215,6 +381,18 @@ main(int argc, char **argv)
|
||||
commandpackage = 1;
|
||||
} else if (strcmp(a, "--entry") == 0) {
|
||||
entrymode = 1;
|
||||
} else if (strcmp(a, "--package-init-symbol") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fputs("w6c: --package-init-symbol requires arg\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
packageinit = argv[++i];
|
||||
} else if (strcmp(a, "--init-dispatch-symbol") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fputs("w6c: --init-dispatch-symbol requires arg\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
initdispatch = argv[++i];
|
||||
} else if (strcmp(a, "--test-support-module") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fputs("w6c: --test-support-module requires arg\n", stderr);
|
||||
@@ -256,10 +434,14 @@ main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
if (src == NULL) {
|
||||
fputs("usage: w6c [-T|--test-package] [--command-package] [--entry] [--test-target-package path] [-c] [-I out.wwi] "
|
||||
fputs("usage: w6c [-T|--test-package] [--command-package] [--entry] [--package-init-symbol symbol] [--init-dispatch-symbol symbol] [--test-target-package path] [-c] [-I out.wwi] "
|
||||
"[--import path dep.wwi]... [--import-map source path]... [-o out.s] file.ww\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
if (out != NULL && wwiout != NULL && strcmp(out, wwiout) == 0) {
|
||||
fputs("w6c: assembly and interface outputs must be distinct\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
if (nimports > 0 && !sepmode) {
|
||||
fputs("w6c: --import requires -c\n", stderr);
|
||||
return 2;
|
||||
@@ -272,6 +454,19 @@ main(int argc, char **argv)
|
||||
fputs("w6c: --entry, --test-package, and --command-package require -c\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
if ((packageinit != NULL || initdispatch != NULL) && !sepmode) {
|
||||
fputs("w6c: package initialization symbols require -c\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
if (packageinit != NULL && packageinit[0] == '\0') {
|
||||
fputs("w6c: --package-init-symbol is empty\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
if (initdispatch != NULL && (initdispatch[0] == '\0' || !entrymode)) {
|
||||
fputs("w6c: --init-dispatch-symbol requires --entry and a non-empty symbol\n",
|
||||
stderr);
|
||||
return 2;
|
||||
}
|
||||
if (testmode && testpackage) {
|
||||
fputs("w6c: -T and --test-package are mutually exclusive\n", stderr);
|
||||
return 2;
|
||||
@@ -427,27 +622,33 @@ main(int argc, char **argv)
|
||||
if (testsupport != NULL) c.test_module = testsupport;
|
||||
c.test_target = testtarget;
|
||||
c.sep_mode = sepmode;
|
||||
c.package_init_symbol = packageinit;
|
||||
check_file(&c, file);
|
||||
if (c.errs) return 1;
|
||||
|
||||
/* M2 export-data: write the `.wwi` after a clean check, before cgen.
|
||||
* Dead on the live path (no existing invocation passes -I); the
|
||||
* producer's check_exported_type may reject a dangling export. */
|
||||
/* Anonymous streams keep every named destination untouched until checking,
|
||||
* export writing, and lowering have all succeeded. A cgen fatal exits with
|
||||
* only kernel-owned anonymous files open. */
|
||||
FILE *wf = NULL;
|
||||
if (wwiout) {
|
||||
FILE *wf = fopen(wwiout, "wb");
|
||||
wf = tmpfile();
|
||||
if (wf == NULL) {
|
||||
fprintf(stderr, "w6c: cannot open %s\n", wwiout);
|
||||
fprintf(stderr, "w6c: cannot stage %s\n", wwiout);
|
||||
return 1;
|
||||
}
|
||||
if (wwi_emit(&c, wf, file) != 0 || fflush(wf) != 0
|
||||
|| ferror(wf)) {
|
||||
fclose(wf);
|
||||
return 1;
|
||||
}
|
||||
if (wwi_emit(&c, wf, file) != 0) { fclose(wf); return 1; }
|
||||
fclose(wf);
|
||||
}
|
||||
|
||||
FILE *of = stdout;
|
||||
if (out) {
|
||||
of = fopen(out, "wb");
|
||||
of = tmpfile();
|
||||
if (of == NULL) {
|
||||
fprintf(stderr, "w6c: cannot open %s\n", out);
|
||||
fprintf(stderr, "w6c: cannot stage %s\n", out);
|
||||
if (wf != NULL) fclose(wf);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -458,13 +659,46 @@ main(int argc, char **argv)
|
||||
* properties. Legacy raw invocations without -I remain entry-like; every
|
||||
* driver package now supplies -I, and only link roots add --entry. */
|
||||
cg.sep_isdep = (wwiout != NULL && !entrymode);
|
||||
cg.init_dispatch_symbol = initdispatch;
|
||||
cg_file(&cg, of, file);
|
||||
|
||||
if (of != stdout && (fflush(of) != 0 || ferror(of))) {
|
||||
fclose(of);
|
||||
if (wf != NULL) fclose(wf);
|
||||
return 1;
|
||||
}
|
||||
struct publication pub[2] = {0};
|
||||
int npub = 0;
|
||||
if (wwiout != NULL) {
|
||||
pub[npub].dst = wwiout;
|
||||
if (materialize_stream(wf, &pub[npub]) < 0) goto publish_fail;
|
||||
npub++;
|
||||
}
|
||||
if (out != NULL) {
|
||||
pub[npub].dst = out;
|
||||
if (materialize_stream(of, &pub[npub]) < 0) goto publish_fail;
|
||||
npub++;
|
||||
}
|
||||
if (wf != NULL) fclose(wf);
|
||||
if (of != stdout) fclose(of);
|
||||
if (publish_all(pub, npub) < 0) goto publish_free_fail;
|
||||
for (int i = 0; i < npub; i++) publication_free(&pub[i]);
|
||||
freearena(a);
|
||||
for (int i = 0; i < nimports; i++) free(imports[i].buf);
|
||||
free(imports);
|
||||
free(maps);
|
||||
free(buf);
|
||||
return 0;
|
||||
|
||||
publish_fail:
|
||||
if (wf != NULL) fclose(wf);
|
||||
if (of != stdout) fclose(of);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (pub[i].stage != NULL) (void)unlink(pub[i].stage);
|
||||
publication_free(&pub[i]);
|
||||
}
|
||||
return 1;
|
||||
publish_free_fail:
|
||||
for (int i = 0; i < npub; i++) publication_free(&pub[i]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ static const char *
|
||||
wwi_use_path(Checker *c, const char *owner, int source, const char *alias)
|
||||
{
|
||||
for (Node *u = c->file->list; u; u = u->next) {
|
||||
if (u->kind != N_USE || u->str == NULL
|
||||
if (u->kind != N_USE || u->useblank || u->str == NULL
|
||||
|| u->sourceid != source || strcmp(u->str, alias) != 0)
|
||||
continue;
|
||||
int same = owner == NULL ? u->imported == 0
|
||||
@@ -592,6 +592,7 @@ factcmp(const void *a, const void *b)
|
||||
static int
|
||||
wwi_is_decl(Node *d)
|
||||
{
|
||||
if (d->initfn || d->initsynthetic) return 0;
|
||||
return d->kind == N_FNDECL || d->kind == N_TYPEDECL
|
||||
|| d->kind == N_DEF || d->kind == N_LET;
|
||||
}
|
||||
@@ -791,7 +792,7 @@ wwi_collect_decl(struct factset *fs, const char *owner, Node *d)
|
||||
static int
|
||||
wwi_use_owned(Node *u, const char *owner, int source)
|
||||
{
|
||||
return u->kind == N_USE && u->imported != 0
|
||||
return u->kind == N_USE && !u->useblank && u->imported != 0
|
||||
&& u->sourceid == source && wwi_mod_eq(u->module, owner);
|
||||
}
|
||||
|
||||
@@ -803,7 +804,7 @@ wwi_emit_imports(FILE *of, Node *file, const char *owner, int source,
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
int owned = imported ? wwi_use_owned(u, owner, source)
|
||||
: u->kind == N_USE && u->imported == 0
|
||||
&& u->sourceid == source;
|
||||
&& !u->useblank && u->sourceid == source;
|
||||
if (owned) nuse++;
|
||||
}
|
||||
if (nuse == 0) return;
|
||||
@@ -813,7 +814,7 @@ wwi_emit_imports(FILE *of, Node *file, const char *owner, int source,
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
int owned = imported ? wwi_use_owned(u, owner, source)
|
||||
: u->kind == N_USE && u->imported == 0
|
||||
&& u->sourceid == source;
|
||||
&& !u->useblank && u->sourceid == source;
|
||||
if (!owned) continue;
|
||||
us[k].path = u->usepath ? u->usepath : u->str;
|
||||
us[k].idx = k;
|
||||
@@ -836,7 +837,7 @@ wwi_primary_section_has(Checker *c, Node *file, struct factset *fs,
|
||||
struct declent *exports, int nexports, int source)
|
||||
{
|
||||
for (Node *u = file->list; u; u = u->next)
|
||||
if (u->kind == N_USE && u->imported == 0
|
||||
if (u->kind == N_USE && !u->useblank && u->imported == 0
|
||||
&& u->sourceid == source)
|
||||
return 1;
|
||||
for (int i = 0; i < fs->nprivate; i++)
|
||||
|
||||
744
cmd/wcc/check.c
744
cmd/wcc/check.c
@@ -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
|
||||
|
||||
@@ -1326,9 +1326,7 @@ parseuse(Parser *p)
|
||||
const char *alias = NULL;
|
||||
const char *first;
|
||||
if (p->cur.kind == TK_UNDER) {
|
||||
errorf(p->cur.pos, "blank import alias _ is not implemented");
|
||||
p->errs++;
|
||||
alias = "_";
|
||||
n->useblank = 1;
|
||||
advance(p);
|
||||
first = expectident(p);
|
||||
} else {
|
||||
@@ -1344,8 +1342,10 @@ parseuse(Parser *p)
|
||||
leaf = expectident(p);
|
||||
path = aprintf(p->a, "%s.%s", path, leaf);
|
||||
}
|
||||
n->str = alias ? alias : leaf;
|
||||
n->strlen = strlen(n->str);
|
||||
if (!n->useblank) {
|
||||
n->str = alias ? alias : leaf;
|
||||
n->strlen = strlen(n->str);
|
||||
}
|
||||
n->usesource = path;
|
||||
n->usepath = path;
|
||||
n->usealias = alias;
|
||||
|
||||
@@ -349,12 +349,20 @@ struct Node {
|
||||
const char *usealias; /* N_USE: explicit file-local alias, or NULL. */
|
||||
const char *usepkgname; /* N_USE: imported declared package name,
|
||||
* independent of the visible binding in `str`. */
|
||||
int useblank; /* N_USE: `_` spelling; no source binding. */
|
||||
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 initfn; /* special source `fn init`, absent from scope/API. */
|
||||
int initsynthetic; /* compiler-owned variable helper/package task. */
|
||||
int runtimeinit; /* module let lowered through an init helper. */
|
||||
u64 initorder; /* 1-based variable or init-function order. */
|
||||
const char *linksym; /* raw compiler-private assembler symbol. */
|
||||
Node *refdecl; /* checker-resolved value declaration. */
|
||||
u64 initmark; /* checker-private initializer walk mark. */
|
||||
int imported; /* M1 #22: decl reached through an
|
||||
* `//ww:module <path>` import boundary
|
||||
* (vs root/primary). Gates the root-only
|
||||
@@ -616,6 +624,7 @@ struct Checker {
|
||||
* any OTHER empty alloc has no element-type hint
|
||||
* and must fail to infer (harec check.c:1801).
|
||||
* Set by clet around its cexpr, NULL elsewhere. */
|
||||
const char *package_init_symbol; /* canonical action-owned hidden task. */
|
||||
};
|
||||
|
||||
void check_init(Checker*, Arena*);
|
||||
|
||||
1273
cmd/ww/main.c
1273
cmd/ww/main.c
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user