w6c+selfhost: widen concrete variant to tagged-union call arg
Tagged-union widening already fired for `let r: (str|rune) = "...";`, `r = "...";`, and `return "..."` from a tagged-returning fn — but not at call sites, so `fn f(x: (str|rune))` couldn't be called with a bare str or rune. The arg was pushed as its own static type (2 words for str, 1 for rune) while the callee's slot expected 3 (tag + payload). C cgen: at the call boundary, look up the callee's declared param type per arg. When the param is TY_TAGGED and the arg is a concrete variant, materialise (tag, value-words, padding) sized to the param's tagged_arg_size — then the existing pop-into-arg-regs logic picks it up. Nullable `(*T | void)` collapses to a single 8B push. selfhost: fnret now carries the params head alongside rtype (amalloc bumped to 48); pushargsrev takes the matching param node and runs the same widening sequence per arg. The pop drain in cgcall already handled extra slot words, so no change needed on that side. Verified with a smoke covering str/rune literals, typed locals, pre-existing tagged-local pass-through, and nullable widening from a raw pointer. Selfhost emits byte-identical asm to C cgen on the test.
This commit is contained in:
@@ -1315,6 +1315,7 @@ fn emitdatasection(c: *cgen) void = {
|
||||
type fnret = struct {
|
||||
fname: str,
|
||||
rtype: *node,
|
||||
params: *node,
|
||||
frnext: *fnret,
|
||||
};
|
||||
|
||||
@@ -1323,9 +1324,10 @@ fn collectfnrets(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_FNDECL) {
|
||||
let f: *fnret = amalloc(c.a, 32u64): *fnret;
|
||||
let f: *fnret = amalloc(c.a, 48u64): *fnret;
|
||||
f.fname = d.str;
|
||||
f.rtype = d.lhs;
|
||||
f.params = d.list;
|
||||
f.frnext = c.fnrets;
|
||||
c.fnrets = f;
|
||||
};
|
||||
@@ -1343,6 +1345,19 @@ fn fnretlookup(c: *cgen, name: str) *node = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// fnparamslookup — head of the declared param-list for a fn, or nil
|
||||
// if the name isn't a registered fn. Used by cgcall / pushargsrev to
|
||||
// detect implicit widening from a concrete variant into a tagged-union
|
||||
// parameter slot.
|
||||
fn fnparamslookup(c: *cgen, name: str) *node = {
|
||||
let f: *fnret = c.fnrets;
|
||||
for (f != nil) {
|
||||
if (streq(f.fname, name)) { return f.params; };
|
||||
f = f.frnext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// ---- def-constant registry ------------------------------------------
|
||||
//
|
||||
// `def NAME: T = LIT;` becomes a DATA symbol the C-side w6c emits; an
|
||||
|
||||
Reference in New Issue
Block a user