wcc/check: #133 const-expr scalar module-global — fold+stamp let-init like def, emit DATA (both stages)
A module-global let with a const-expr init (let s = 7*6) emitted NO DATA word: cstage LINK-FAILed (undefined main.s, loud), wwstage was SILENT (no DATA, MOVSXD on stale AX, exit 152). The DEF pass-2 arm already const-folds + stamps its rhs to N_INTLIT (the #88 eval_def_const/stamp_intlit machinery); the LET pass-2 arm omitted it. Mirror it: after the assignability check, fold the rhs and stamp N_INTLIT when the plain-literal fold missed AND the const-fold succeeded. The existing DATA-emit downstream then fires (DATAW 42 + load). Both stages, byte-identical. Closes the inferred const-expr global and the typed b-ii case (let s:i64=7*6, link-fail both stages) with one stamp. Gated on genuine int-const success (the eval return value, not the out-param): str/struct/slice/call/runtime-operand rhs short-circuit before the stamp and are left untouched — never zeroed. Non-const rhs stays on its current loud route; div-by-zero stays loud. Latent in selfhost (no const-expr module globals → 990-997 byte-id unchanged). Pin: 947 rows C1 inferred 7*6, C2 typed b-ii, C3 def-ref K*7, C4 unary-over-binop, C5 div-by-zero loud-guard; cs==ww byte-id.
This commit is contained in:
@@ -39,7 +39,7 @@ runwait(const char *cmd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want_exit; };
|
||||
struct row { const char *label; const char *src; int want_exit; int want_err; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* R1 — the #66(b-i) repro: inferred int-literal module-global, read
|
||||
@@ -50,7 +50,7 @@ static const struct row rows[] = {
|
||||
"let s = 42;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 42 },
|
||||
"};\n", 42, 0 },
|
||||
/* R2 — typed-annotation control: `let s: i64 = 42;` already worked
|
||||
* (cs==ww, DATA emitted). Regression guard for the annotated path. */
|
||||
{ "typed_ctrl",
|
||||
@@ -58,7 +58,7 @@ static const struct row rows[] = {
|
||||
"let s: i64 = 42;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 42 },
|
||||
"};\n", 42, 0 },
|
||||
/* R3 — #134 neg leg: inferred unary-over-int-literal module-global
|
||||
* `let s = -42;` (rhs N_UN(TK_MINUS) over N_INTLIT). Pre-fix wwstage
|
||||
* emitted neither DATAW nor MOVQ (un-defaulted untyped_int annotation)
|
||||
@@ -70,7 +70,7 @@ static const struct row rows[] = {
|
||||
"let s = -42;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 214 },
|
||||
"};\n", 214, 0 },
|
||||
/* R4 — #134 neg typed control: `let s: int = -42;` (TYPED). Already
|
||||
* cs==ww today (concrete annotation → letscalarprim fires). Locks the
|
||||
* construction proof: R3's emitted .s is byte-identical to this typed
|
||||
@@ -80,8 +80,58 @@ static const struct row rows[] = {
|
||||
"let s: int = -42;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 214 },
|
||||
{ NULL, NULL, 0 }
|
||||
"};\n", 214, 0 },
|
||||
/* #133 — const-EXPR scalar module-global. The let pass-2 arm now
|
||||
* const-folds an N_BIN / unary-over-N_BIN / def-ref rhs and stamps it
|
||||
* to an N_INTLIT (mirroring the N_DEF arm), so cgen's literal-only
|
||||
* DATA emitter lays the row in BOTH stages. Pre-fix: cstage LINK-FAILed
|
||||
* (`undefined main.s` — read emitted, no DATA word) and wwstage was
|
||||
* SILENT-WRONG (no DATA, no load, MOVSXD on stale AX → exit 152). */
|
||||
|
||||
/* C1 — inferred const-expr `let s = 7*6;` → 42 (N_BIN rhs). */
|
||||
{ "const_inferred",
|
||||
"package main;\n"
|
||||
"let s = 7 * 6;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 42, 0 },
|
||||
/* C2 — typed const-expr (b-ii) `let s: i64 = 7*6;` → 42. Pre-fix this
|
||||
* LINK-FAILed BOTH stages (annotation-independent no-DATA gap); the
|
||||
* same stamp supplies the foldable value. */
|
||||
{ "const_typed",
|
||||
"package main;\n"
|
||||
"let s: i64 = 7 * 6;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 42, 0 },
|
||||
/* C3 — def-ref through let `def K: int = 6; let s = K*7;` → 42. Proves
|
||||
* eval_def_const's N_IDENT def-resolution reaches through the new let
|
||||
* hook. */
|
||||
{ "const_defref",
|
||||
"package main;\n"
|
||||
"def K: int = 6;\n"
|
||||
"let s = K * 7;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 42, 0 },
|
||||
/* C4 — unary-over-binop `let s = -(2*3);` → -6 (low byte 250). Proves
|
||||
* the N_UN ∘ N_BIN composition folds through eval_def_const. */
|
||||
{ "const_unary_binop",
|
||||
"package main;\n"
|
||||
"let s = -(2 * 3);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 250, 0 },
|
||||
/* C5 — loud guard: a div-by-zero const-expr module-global init stays a
|
||||
* compile ERROR in BOTH stages, NOT silently zeroed. Asserts the
|
||||
* eval_def_const error path is preserved through the let hook. */
|
||||
{ "const_divzero",
|
||||
"package main;\n"
|
||||
"let s = 7 / 0;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 0, 1 },
|
||||
{ NULL, NULL, 0, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
@@ -132,6 +182,36 @@ main(void)
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
/* Loud-guard rows (#133 R5): both stages must REJECT the
|
||||
* source at compile (e.g. a div-by-zero const-expr init). No
|
||||
* value to run / no byte-id — only the error-path preservation
|
||||
* matters. Assert raw w6c AND w6c_ww both emit non-zero. */
|
||||
if (rows[i].want_err) {
|
||||
char cs_s[64], ws_s[64], cmd[2048];
|
||||
snprintf(cs_s, sizeof cs_s, "/tmp/wwisg_%d_%d_cs.s",
|
||||
getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "/tmp/wwisg_%d_%d_ww.s",
|
||||
getpid(), i);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "row[%s]: w6c accepted a source "
|
||||
"that must be rejected (loud guard)\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww accepted a "
|
||||
"source that must be rejected (loud "
|
||||
"guard)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
unlink(src); unlink(cs_s); unlink(ws_s);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* (a) cstage build + run. */
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwisg_%d_d_%d",
|
||||
|
||||
Reference in New Issue
Block a user