From c421c2b20afa9a2ef15f5a9ca1acbc86ccd4c8e4 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 8 Aug 2026 00:58:04 +0900 Subject: [PATCH] cgen: key the str/slice arg recognizers off the checker stamp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The wwstage nodeisstr/nodeisslice recognizers were name-keyed for every non-local shape: an indirect fn-pointer callee ((*f)() — the errnotest #59.5 divergence, streq receiving a shifted register file) and module-global let/const idents (path.sepstr — union-widen pushes zero-filled len/cap) both fell to false while cstage keys off the checker stamp unconditionally. Both recognizers now fall back to the stamped n.type_; push and pop sites share them, so the drain stays balanced by construction. Graduates the #59.5 errnotest pin. --- internal/wwfixture/types.ww | 8 +++--- selfhost/cmd/wcc/cgenutil.ww | 25 +++++++++++++++++-- test/wcc/989_lib_byteid.c | 8 +++++- test/wcc/data/globalconst_slice_widen/case.ww | 23 +++++++++++++++++ test/wcc/data/globalconst_str_widen/case.ww | 24 ++++++++++++++++++ .../data/r595_indirect_call_str_arg/case.ww | 22 ++++++++++++++++ 6 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 test/wcc/data/globalconst_slice_widen/case.ww create mode 100644 test/wcc/data/globalconst_str_widen/case.ww create mode 100644 test/wcc/data/r595_indirect_call_str_arg/case.ww diff --git a/internal/wwfixture/types.ww b/internal/wwfixture/types.ww index df66604a..ba27a00d 100644 --- a/internal/wwfixture/types.ww +++ b/internal/wwfixture/types.ww @@ -1,13 +1,13 @@ package wwfixture; def protocolversion: i32 = 1; -def corpuscount: i32 = 1233; +def corpuscount: i32 = 1236; def errorcount: i32 = 314; def compilecount: i32 = 12; -def runcount: i32 = 141; +def runcount: i32 = 144; def runexitcount: i32 = 766; -def nativecount: i32 = 2466; -def corpushash: str = "65d3642dfb77b486f5aed23044a979bbce37c36fbbcb7a7aedae20d87c7b0e4f"; +def nativecount: i32 = 2472; +def corpushash: str = "4c8f4a693d2d8193b15f4215610c1d070dc83493b1189af772ca09cad4a69db7"; type directive = enum i32 { ERROR = 0, diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 62cb8f83..f959a766 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -1342,6 +1342,11 @@ fn nodeisslice(c: *cgen, n: *syntax.node) bool = { if (deflookup(c, nm)) { return syntax.typeisslice(n.type_: *syntax.tinfo); }; + // #142-adjacent: module-global let/const slice ident — the + // str twin's rationale verbatim (see nodeisstr N_IDENT). + if (isletvar(c, nm)) { + return syntax.typeisslice(n.type_: *syntax.tinfo); + }; return false; }; if (k == syntax.nkind.N_SLICE) { return true; }; @@ -1376,7 +1381,13 @@ fn nodeisslice(c: *cgen, n: *syntax.node) bool = { return isslicetype(c, rtyp); }; }; - return false; + // #59.5: a callee that is neither N_IDENT nor N_DOT — an + // indirect call through a fn-pointer VALUE (`(*f)()`, + // N_UN TK_STAR) — used to fall to false, so a slice return + // pushed one word and cgcall's pop sizer under-drained. + // Read the checker stamp (check.ww fn-VALUE callee arm), + // the callee-shape-blind key cstage node_isslice uses. + return syntax.typeisslice(n.type_: *syntax.tinfo); }; // N_DOT: read the checker-stamped n.type_. Struct field, nested // dot, value-struct hops, and pseudo-fields (.ptr/.len/.cap) all @@ -1465,6 +1476,14 @@ fn nodeisstr(c: *cgen, n: *syntax.node) bool = { if (deflookup(c, nm)) { return syntax.typeisstr(n.type_: *syntax.tinfo); }; + // #142-adjacent: a module-global let/const str ident (path's + // `const sepstr: str`) is neither a frame slot nor a def; the + // false fall-through sent its union-widen push to the scalar + // arm (len/cap zero-filled). Read the checker stamp, the key + // cstage node_isstr uses unconditionally. + if (isletvar(c, nm)) { + return syntax.typeisstr(n.type_: *syntax.tinfo); + }; return false; }; if (k == syntax.nkind.N_CALL) { @@ -1489,7 +1508,9 @@ fn nodeisstr(c: *cgen, n: *syntax.node) bool = { return isstrtype(c, rtyp); }; }; - return false; + // #59.5: indirect fn-pointer callee (`(*f)()`) — read the + // checker stamp; see the nodeisslice N_CALL twin above. + return syntax.typeisstr(n.type_: *syntax.tinfo); }; // #46 (F7-c2): `arr[i]` whose element is a str. The prior structural // walk only recognised N_IDENT and N_DOT bases (idxelemtn off the diff --git a/test/wcc/989_lib_byteid.c b/test/wcc/989_lib_byteid.c index 2f653955..12e7fe09 100644 --- a/test/wcc/989_lib_byteid.c +++ b/test/wcc/989_lib_byteid.c @@ -157,8 +157,14 @@ static const struct ent ents[] = { { .fixture = "lib/encoding/base64/base64_test.ww", .mode = M_ID }, /* #59.4 hextest graduated to M_ID above (#22a reviewer fixes) */ + /* #59.5 graduated: the wwstage nodeisstr/nodeisslice recognizers + * fell to false for an indirect fn-pointer callee (`(*f)()`) and + * for module-global let/const idents, so a 3-word str/slice value + * pushed one word (or zero-filled its widen box) and cgcall + * under-drained; both now fall back to the checker stamp. Runtime + * pins: r595_indirect_call_str_arg, globalconst_{str,slice}_widen. */ { .fixture = "lib/errors/errnotest.ww", - .mode = M_DIVERGE, .cite = "#59.5" }, + .mode = M_ID, .cite = "#59.5 graduated by the stamp-keyed recognizers" }, /* #59.6 fmt graduated to M_ID above (#129 fix) */ /* #59.7 siphash graduated to M_ID above (#61 fix) */ { .fixture = "lib/log/logtest.ww", diff --git a/test/wcc/data/globalconst_slice_widen/case.ww b/test/wcc/data/globalconst_slice_widen/case.ww new file mode 100644 index 00000000..62d5e219 --- /dev/null +++ b/test/wcc/data/globalconst_slice_widen/case.ww @@ -0,0 +1,23 @@ +//ww:run +// A module-global const []u8 widened into a tagged-union call arg — +// the slice twin of globalconst_str_widen (path's const dot/dotdot +// shape). +package main; + +const gb: []u8 = ['x', 'y', 'z']; + +fn f(x: ([]u8 | i32)) i32 = { + match (x) { + case let s: []u8 => { + if (len(s) != 3) { return 1; }; + if (s[2] != 122u8) { return 2; }; + return 0; + }; + case let v: i32 => { return 3; }; + }; + return 4; +}; + +fn main() i32 = { + return f(gb); +}; diff --git a/test/wcc/data/globalconst_str_widen/case.ww b/test/wcc/data/globalconst_str_widen/case.ww new file mode 100644 index 00000000..bf446704 --- /dev/null +++ b/test/wcc/data/globalconst_str_widen/case.ww @@ -0,0 +1,24 @@ +//ww:run +// A module-global const str widened into a tagged-union call arg. +// The pre-fix wwstage nodeisstr N_IDENT arm knew only frame slots and +// defs, so the widen push took the scalar arm and zero-filled len/cap +// (path.sepstr's strings.hasprefix shape). +package main; + +const gs: str = "hello"; + +fn f(x: (str | i32)) i32 = { + match (x) { + case let s: str => { + if (s.len != 5) { return 1; }; + if (s[4] != 111u8) { return 2; }; + return 0; + }; + case let v: i32 => { return 3; }; + }; + return 4; +}; + +fn main() i32 = { + return f(gs); +}; diff --git a/test/wcc/data/r595_indirect_call_str_arg/case.ww b/test/wcc/data/r595_indirect_call_str_arg/case.ww new file mode 100644 index 00000000..4d247c11 --- /dev/null +++ b/test/wcc/data/r595_indirect_call_str_arg/case.ww @@ -0,0 +1,22 @@ +//ww:run +// #59.5: a str returned by an INDIRECT fn-pointer call, passed +// directly as a call arg. The pre-fix wwstage nodeisstr/nodeisslice +// N_CALL arms were callee-name-keyed and fell to false for the +// N_UN(STAR) callee, so only the ptr word was pushed and the callee +// read a shifted register file (len=b.ptr). +package main; + +fn hello() str = { + return "hello"; +}; + +fn taker(s: str) i32 = { + if (s.len != 5) { return 1; }; + if (s[0] != 104u8) { return 2; }; + return 0; +}; + +fn main() i32 = { + let f = (&hello): *fn() str; + return taker((*f)()); +};