From 72e890d5aa892257b5c227bc89e66e92c5327da0 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Fri, 14 Aug 2026 11:14:52 +0900 Subject: [PATCH] ww: classify imported call returns canonically --- selfhost/cmd/wcc/cgenutil.ww | 21 +++++++++++++-------- test/byteid/wwi_test.ww | 12 +++++++++--- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index d71d1465..8a1bfe5f 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -2492,13 +2492,20 @@ fn sretretsizetn(c: *cgen, t: *syntax.tinfo) i32 = { return 0; }; -// callsretsize — if N_CALL `n`'s callee returns a plain TY_STRUCT -// > 24B, return its natural size; else 0. Wraps sretretsize over the -// callee's resolved return type, used by cglet / cgassign receive -// sites and cgcall to detect sret at the receive / emit boundaries. +// callsretsize — classify an N_CALL from the checker-stamped result type. +// The callee's return AST can belong to an imported interface source whose +// private canonical qualifier is deliberately invisible in the caller's file +// scope. Re-resolving that spelling here loses the canonical type and can +// select the wrong return ABI. Cstage classifies the checked Type directly; +// sretretsizetn is its isomorphic tinfo path. fn callsretsize(c: *cgen, n: *syntax.node) i32 = { if (n == nil) { return 0; }; if (n.kind != syntax.nkind.N_CALL) { return 0; }; + if (n.type_ != nil) { + return sretretsizetn(c, n.type_: *syntax.tinfo); + }; + // Raw cgen-only callers may supply an unchecked AST. Retain their + // historical name-based fallback; production checked calls never take it. let callee: *syntax.node = n.lhs; if (callee == nil) { return 0; }; let cn: str; @@ -2519,10 +2526,8 @@ fn callsretsize(c: *cgen, n: *syntax.node) i32 = { }; if (cn.len == 0) { return 0; }; let rtyp: *syntax.node = fnretlookupmod(c, cn, cmod); - // #129: sretretsize must see the CALLEE's module context so - // aliassamemod resolves aliases from the callee's module (not the - // caller's). Mirrors cstage operating on resolved Type* objects - // (type_chase_named never has this confusion). Swap + restore. + // The unchecked fallback still needs the callee module for its + // same-module alias preference; checked production calls bypass it above. let savedmod: str = c.curmod; if (cmod.len > 0) { c.curmod = cmod; }; let r: i32 = sretretsize(c, rtyp); diff --git a/test/byteid/wwi_test.ww b/test/byteid/wwi_test.ww index d47fb60e..08d2d331 100644 --- a/test/byteid/wwi_test.ww +++ b/test/byteid/wwi_test.ww @@ -224,10 +224,16 @@ fn m2positive(pkg: str) void = { fail("qualified", "cs.wwi != ww.wwi"); }; let body: str = testenv.readfile(cs); - if (!testenv.has(body, "export fn use(x: dep.Clash) i32;")) { - fail("qualified", ".wwi dropped the qualified signature"); + if (!testenv.has(body, "import __wwi_612e646570 a.dep;\n") + || !testenv.has(body, + "export fn use(x: __wwi_612e646570.Clash) i32;")) { + fail("qualified", ".wwi did not canonicalize the qualified signature"); }; - if (!testenv.has(body, "//ww:module a.dep\n") + if (testenv.has(body, "export fn use(x: dep.Clash) i32;")) { + fail("qualified", ".wwi retained the source-file qualifier"); + }; + if (!testenv.has(body, + "//ww:module a.dep\npackage __wwi_612e646570;\n") || !testenv.has(body, "export type Clash = struct { x: i32 };")) { fail("qualified", ".wwi omitted the signature's origin-owned type fact"); };