diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index 69af04b8..b875ba2b 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -1227,6 +1227,25 @@ fn cgident(c: *cgen, n: *syntax.node) void = { }; return; }; + // Fn-name used as a value (e.g. `let f = some_fn;`) — LEAQ the + // symbol address into AX. Classified by the checker-STAMPED + // TY_FN type, never by a name lookup: the unit-wide fnrets + // table matches a foreign same-leaf fn (main.v shadowing aa's + // i32 global while cgen'ing aa.getv — the #55 cgen-side + // sibling), where the stamp already resolved via + // scopelookupprefer. Arm order TY_FN → def → let mirrors + // cstage cgexpr N_IDENT (cmd/w6c/cgen.c:4315-4374); same + // predicate-to-stamp conversion as the #14 F7-c7 `&fn` arm + // (cgen.ww nodefnptr). emitfnname still handles ffiresolve + + // module-mangling, so a body-less FFI binding emits its + // @symbol() name. + let fu: *syntax.tinfo = tichase(n.type_: *syntax.tinfo); + if (fu != nil && fu.kind == syntax.tykind.TY_FN) { + emitline("\tLEAQ\t"); + emitfnname(c, nm, c.curmod); + emitline("(SB), AX\n"); + return; + }; // Top-level `def` constant — load from its DATA symbol. // Str defs (rhs N_STRLIT) aren't laid out at a SB symbol; the // MOVQ symname(SB) fallback below would emit a bogus reference @@ -1274,19 +1293,6 @@ fn cgident(c: *cgen, n: *syntax.node) void = { emitline("(SB), AX\n"); return; }; - // Fn-name used as a value (e.g. `let f = some_fn;` or - // `... = some_fn;`). LEAQ the symbol address into AX. The - // emitfnname helper handles ffiresolve and module-mangling - // in one go, so a body-less FFI binding emits the C symbol - // it was declared with via @symbol(), not the ww-side ident. - // Bare ident → same-module by ww's resolver, hint with c.curmod. - let rtyp: *syntax.node = fnretlookup(c, nm); - if (rtyp != nil) { - emitline("\tLEAQ\t"); - emitfnname(c, nm, c.curmod); - emitline("(SB), AX\n"); - return; - }; // Top-level mutable `let` — RIP-relative load from its DATAW // slot. Mirrors C cgen's catch-all `MOVQ masym(s), AX` for // scalar lets, plus the (LEAQ, MOVQ, MOVQ[, MOVQ]) sequence @@ -4652,8 +4658,13 @@ fn cgdot(c: *cgen, n: *syntax.node) void = { // lhs.str is the explicit module hint so a same-leaf // def in another module (head of c.fnrets) can't shadow // the explicit qualifier (#17 N_DOT-arm omission audit). - let frt: *syntax.node = fnretlookupmod(c, fld, usehint(c, lhs.str)); - if (frt != nil) { + // Fn value vs data read: classified by the STAMPED + // TY_FN type, not fnretlookupmod — its leaf fallback + // matches a curmod fn against a foreign scalar def + // (`mod.MSG` under a curmod `fn MSG`) and emits LEAQ + // where cstage MOVQs the data. #55 cgen-side class. + let du: *syntax.tinfo = tichase(n.type_: *syntax.tinfo); + if (du != nil && du.kind == syntax.tykind.TY_FN) { emitline("\tLEAQ\t"); emitfnname(c, fld, usehint(c, lhs.str)); emitline("(SB), AX\n"); @@ -5286,7 +5297,12 @@ fn cgun(c: *cgen, n: *syntax.node) void = { // (LEAQ + emitfnname(c, nm, c.curmod)). Previously // fell through silently — the AX-store at the // assign site picked up whatever AX held. - if (fnretlookup(c, nm) != nil) { + // Gate on the stamped TY_FN (cstage cgen.c amp + // arm parity), not the name table — the leaf + // fallback matches a foreign same-leaf fn + // (#55 cgen-side class). + let ou: *syntax.tinfo = tichase(opnd.type_: *syntax.tinfo); + if (ou != nil && ou.kind == syntax.tykind.TY_FN) { emitline("\tLEAQ\t"); emitfnname(c, nm, c.curmod); emitline("(SB), AX\n");