/* * 918_struct_composite_init_run — runtime + byte-id net for #129 * Phase A.2: module-level `let`/`def` with composite-struct * initializer silently emitted undefined ref OR wrong bytes pre-fix. * * Pre-A.2 failure modes: * - cstage emit_lets / wwstage emitletdataw skipped struct-typed lets * entirely (`if (is_struct) continue;` / parallel) — link surfaced * `undefined reference to 'main.NAME'`. * - cstage emit_defs / wwstage emitdefconstants had no struct arm — * same undef ref for def, plus a SEPARATE LOAD-side cgen bug * emitting `MOVSXD (BP), AX` (reading stack frame byte 0) when the * LOAD did get past the link. * * Phase A.2 fix (per A.1 SSoT-helper precedent): * - cstage: emit_struct_data + emit_struct_lit_bytes helpers walk * Tfield list in declaration order, zero-fill padding via per- * field offsets (rule 13), dispatch per field type. Float-field * bytes inlined (mirroring A.1's emit_floatlit_data shape but * localised so the byte loop covers padding too). Nested struct * recurses. Out-of-scope field kinds (array / str / slice / ptr) * fatal loud per rule-7. * - cstage: emit_lets + emit_defs gain struct arms routing through * the helper. * - cstage: LOAD-side widening at `if (u && u->kind == TY_STRUCT * && lhs->kind == N_IDENT)` arm — the `let_islet`-gated LEAQ * name(SB) shape now also fires for struct defs via the new * `def_isstructdef` registry (mirror of letvars). * - wwstage: parallel emitstructdata + emitstructlitbytes helpers, * defent.dtnode field, defvarstructinfo (cgdot LOAD widening). * * Bootstrap NEUTRAL: zero `let/def: T = T{...}` consumers in lib/ or * selfhost/. γ-cleanup is the first consumer (lib/math:floatinfo). * * Rows cover all A.2-scope shapes: * - (a) `let CFG: cfg_t = cfg_t{a=1, b=2u64};` — plain mixed-int let * - (b) `def CFG: cfg_t = cfg_t{a=1, b=2u64};` — plain mixed-int def * (exercises LOAD-widening; both stages) * - (c) `let F: ft = ft{a=1.5, b=99u32};` — let with float field * - (d) `def F: ft = ft{a=1.5, b=99u32};` — def with float field * (storage + LOAD + float-narrow path together) * - (e) `let Z: zt = zt{};` — empty struct-lit zero-fill * - (f) `let CFG: cfg_t;` — regression: no-rhs (pre-existing path * unchanged) * * Each row carries (a) cstage `ww build` + run asserting exit code * and (b) w6c vs w6c_ww `.s` cmp (rule-10 byte-id). * * Nested-struct shape (#3 in design report) is OMITTED here — the * helper implements the recursion but #145 (parser/checker inner- * literal field-name leak) blocks end-to-end correctness; nested * row deferred until #145 lands. * * Array-in-struct shape parked to Phase A.3 boundary. */ #include #include #include #include #include static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; if (WIFEXITED(rc)) return WEXITSTATUS(rc); return -1; } struct row { const char *label; const char *src; int want_exit; }; static const struct row rows[] = { { "let_int_struct", "package main;\n" "type cfg_t = struct { a: i32, b: u64 };\n" "let CFG: cfg_t = cfg_t{a=1, b=2u64};\n" "export fn main() i32 = {\n" " return CFG.a;\n" "};\n", 1 }, { "def_int_struct", "package main;\n" "type cfg_t = struct { a: i32, b: u64 };\n" "def CFG: cfg_t = cfg_t{a=1, b=2u64};\n" "export fn main() i32 = {\n" " return CFG.a;\n" "};\n", 1 }, { "let_float_field", "package main;\n" "type ft = struct { a: f64, b: u32 };\n" "let F: ft = ft{a=1.5, b=99u32};\n" "export fn main() i32 = {\n" " return (F.a: i32);\n" "};\n", 1 }, { "def_float_field", "package main;\n" "type ft = struct { a: f64, b: u32 };\n" "def F: ft = ft{a=1.5, b=99u32};\n" "export fn main() i32 = {\n" " return (F.a: i32);\n" "};\n", 1 }, { "let_empty_struct", "package main;\n" "type zt = struct { a: i32, b: u64 };\n" "let Z: zt = zt{};\n" "export fn main() i32 = {\n" " return Z.a;\n" "};\n", 0 }, /* 8B struct hits the cstage emit_lets scalar-8B short-circuit * (sz==8 fold_int_literal arm) — without the `!let_isstruct` * gate the struct lit fold-fails and the let drops entirely, * emitting no DATA. Both stages now route via the struct arm. */ { "let_int_struct_8b", "package main;\n" "type s8 = struct { a: i32, b: i32 };\n" "let X: s8 = s8{a=7, b=42};\n" "export fn main() i32 = {\n" " return X.a;\n" "};\n", 7 }, /* Regression: no-rhs path unchanged (emit_data_row_zero / wwstage * parallel). */ { "let_norhs_struct", "package main;\n" "type cfg_t = struct { a: i32, b: u64 };\n" "let CFG: cfg_t;\n" "export fn main() i32 = {\n" " return CFG.a;\n" "};\n", 0 }, { NULL, NULL, 0 } }; static int slurp_eq(const char *a, const char *b) { FILE *fa = fopen(a, "rb"); FILE *fb = fopen(b, "rb"); if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; } int rc = 0; for (;;) { int ca = fgetc(fa); int cb = fgetc(fb); if (ca != cb) { rc = -1; break; } if (ca == EOF) break; } fclose(fa); fclose(fb); return rc; } int main(void) { const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; char absbin[1024]; if (bin[0] != '/') { char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return 1; snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; } char w6c[1100], w6c_ww[1100]; snprintf(w6c, sizeof w6c, "%s/w6c", bin); snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); if (access(w6c_ww, X_OK) != 0) { fprintf(stderr, "strcomp: w6c_ww missing — cannot run " "the cs==ww byte-id gate (the whole point of this test)\n"); return 1; } int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { char tmpdir[64]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwstrc_%d_d_%d", getpid(), i); mkdir(tmpdir, 0755); char src[128], outbin[128], cs_s[128], ws_s[128], rmcmd[160]; snprintf(src, sizeof src, "%s/wwstrc_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/wwstrc_%d_%d", tmpdir, getpid(), i); snprintf(cs_s, sizeof cs_s, "%s/wwstrc_%d_%d_cs.s", tmpdir, getpid(), i); snprintf(ws_s, sizeof ws_s, "%s/wwstrc_%d_%d_ww.s", tmpdir, getpid(), i); snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); FILE *f = fopen(src, "wb"); if (f == NULL) { fail++; runwait(rmcmd); continue; } fputs(rows[i].src, f); fclose(f); char cmd[2048]; snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", bin, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: cstage build failed\n", rows[i].label); fail++; runwait(rmcmd); continue; } int got = runwait(outbin); if (got != rows[i].want_exit) { fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", rows[i].label, got, rows[i].want_exit); fail++; } snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label); fail++; runwait(rmcmd); continue; } 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 failed\n", rows[i].label); fail++; runwait(rmcmd); continue; } if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER (rule-10 " "byte-id violation)\n", rows[i].label); fail++; } runwait(rmcmd); } if (fail) { fprintf(stderr, "%d/%d struct-composite tests failed\n", fail, n); return 1; } printf("strcomp: %d/%d ok (cstage run + cs==ww byte-id)\n", n, n); return 0; }