diff --git a/Makefile b/Makefile index d1791f28..ab0feb3d 100644 --- a/Makefile +++ b/Makefile @@ -730,16 +730,7 @@ DATABYTEID_EXPECTED_MIN = 915 # discipline): each entry must still build on both stages AND still differ. # When a compiler fix lands the entry fails demanding graduation out of # this list rather than silently widening coverage. -# r700_strings_byteindex: latent, exposed by the 700_e2e migration — -# wwstage spills a wwi-decl'd tagged call-result arg through a 16B -# scratch pair where cstage PUSHQes AX/DX (frame 64 vs 80); runtime- -# equivalent, sibling of the closed #211 name-keyed family. -# r839_xmod_nominal_match: latent, exposed by the 839 migration (the -# carrier waived byte-id silently) — same spill family as r700: wwstage -# routes the cross-module tagged call-result through a 16B scratch pair -# where cstage PUSHQes AX/DX (frame 16 vs 32); runtime-equivalent -# (both stages run exit 42). -DATABYTEID_DIVERGED = r700_strings_byteindex r839_xmod_nominal_match +DATABYTEID_DIVERGED = $(if $(DATABYTEID_FILES),,$(error test-byteid: empty data corpus)) test-data-byteid: $(BIN)/ww $(BIN)/w6c $(BIN)/w6c_ww @work=$$(mktemp -d "$(CURDIR)/$(DATABYTEID_DIR).XXXXXX") || exit 1; \ diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 7a964dfc..9dc41749 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -1446,12 +1446,14 @@ fn pushargsrev(c: *cgen, arg: *syntax.node, param: *syntax.node, memphase: bool, export fn taggedcallslot(c: *cgen, n: *syntax.node) i32 = { if (n == nil) { return 0; }; if (n.kind != syntax.nkind.N_CALL) { return 0; }; - let callee: *syntax.node = n.lhs; - if (callee == nil) { return 0; }; - if (callee.kind != syntax.nkind.N_IDENT) { return 0; }; - let rtyp: *syntax.node = fnretlookup(c, callee.str); - if (!istaggedtype(c, rtyp)) { return 0; }; - return slotsize(c, rtyp); + // Stamped result type, never a callee-name lookup (#209/#211 + // discipline): the old N_IDENT + fnretlookup gate returned 0 for + // every N_DOT / wwi-decl'd cross-module callee, so their tagged + // call-result ARGS fell off the cursor-push path into a 16B + // scratch spill. cstage keys the same push on args[i]->type + // (cgen.c tagged_arg_size). + if (!istaggedtype(c, n)) { return 0; }; + return slotsize(c, n); }; fn nodeisslice(c: *cgen, n: *syntax.node) bool = { diff --git a/test/lang/taggedarg_callresult_test.ww b/test/lang/taggedarg_callresult_test.ww new file mode 100644 index 00000000..f3314ac8 --- /dev/null +++ b/test/lang/taggedarg_callresult_test.ww @@ -0,0 +1,49 @@ +// taggedarg_callresult_test — a tagged-union call RESULT passed +// directly as a call ARGUMENT rides the register cursor (PUSHQ AX/DX +// pairs), classified by the STAMPED result type (taggedcallslot); the +// old callee-name gate dropped fn-pointer and cross-module callees to +// a 16B scratch spill (the r700_strings_byteindex / r839 byteid pins; +// the corpus fixtures own the cross-module byte-id legs). + +package taggedarg_callresult_test; + +fn ok(v: i64) (i64 | void) = { return v; }; +fn unwrap(r: (i64 | void)) i64 = { + match (r) { + case let n: i64 => return n; + case void => return -1; + }; +}; + +@test fn samemod() void = { + assert(unwrap(ok(42)) == 42i64); +}; + +@test fn fnptr() void = { + let f: *fn(v: i64) (i64 | void) = &ok; + assert(unwrap(f(7)) == 7i64); +}; + +fn mkf(v: f64) (f64 | void) = { return v; }; +fn pickf(r: (f64 | void)) i32 = { + match (r) { + case let d: f64 => return (d: i32); + case void => return -1; + }; +}; + +@test fn floatmember() void = { + assert(pickf(mkf(5.0)) == 5); +}; + +fn mks(s: str) (str | void) = { return s; }; +fn picks(r: (str | void)) i32 = { + match (r) { + case let s: str => return (s.len: i32); + case void => return -1; + }; +}; + +@test fn strmember() void = { + assert(picks(mks("hello")) == 5); +};