ww: classify imported call returns canonically

This commit is contained in:
2026-08-14 11:14:52 +09:00
parent 10e02a00ee
commit 72e890d5aa
2 changed files with 22 additions and 11 deletions

View File

@@ -2492,13 +2492,20 @@ fn sretretsizetn(c: *cgen, t: *syntax.tinfo) i32 = {
return 0; return 0;
}; };
// callsretsize — if N_CALL `n`'s callee returns a plain TY_STRUCT // callsretsize — classify an N_CALL from the checker-stamped result type.
// > 24B, return its natural size; else 0. Wraps sretretsize over the // The callee's return AST can belong to an imported interface source whose
// callee's resolved return type, used by cglet / cgassign receive // private canonical qualifier is deliberately invisible in the caller's file
// sites and cgcall to detect sret at the receive / emit boundaries. // 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 = { fn callsretsize(c: *cgen, n: *syntax.node) i32 = {
if (n == nil) { return 0; }; if (n == nil) { return 0; };
if (n.kind != syntax.nkind.N_CALL) { 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; let callee: *syntax.node = n.lhs;
if (callee == nil) { return 0; }; if (callee == nil) { return 0; };
let cn: str; let cn: str;
@@ -2519,10 +2526,8 @@ fn callsretsize(c: *cgen, n: *syntax.node) i32 = {
}; };
if (cn.len == 0) { return 0; }; if (cn.len == 0) { return 0; };
let rtyp: *syntax.node = fnretlookupmod(c, cn, cmod); let rtyp: *syntax.node = fnretlookupmod(c, cn, cmod);
// #129: sretretsize must see the CALLEE's module context so // The unchecked fallback still needs the callee module for its
// aliassamemod resolves aliases from the callee's module (not the // same-module alias preference; checked production calls bypass it above.
// caller's). Mirrors cstage operating on resolved Type* objects
// (type_chase_named never has this confusion). Swap + restore.
let savedmod: str = c.curmod; let savedmod: str = c.curmod;
if (cmod.len > 0) { c.curmod = cmod; }; if (cmod.len > 0) { c.curmod = cmod; };
let r: i32 = sretretsize(c, rtyp); let r: i32 = sretretsize(c, rtyp);

View File

@@ -224,10 +224,16 @@ fn m2positive(pkg: str) void = {
fail("qualified", "cs.wwi != ww.wwi"); fail("qualified", "cs.wwi != ww.wwi");
}; };
let body: str = testenv.readfile(cs); let body: str = testenv.readfile(cs);
if (!testenv.has(body, "export fn use(x: dep.Clash) i32;")) { if (!testenv.has(body, "import __wwi_612e646570 a.dep;\n")
fail("qualified", ".wwi dropped the qualified signature"); || !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 };")) { || !testenv.has(body, "export type Clash = struct { x: i32 };")) {
fail("qualified", ".wwi omitted the signature's origin-owned type fact"); fail("qualified", ".wwi omitted the signature's origin-owned type fact");
}; };