From 094ccc63f018fc5f6f3e0fc825eeb3f13f5c851b Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 22 Jun 2026 17:10:22 +0900 Subject: [PATCH] test: migrate 944_alias_def_addr to @test + runww reject, retire C twin (fold-3) #88 alias/def/address-of family: 5 value rows -> test/lang/alias_def_addr_test.ww (primitive-only asserts), 1 reject (str-def-non-addressable, shared body 'cannot take address of non-addressable def') -> runww //ww:error dual-stage carrier. byteid floor 53->54. --- Makefile | 9 +- test/lang/alias_def_addr_test.ww | 70 ++++ test/wcc/944_alias_def_addr_run.c | 321 ------------------- test/wcc/data/alias_def_addr_nonaddr/case.ww | 15 + 4 files changed, 86 insertions(+), 329 deletions(-) create mode 100644 test/lang/alias_def_addr_test.ww delete mode 100644 test/wcc/944_alias_def_addr_run.c create mode 100644 test/wcc/data/alias_def_addr_nonaddr/case.ww diff --git a/Makefile b/Makefile index 47b2bc30..4a28fd07 100644 --- a/Makefile +++ b/Makefile @@ -370,7 +370,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_array_zeroinit_run \ $(BIN)/test_alias_accept_run \ $(BIN)/test_alias_idx_family_run \ - $(BIN)/test_alias_def_addr_run \ $(BIN)/test_idx_tagged_field_run \ $(BIN)/test_tagged_widen_arg_run \ $(BIN)/test_tuple_tagged_union_run \ @@ -2231,12 +2230,6 @@ $(BIN)/test_alias_idx_family_run: test/wcc/944_alias_idx_family_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_alias_def_addr_run: test/wcc/944_alias_def_addr_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_idx_tagged_field_run: test/wcc/944_idx_tagged_field_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -3287,7 +3280,7 @@ test-lang: all LANGBYTEID_DIR = $(OUT)/langbyteid LANGBYTEID_FILES = $(wildcard test/lang/*_test.ww) LANGBYTEID_VERB = test -c -LANGBYTEID_EXPECTED_MIN = 53 +LANGBYTEID_EXPECTED_MIN = 54 $(if $(LANGBYTEID_FILES),,$(error test-lang-byteid: empty corpus)) test-lang-byteid: all @set -e; \ diff --git a/test/lang/alias_def_addr_test.ww b/test/lang/alias_def_addr_test.ww new file mode 100644 index 00000000..c3c1b7c1 --- /dev/null +++ b/test/lang/alias_def_addr_test.ww @@ -0,0 +1,70 @@ +// alias_def_addr_test — address-of a def whose declared type is an ALIAS of +// an array/struct (#5 alias arc F2a, task #88), migrated from +// test/wcc/944_alias_def_addr_run.c. defisaddressable (cgen.ww) keyed the +// array leg on the UNCHASED syntactic dtnode (N_TARRAY), so `&D` where D is a +// def typed as an ALIAS of an array missed the gate and fell to the rule-7 +// loud "non-addressable def" tail — even though the def-array DATA emitter +// already peels TY_NAMED transitively, so the alias def HAS a DATA symbol. +// The gate-only fix (tichase(dtn.type_) == TY_ARRAY) restores gate == +// emission set; cstage (def_isarraydef over the chased let_isarray) is the +// runtime-correct reference. Pre-fix: def_l1/def_l2/def_order ww LOUD, cs 0. +// These rows are the #88 ASM-FRAGILE surface (&def / alias-of-addr is exactly +// where cs!=ww surfaces) — T2 keeps cs==ww. The non-addressable str def +// (`&S`, no DATA symbol) STAYS a loud rule-7 reject and is the runww carrier +// (test/wcc/data/alias_def_addr_nonaddr). PRIMITIVE-only asserts (readback +// through the &-derived pointer is the pin — a wrong address or wrong DATA +// emission yields a wrong readback; no fmt/strconv in any assert path). +// def_l2's readback casts to the BASE array ptr before the deref-index: the +// natural `(*p)[2]` over a 2-LEVEL-alias pointee trips a SEPARATE pre-existing +// cstage double-deref (task #93), deliberately not exercised here. + +package alias_def_addr_test; + +// def_ctrl — plain [3]int def, no alias; the always-held control. +def Dctrl: [3]int = [1000: int, 2000: int, 3000: int]; + +@test fn alias_def_addr_ctrl() void = { + let p: *[3]int = &Dctrl; + assert((*p)[2] == 3000); +}; + +// def_l1 — 1-level alias def array; &D missed the unchased gate pre-fix. +type arr1 = [3]int; +def Dl1: arr1 = [1000: int, 2000: int, 3000: int]; + +@test fn alias_def_addr_l1() void = { + let p: *arr1 = &Dl1; + assert((*p)[2] == 3000); +}; + +// def_l2 — 2-level alias; readback casts to the BASE array ptr (task #93). +type arr2a = [3]int; +type arr2b = arr2a; +def Dl2: arr2b = [1000: int, 2000: int, 3000: int]; + +@test fn alias_def_addr_l2() void = { + let p: *arr2b = &Dl2; + let q: *[3]int = p: *[3]int; + assert((*q)[2] == 3000); +}; + +// def_order — the alias type is declared AFTER the def that uses it. +def Dord: arrord = [1000: int, 2000: int, 3000: int]; + +@test fn alias_def_addr_order() void = { + let p: *arrord = &Dord; + assert((*p)[2] == 3000); +}; + +type arrord = [3]int; + +// def_struct — alias-typed STRUCT def; defvarstructinfo already chased +// aliaslookup transitively, so this leg held before and after (PREMISE-5). +type pt = struct { x: int, y: int }; +type pt2 = pt; +def Dstruct: pt2 = pt2{x = 1000, y = 2000}; + +@test fn alias_def_addr_struct() void = { + let p: *pt2 = &Dstruct; + assert(p.y == 2000); +}; diff --git a/test/wcc/944_alias_def_addr_run.c b/test/wcc/944_alias_def_addr_run.c deleted file mode 100644 index d8c92989..00000000 --- a/test/wcc/944_alias_def_addr_run.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * 944_alias_def_addr_run — #5 alias arc F2a batch 3 c2 (task #88): - * defisaddressable (cgen.ww:3089) keyed the array leg on the UNCHASED - * syntactic dtnode (N_TARRAY) — `&D` where D is a def whose declared - * type is an ALIAS of an array missed the gate and fell to the rule-7 - * loud error "cannot take address of non-addressable def". LOUD class, - * but the gate was lying: the ww def-array DATA emitter already peels - * TY_NAMED transitively (emitdefconstants' array arm), so the alias - * def HAS a DATA symbol (probe-observed: `DATA main.D(SB)` emitted - * byte-id by both stages for the &-less program). Gate-only fix — - * tichase(dtn.type_) == TY_ARRAY — restores gate == emission set; no - * emitter twin. cstage registers def arrays via the g-fold-G1 chased - * let_isarray and gates TK_AMP on def_isarraydef (cgen.c:3975-3977), - * so cs is the runtime-correct reference (observed 0 on every row). - * - * row | shape | want - * ------------+----------------------------------------------+----- - * def_ctrl | plain [3]int def; &D; read via ptr | 0 - * def_l1 | 1-level alias def array, &D | 0 - * def_l2 | 2-level, readback via cast-to-base ptr | 0 - * def_order | type decl AFTER the def | 0 - * def_struct | alias-typed STRUCT def, &D (defvarstructinfo | - * | leg already chased — held throughout) | 0 - * def_nonaddr | str def, &D -> rule-7 loud error | LOUD - * - * Pre-fix: def_l1/def_l2/def_order ww LOUD / cs 0; controls 0/0. - * Post-fix: every value row 0/0 byte-id; def_nonaddr STAYS LOUD both - * stages with a BYTE-IDENTICAL diagnostic (the error-path pin — - * genuinely symbol-less defs must keep firing the rule-7 tail, and - * the tail's text is itself a convergence surface). - * - * def_l2's readback casts to the BASE array ptr (`p: *[3]int`) before - * the deref-index: the natural `(*p)[2]` spelling over a 2-LEVEL-alias - * pointee trips a SEPARATE pre-existing CSTAGE double-deref (spurious - * MOVQ (AX),AX, SEGV; def-INDEPENDENT — a local twin reproduces it; - * ww runs it correctly) — filed as task #93 (#85 type_unwrap kin, F2b - * OUT territory), deliberately not pinned here. - */ -#include -#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; -} - -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), cb = fgetc(fb); - if (ca != cb) { rc = -1; break; } - if (ca == EOF) break; - } - fclose(fa); fclose(fb); - return rc; -} - -/* mustfail: build must FAIL on BOTH drivers (the error-path pin); - * such rows trade the asm byte-id check for a DIAGNOSTIC byte-id - * check (w6c vs w6c_ww stderr compared byte-for-byte). */ -struct row { const char *label; const char *src; int want; int mustfail; }; - -static const struct row rows[] = { - { "def_ctrl", - "package main;\n" - "def D: [3]int = [1000: int, 2000: int, 3000: int];\n" - "export fn main() i32 = {\n" - " let p: *[3]int = &D;\n" - " if ((*p)[2] != 3000) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0 }, - { "def_l1", - "package main;\n" - "type arr = [3]int;\n" - "def D: arr = [1000: int, 2000: int, 3000: int];\n" - "export fn main() i32 = {\n" - " let p: *arr = &D;\n" - " if ((*p)[2] != 3000) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* cast-to-base readback — see the header note on task #93. */ - { "def_l2", - "package main;\n" - "type arr = [3]int;\n" - "type arr2 = arr;\n" - "def D: arr2 = [1000: int, 2000: int, 3000: int];\n" - "export fn main() i32 = {\n" - " let p: *arr2 = &D;\n" - " let q: *[3]int = p: *[3]int;\n" - " if ((*q)[2] != 3000) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0 }, - { "def_order", - "package main;\n" - "def D: arr = [1000: int, 2000: int, 3000: int];\n" - "export fn main() i32 = {\n" - " let p: *arr = &D;\n" - " if ((*p)[2] != 3000) { return 1; };\n" - " return 0;\n" - "};\n" - "type arr = [3]int;\n", 0, 0 }, - /* PREMISE-5 leg: defvarstructinfo already walks aliaslookup - * transitively — held before and after. */ - { "def_struct", - "package main;\n" - "type pt = struct { x: int, y: int };\n" - "type pt2 = pt;\n" - "def D: pt2 = pt2{x=1000, y=2000};\n" - "export fn main() i32 = {\n" - " let p: *pt2 = &D;\n" - " if (p.y != 2000) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* str def has no DATA symbol — the rule-7 loud tail must keep - * firing (error-path pin). */ - { "def_nonaddr", - "package main;\n" - "def S: str = \"hello\";\n" - "export fn main() i32 = {\n" - " let p = &S;\n" - " return 0;\n" - "};\n", 0, 1 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[96], tmpdir[96], errf[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/ada_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/ada_%d_d_%d", getpid(), i); - snprintf(errf, sizeof errf, "/tmp/ada_%d_e_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, - "cd %s && timeout 20 %s build %s >/dev/null 2>%s", - tmpdir, driver, src, errf); - int brc = runwait(cmd); - if (r->mustfail) { - unlink(src); unlink(errf); rmdir(tmpdir); - if (brc == 0) { - fprintf(stderr, "row[%s]: %s built but must fail " - "loud\n", r->label, driver); - return 1; - } - return 0; - } - if (brc != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - unlink(src); unlink(errf); rmdir(tmpdir); - return -1; - } - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[256]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - int got = runwait(outbin); - - unlink(src); unlink(outbin); unlink(errf); rmdir(tmpdir); - if (got != r->want) { - fprintf(stderr, "row[%s]: %s exit %d, want %d\n", - r->label, driver, got, r->want); - return 1; - } - return 0; -} - -static int -asm_byte_identical(const char *bin, const struct row *r, int i) -{ - char src[96], cs[96], ws[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/ada_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/ada_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/ada_asm_%d_%d_w.s", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: w6c errored\n", r->label); - unlink(src); - return -1; - } - snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", - bin, ws, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); - unlink(src); unlink(cs); - return -1; - } - int rc = slurp_eq(cs, ws); - if (rc != 0) - fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", - r->label); - unlink(src); unlink(cs); unlink(ws); - return rc; -} - -/* Error-path twin of asm_byte_identical: both compilers must REJECT - * the row and emit the same diagnostic bytes (an empty stderr is a - * fail — a silent reject is not the rule-7 loud tail). */ -static int -diag_byte_identical(const char *bin, const struct row *r, int i) -{ - char src[96], cs[96], ws[96], ce[96], we[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/ada_diag_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/ada_diag_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/ada_diag_%d_%d_w.s", getpid(), i); - snprintf(ce, sizeof ce, "/tmp/ada_diag_%d_%d_c.e", getpid(), i); - snprintf(we, sizeof we, "/tmp/ada_diag_%d_%d_w.e", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - int rc = 0; - snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>%s", bin, cs, src, ce); - if (runwait(cmd) == 0) { - fprintf(stderr, "row[%s]: w6c accepted but must fail loud\n", - r->label); - rc = -1; - } - snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>%s", - bin, ws, src, we); - if (rc == 0 && runwait(cmd) == 0) { - fprintf(stderr, "row[%s]: w6c_ww accepted but must fail " - "loud\n", r->label); - rc = -1; - } - if (rc == 0) { - FILE *fe = fopen(ce, "rb"); - if (!fe || fgetc(fe) == EOF) { - fprintf(stderr, "row[%s]: empty diagnostic\n", - r->label); - rc = -1; - } - if (fe) fclose(fe); - } - if (rc == 0) { - rc = slurp_eq(ce, we); - if (rc != 0) - fprintf(stderr, "row[%s]: cstage vs wwstage " - "diagnostic differs\n", r->label); - } - unlink(src); unlink(cs); unlink(ws); unlink(ce); unlink(we); - return rc; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[2080]; - 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 cdrv[2120], wdrv[2120]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - total++; - if (run_driver(cdrv, &rows[i], i) != 0) fail++; - } - if (access(wdrv, X_OK) == 0) { - for (int i = 0; i < n; i++) { - total++; - if (run_driver(wdrv, &rows[i], i) != 0) fail++; - } - for (int i = 0; i < n; i++) { - total++; - if (rows[i].mustfail) { - if (diag_byte_identical(bin, &rows[i], i) - != 0) fail++; - continue; - } - if (asm_byte_identical(bin, &rows[i], i) != 0) fail++; - } - } - - if (fail) { - fprintf(stderr, "alias_def_addr: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("alias_def_addr: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/data/alias_def_addr_nonaddr/case.ww b/test/wcc/data/alias_def_addr_nonaddr/case.ww new file mode 100644 index 00000000..01ffc639 --- /dev/null +++ b/test/wcc/data/alias_def_addr_nonaddr/case.ww @@ -0,0 +1,15 @@ +//ww:error "cannot take address of non-addressable def" +// Lifted from test/wcc/944_alias_def_addr_run.c reject row "def_nonaddr" +// (#88/#149/#147/rule-7): a `def S: str` is inlined and has NO DATA symbol, +// so `&S` is not addressable and MUST loud-fail in both stages, not emit a +// wild deref. The substring is the shared diagnostic body — byte-identical on +// both stages (no file:line, no "ww: " prefix — the prefix differs cs vs ww +// only by being emitted, the body is shared verbatim). This is the error-path +// pin for the #88 alias-def address-of family: the gate fix must keep firing +// the rule-7 tail for genuinely symbol-less defs. +package main; +def S: str = "hello"; +export fn main() i32 = { + let p = &S; + return 0; +};