From b52f30a894b3e2ccacfc305d13ab110c2937b332 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 8 Aug 2026 19:15:10 +0900 Subject: [PATCH] wcc: reject module-scope alloc/call let initializers at check time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A module-scope let whose rhs runs code (alloc, call — peeled through cast/?/! wrappers) emitted no DATAW slot: emit_lets' fold-fail silently skipped the definition and every reference died at LINK time with 'undefined reference', the one unacceptable failure mode (rule 7). Hare's model rejects at check time (ref/harec/src/ check.c:4360 'Unable to evaluate initializer at compile time') and routes runtime init through @init, which ww does not have — so both frontends now reject at the declaration with identical wording. alias_infptr_global flips compile->error as the alloc pin (its letvartnode N_TPTR-over-N_TSTRUCT coverage lives on in the local alias_infptr_{nest,slicecap} siblings); callinit_global_reject pins the call shape. Corpus pin 1741/345/21/209/1166/3482. --- cmd/wcc/check.c | 20 +++++++++++++++ internal/wwfixture/types.ww | 10 ++++---- selfhost/cmd/wcc/check.ww | 27 +++++++++++++++++++- test/wcc/data/alias_infptr_global/case.ww | 10 +++++--- test/wcc/data/callinit_global_reject/case.ww | 7 +++++ 5 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 test/wcc/data/callinit_global_reject/case.ww diff --git a/cmd/wcc/check.c b/cmd/wcc/check.c index 60506564..0a97c9c0 100644 --- a/cmd/wcc/check.c +++ b/cmd/wcc/check.c @@ -3126,6 +3126,26 @@ 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 && d->str[0]) { Sym *prev = scope_lookup_local(c->cur, d->str); const char *mod = decl_mod(file, d); diff --git a/internal/wwfixture/types.ww b/internal/wwfixture/types.ww index 63a5b15d..837b0694 100644 --- a/internal/wwfixture/types.ww +++ b/internal/wwfixture/types.ww @@ -1,13 +1,13 @@ package wwfixture; def protocolversion: i32 = 1; -def corpuscount: i32 = 1740; -def errorcount: i32 = 343; -def compilecount: i32 = 22; +def corpuscount: i32 = 1741; +def errorcount: i32 = 345; +def compilecount: i32 = 21; def runcount: i32 = 209; def runexitcount: i32 = 1166; -def nativecount: i32 = 3480; -def corpushash: str = "a2f2514f07ca4e20457331e30f051394da3e59fcfa8f5f6ec3b33d9c14490354"; +def nativecount: i32 = 3482; +def corpushash: str = "bad5b37d555f3f4d742f7a6fcfa857ff46315757233819a19eb9bc1da2800ce7"; type directive = enum i32 { ERROR = 0, diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index fdf17201..ff19db04 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -343,7 +343,32 @@ fn installdecl(c: *checker, file: *syntax.node, d: *syntax.node) void = { if (k == syntax.nkind.N_DEF) { installtop(c, d, nm, mod, syntax.skind.SK_DEF, "def"); return; }; if (k == syntax.nkind.N_TYPEDECL) { installtop(c, d, nm, mod, syntax.skind.SK_TYPE, "type"); return; }; if (k == syntax.nkind.N_FNDECL) { installtop(c, d, nm, mod, syntax.skind.SK_FN, "fn"); return; }; - if (k == syntax.nkind.N_LET) { installtop(c, d, nm, mod, syntax.skind.SK_VAR, "let"); return; }; + if (k == syntax.nkind.N_LET) { + // A module-scope initializer must be link-time data: an + // alloc/call rhs runs code, emitletdataw's 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); ww has no @init + // path. Mirrors cstage check_file N_LET. + let rr: *syntax.node = d.rhs; + for (rr != nil) { + if (rr.kind == syntax.nkind.N_CAST) { rr = rr.lhs; continue; }; + if (rr.kind == syntax.nkind.N_TRYPROP) { rr = rr.lhs; continue; }; + if (rr.kind == syntax.nkind.N_TRYUNW) { rr = rr.lhs; continue; }; + break; + }; + if (rr != nil) { + if (rr.kind == syntax.nkind.N_ALLOC || rr.kind == syntax.nkind.N_CALL) { + cerr(d.file); cerr(": error: module-scope let "); + cerr(nm); + cerr(": runtime initializer unsupported (alloc/call; rule 7)\n"); + c.errs += 1; + }; + }; + installtop(c, d, nm, mod, syntax.skind.SK_VAR, "let"); + return; + }; }; // installtop — install a top-level decl name into c.top, rejecting a diff --git a/test/wcc/data/alias_infptr_global/case.ww b/test/wcc/data/alias_infptr_global/case.ww index 58d936a1..d93d3716 100644 --- a/test/wcc/data/alias_infptr_global/case.ww +++ b/test/wcc/data/alias_infptr_global/case.ww @@ -1,6 +1,10 @@ -//ww:compile -// INFERRED-global *struct root: the letvartnode gate must accept the -// checker-synthesized N_TPTR-over-N_TSTRUCT exactly as the local arm. +//ww:error "runtime initializer unsupported (alloc/call; rule 7)" +// Module-scope alloc initializer: no DATAW slot exists for a +// runtime-computed global, so pre-reject every reference died at +// LINK time ("undefined reference to 'main.gp'") — the checker now +// rejects at the declaration on both frontends (Hare model: +// ref/harec/src/check.c:4360 rejects non-compile-time-evaluable +// global initializers; ww has no @init path). package main; type box = struct { s: str, n: int }; let gp = alloc(box { s = "hi", n = 1 })!; diff --git a/test/wcc/data/callinit_global_reject/case.ww b/test/wcc/data/callinit_global_reject/case.ww new file mode 100644 index 00000000..99b27133 --- /dev/null +++ b/test/wcc/data/callinit_global_reject/case.ww @@ -0,0 +1,7 @@ +//ww:error "runtime initializer unsupported (alloc/call; rule 7)" +// The call sibling of alias_infptr_global's alloc reject: a +// module-scope call initializer has no link-time data either. +package main; +fn mk() i64 = { return 7; }; +let gc: i64 = mk(); +export fn main() i32 = { return gc: i32; };