wcc/cgen: #124 cross-module &fn in a const — N_DOT reloc + checker accept (both-stage)
A cross-module `&module.fn` in a const emitted no static reloc (the const was never defined -> w6l undefined-reference, both stages) and wwstage's checker rejected the const fn-table. #117/#119 wired the &fn->DATAR const-data reloc for SAME-module &fn only; charclass_map (fold-6) needs cross-module (12x &ascii.isXXX). cgen: add the N_DOT arm to the &fn->symbol helper (node_fnptr_sym / nodefnptr + the two ww emit sites), emitting mafn(leaf, module-ident) -- exactly the symbol a runtime &mod.fn or a direct cross-module call already emits. The helper is the SSoT for both the scalar (#119) and tuple-row (#117) const-data paths, so one arm closes both. checker: type a cross-module `&mod.fn` as `*fn(...)` in the TK_AMP arm (the N_DOT twin of #206's N_IDENT fn-ptr synthesis, gated on a resolved SK_FN/N_FNDECL leaf), so isassignable affirmatively accepts the const table -- aligning wwstage UP to cstage's actual acceptance reason rather than by abdication. The SK_FN gate keeps a non-fn `&mod.var` from synthesizing a fn type (the one pre-existing nonfn-scalar cs!=ww slip is N_IDENT-base, untouched and reproduces same-module). One consumer-coupled commit (the checker accept gates wwstage cgen, so neither half is independently testable). Narrow: slice-row + scalar only; fixed-array (#118) and struct-field (#129) stay separate. Both stages emit the correct cross-module symbols at the right tuple-slot offsets -> byte-identical (990-997 green). Pin 949_xmod_fnptr_const_run (distinct fns so a wrong reloc is caught + the SK_FN-gate axis). This was the last fold-6 cgen blocker; charclass_map is now unblocked.
This commit is contained in:
@@ -12640,6 +12640,33 @@ fn unoptype(c: *checker, e: *node) *node = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #124: a cross-module `&mod.fn` — same synthesis as the
|
||||
// N_IDENT arm, but the fn decl lives in the qualifier module
|
||||
// (scopelookupinmodule). Without this the address-of falls to
|
||||
// the generic `*opt` path and a const `[](str,*fn)` table's
|
||||
// nested cross-module &fn element fails the isassignable
|
||||
// typeeq → confident reject; cstage types `&mod.fn` as `*fn`
|
||||
// natively (a dotted fn ref is TY_FN), so this aligns ww UP to
|
||||
// cstage's actual acceptance reason.
|
||||
if (e.lhs.kind == nkind.N_DOT) {
|
||||
if (e.lhs.lhs != nil && e.lhs.lhs.kind == nkind.N_IDENT) {
|
||||
let fs: *sym = scopelookupinmodule(c.cur, e.lhs.lhs.str, e.lhs.str);
|
||||
if (fs != nil) {
|
||||
if (fs.skind == skind.SK_FN) {
|
||||
if (fs.decl != nil) {
|
||||
if (fs.decl.kind == nkind.N_FNDECL) {
|
||||
let synth: *node = newnode(nkind.N_TFN, "", 0, 0);
|
||||
synth.lhs = fs.decl.lhs;
|
||||
synth.list = fs.decl.list;
|
||||
let pf: *node = newnode(nkind.N_TPTR, "", 0, 0);
|
||||
pf.lhs = synth;
|
||||
return pf;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// opt nil → propagation from inherent-IDENT bail (5-lite-b
|
||||
// #34). Generic &expr widens to *opt; without opt we can't
|
||||
@@ -40228,6 +40255,22 @@ fn nodefnptr(c: *cgen, ev: *node) bool = {
|
||||
if (ev.op != tkind.TK_AMP) { return false; };
|
||||
let opnd: *node = ev.lhs;
|
||||
if (opnd == nil) { return false; };
|
||||
// #124: a cross-module `&mod.fn` — opnd is an N_DOT whose base is an
|
||||
// SK_USE module qualifier (not a local / let / def), and whose leaf
|
||||
// resolves to a fn in that module. Mangle via the module ident (not
|
||||
// curmod) at the emit sites so the reloc targets the same TEXT symbol
|
||||
// the runtime `&mod.fn` emits (cgenexpr.ww N_DOT addr-of arm). The
|
||||
// N_DOT arm of the #117/#119 reloc helper.
|
||||
if (opnd.kind == nkind.N_DOT) {
|
||||
if (opnd.lhs == nil) { return false; };
|
||||
if (opnd.lhs.kind != nkind.N_IDENT) { return false; };
|
||||
let basenm: str = opnd.lhs.str;
|
||||
if (localfindnode(c, basenm) != nil) { return false; };
|
||||
if (isletvar(c, basenm)) { return false; };
|
||||
if (deflookup(c, basenm)) { return false; };
|
||||
if (fnretlookupmod(c, opnd.str, basenm) == nil) { return false; };
|
||||
return true;
|
||||
};
|
||||
if (opnd.kind != nkind.N_IDENT) { return false; };
|
||||
if (fnretlookup(c, opnd.str) == nil) { return false; };
|
||||
return true;
|
||||
@@ -40365,7 +40408,14 @@ fn emittuplerowrelocs(c: *cgen, name: str, module: str, backing: bool, rowoff: i
|
||||
emitline("+");
|
||||
emitint(foff: i64);
|
||||
emitline("(SB),");
|
||||
emitfnname(c, ev.lhs.str, c.curmod);
|
||||
// #124: a cross-module `&mod.fn` operand mangles
|
||||
// the leaf with the MODULE ident; same-module `&fn`
|
||||
// stays on curmod.
|
||||
if (ev.lhs.kind == nkind.N_DOT) {
|
||||
emitfnname(c, ev.lhs.str, ev.lhs.lhs.str);
|
||||
} else {
|
||||
emitfnname(c, ev.lhs.str, c.curmod);
|
||||
};
|
||||
emitline("(SB)\n");
|
||||
};
|
||||
// #22: slot stride via the accessor (tagged is
|
||||
@@ -40545,7 +40595,14 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitline("DATAR ");
|
||||
emitsymnamehint(c, nm, d.nmod);
|
||||
emitline("+0(SB),");
|
||||
emitfnname(c, r.lhs.str, c.curmod);
|
||||
// #124: cross-module `&mod.fn` mangles the
|
||||
// leaf with the MODULE ident; same-module `&fn`
|
||||
// stays on curmod.
|
||||
if (r.lhs.kind == nkind.N_DOT) {
|
||||
emitfnname(c, r.lhs.str, r.lhs.lhs.str);
|
||||
} else {
|
||||
emitfnname(c, r.lhs.str, c.curmod);
|
||||
};
|
||||
emitline("(SB)\n");
|
||||
} else if (ok) {
|
||||
emitline("DATAW ");
|
||||
|
||||
Reference in New Issue
Block a user