From 9cef92175f38179217fcb65aaf66e8a196134633 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 22 Jun 2026 18:28:41 +0900 Subject: [PATCH] test: migrate 989_strglobeq to @test value pin, retire C twin (fold-3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #154 cstage-only str== global-header miscompile (str== fast-path read the global str header off BP+0 instead of name(SB); byte-id 990-997 blind to a runtime-value miscompile). Lifted the run fixture into test/lang/strglobeq_test.ww (primitive @test value pin; byteid-eligible — post-fix cs==ww). byteid floor 55->56. --- Makefile | 7 +--- test/lang/strglobeq_test.ww | 73 ++++++++++++++++++++++++++++++++++++ test/wcc/989_strglobeq.ww | 67 --------------------------------- test/wcc/989_strglobeq_run.c | 51 ------------------------- 4 files changed, 74 insertions(+), 124 deletions(-) create mode 100644 test/lang/strglobeq_test.ww delete mode 100644 test/wcc/989_strglobeq.ww delete mode 100644 test/wcc/989_strglobeq_run.c diff --git a/Makefile b/Makefile index 86b9e118..9e4db92b 100644 --- a/Makefile +++ b/Makefile @@ -511,7 +511,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_hex_run $(BIN)/test_utf8_run $(BIN)/test_bytes_run \ $(BIN)/test_path_run \ $(BIN)/test_letshadow_run \ - $(BIN)/test_strglobeq_run \ $(BIN)/test_test_filter \ $(BIN)/test_strconv_int_run \ $(BIN)/test_stof_run $(BIN)/test_ftos_run \ @@ -2732,10 +2731,6 @@ $(BIN)/test_letshadow_run: test/wcc/989_letshadow_run.c $(BIN)/ww $(BIN)/w6c \ $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_strglobeq_run: test/wcc/989_strglobeq_run.c $(BIN)/ww $(BIN)/w6c \ - $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_ascii_run: test/wcc/904_ascii_run.c $(BIN)/ww $(BIN)/w6c \ $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< @@ -3274,7 +3269,7 @@ test-lang: all LANGBYTEID_DIR = $(OUT)/langbyteid LANGBYTEID_FILES = $(filter-out %_runonly_test.ww,$(wildcard test/lang/*_test.ww)) LANGBYTEID_VERB = test -c -LANGBYTEID_EXPECTED_MIN = 55 +LANGBYTEID_EXPECTED_MIN = 56 $(if $(LANGBYTEID_FILES),,$(error test-lang-byteid: empty corpus)) test-lang-byteid: all @set -e; \ diff --git a/test/lang/strglobeq_test.ww b/test/lang/strglobeq_test.ww new file mode 100644 index 00000000..88892826 --- /dev/null +++ b/test/lang/strglobeq_test.ww @@ -0,0 +1,73 @@ +// strglobeq_test — #154 regression pin, migrated from test/wcc/989_strglobeq_run.c. +// +// `str == str` / `str != str` delegate to rt_streq, and cgen's str-compare +// arm had an N_IDENT fast-path that ALWAYS read the header off BP+localfind(name). +// For a module-GLOBAL str ident localfind→0, so it loaded saved-BP/retaddr +// garbage instead of name(SB) — a silent CSTAGE miscompile (the header lives +// at name(SB), not the frame). Fix mirrors #148's slice global branch: LEAQ +// name(SB), load ptr/len off it. Now cs==ww, so this file is byteid-eligible. +// +// The bug is per-SHAPE in cgen, so each comparison shape is a separate fn the +// @tests drive over three inputs: "/" (eqsep), "foo" (neither), "/usr" +// (eqlong). Shapes cover const-global AND let-global operands, ident on RHS +// AND on LHS, == AND !=, and a length>1 global ("/usr") so the LEN word — not +// just the ptr — is read off name(SB). PRIMITIVE-only (bool) asserts — no +// fmt/strconv; a wrong base/len yields a wrong boolean. + +package strglobeq_test; + +const csep: str = "/"; +let lsep: str = "/"; +const longsep: str = "/usr"; + +// rhs-ident global (p == g): const, let, len>1, and !=. +fn eqr_const(p: str) bool = { return p == csep; }; +fn eqr_let(p: str) bool = { return p == lsep; }; +fn eqr_long(p: str) bool = { return p == longsep; }; +fn ner_const(p: str) bool = { return p != csep; }; + +// lhs-ident global (g == p): const and len>1. +fn eql_const(p: str) bool = { return csep == p; }; +fn eql_long(p: str) bool = { return longsep == p; }; + +// Shape 1 — rhs const-global ==. +@test fn rhs_const_eq() void = { + assert(eqr_const("/") == true); + assert(eqr_const("foo") == false); + assert(eqr_const("/usr") == false); +}; + +// Shape 2 — rhs let-global ==. +@test fn rhs_let_eq() void = { + assert(eqr_let("/") == true); + assert(eqr_let("foo") == false); + assert(eqr_let("/usr") == false); +}; + +// Shape 3 — lhs const-global ==. +@test fn lhs_const_eq() void = { + assert(eql_const("/") == true); + assert(eql_const("foo") == false); + assert(eql_const("/usr") == false); +}; + +// Shape 4 — rhs const-global != (negation arm). +@test fn rhs_const_ne() void = { + assert(ner_const("/") == false); + assert(ner_const("foo") == true); + assert(ner_const("/usr") == true); +}; + +// Shape 5 — rhs len>1 const-global == (LEN word read off name(SB)). +@test fn rhs_long_eq() void = { + assert(eqr_long("/") == false); + assert(eqr_long("foo") == false); + assert(eqr_long("/usr") == true); +}; + +// Shape 6 — lhs len>1 const-global == (LEN word, lhs sub-site). +@test fn lhs_long_eq() void = { + assert(eql_long("/") == false); + assert(eql_long("foo") == false); + assert(eql_long("/usr") == true); +}; diff --git a/test/wcc/989_strglobeq.ww b/test/wcc/989_strglobeq.ww deleted file mode 100644 index b5c0a4ab..00000000 --- a/test/wcc/989_strglobeq.ww +++ /dev/null @@ -1,67 +0,0 @@ -// strglobeq — #154 regression pin. `str == str` / `str != str` delegate to -// rt_streq, and cgen's str-compare arm had an N_IDENT fast-path that ALWAYS -// read the header off BP+localfind(name). For a module-GLOBAL str ident -// localfind→0, so it loaded saved-BP/retaddr garbage instead of name(SB) — -// a silent cstage miscompile (the header lives at name(SB), not the frame). -// Fix mirrors #148's slice global branch: LEAQ name(SB), load ptr/len off it. -// -// Table-driven: each row is {input, expected}; the loop feeds every input -// through the five compile-time-distinct comparison shapes (the bug is per -// SHAPE in cgen, so the shapes are separate fns the rows drive). Shapes cover -// const-global AND let-global operands, ident on RHS AND on LHS, == AND !=, -// and a length>1 global ("/usr") so the LEN word — not just the ptr — is read -// off name(SB) on both sub-sites. signalled = row*10+shape pinpoints failures. -// -// Run with `out/bin/ww run test/wcc/989_strglobeq.ww`; exit 0 = all pass. - -package main; - -import os; - -const csep: str = "/"; -let lsep: str = "/"; -const longsep: str = "/usr"; - -// rhs-ident global (p == g): const, let, len>1, and !=. -fn eqr_const(p: str) bool = { return p == csep; }; -fn eqr_let(p: str) bool = { return p == lsep; }; -fn eqr_long(p: str) bool = { return p == longsep; }; -fn ner_const(p: str) bool = { return p != csep; }; - -// lhs-ident global (g == p): const and len>1. -fn eql_const(p: str) bool = { return csep == p; }; -fn eql_long(p: str) bool = { return longsep == p; }; - -type row = struct { - in: str, - eqsep: bool, // in == "/" - eqlong: bool, // in == "/usr" -}; - -let signalled: i32 = 0; -fn fail() void = { os.exit(signalled + 10); }; - -export fn main() i32 = { - let rows: [_]row = [ - row { in = "/", eqsep = true, eqlong = false }, - row { in = "foo", eqsep = false, eqlong = false }, - row { in = "/usr", eqsep = false, eqlong = true }, - ]; - // len() stamps i32 on cstage (#26); the index must match for the bound. - for (let i: i32 = 0; i < len(rows); i += 1) { - let r = rows[i]; - signalled = i * 10 + 1; - if (eqr_const(r.in) != r.eqsep) { fail(); }; - signalled = i * 10 + 2; - if (eqr_let(r.in) != r.eqsep) { fail(); }; - signalled = i * 10 + 3; - if (eql_const(r.in) != r.eqsep) { fail(); }; - signalled = i * 10 + 4; - if (ner_const(r.in) != !r.eqsep) { fail(); }; - signalled = i * 10 + 5; - if (eqr_long(r.in) != r.eqlong) { fail(); }; - signalled = i * 10 + 6; - if (eql_long(r.in) != r.eqlong) { fail(); }; - }; - return 0; -}; diff --git a/test/wcc/989_strglobeq_run.c b/test/wcc/989_strglobeq_run.c deleted file mode 100644 index 44bc84c4..00000000 --- a/test/wcc/989_strglobeq_run.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 989_strglobeq_run — #154 regression pin. Compile + run the strglobeq - * fixture under the C-side `ww run` driver (cstage w6c) and assert exit 0. - * - * The bug (str== fast-path read the global str header off BP+0 instead of - * name(SB)) is a CSTAGE silent miscompile; byte-id 990-997 can't see a - * runtime-value miscompile, so only this value check catches it. Same - * thin-wrapper shape as 989_letshadow_run. - */ -#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; -} - -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 cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - - const char *src = "test/wcc/989_strglobeq.ww"; - char path[1024], cmd[2048]; - snprintf(path, sizeof path, "%s/%s", cwd, src); - snprintf(cmd, sizeof cmd, "%s/ww run %s", bin, path); - int rc = runwait(cmd); - if (rc != 0) { - fprintf(stderr, "strglobeq_run FAIL: %s exited %d " - "(row %d miscompiled — #154)\n", src, rc, rc - 10); - return 1; - } - printf("strglobeq_run: %s ok\n", src); - return 0; -}