w6c: classify fn-value idents by the stamped TY_FN, not the leaf table

wwstage cgen re-derived fn-ness by NAME: cgident's fn arm gated on
fnretlookup, whose cross-module leaf fallback matches a foreign
same-leaf fn — inside aa.getv the bare `v` (aa's i32 global, stamped
by the #55 prefer fix) took the fn-address arm for main's `fn v` and
emitted LEAQ aa.v(SB) with no deref; the ww_ww-built 794 program
exited 0 instead of 7. cstage reads the checker-stamped type
(cgen.c:4315). Convert the three value-position sites to the stamp
(same predicate-to-stamp conversion as the #14 F7-c7 `&fn` arm):
cgident (now TY_FN -> def -> let, cstage's order — the def-first
order also mis-read a foreign scalar def shadowed by a curmod fn),
the cgdot module-qualified value arm, and the `&ident` #180 arm.
Call-position fnretlookup consumers are unchanged.

The 794 two-file program is now cs==ww byte-identical and both
drivers run exit 7; the carrier retires in the follow-up.
This commit is contained in:
2026-08-08 17:13:05 +09:00
parent 07dfc0e6af
commit 9382eedbbb

View File

@@ -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");