wcc/ww: exprtype stamps bare fn rvalue with its fn type, not return type

For an SK_FN sym, exprtype's N_IDENT and N_DOT arms returned decl.lhs
(the return type), so a bare fn rvalue compared as its return type —
latent silent mis-accept of mismatched-signature fn assignment (the
706/768 over-reject under any general check was the visible face).
Synthesize the N_TFN instead (mirror of assignableaddrfn's synth), and
add the N_TFN-vs-N_TFN confident-mismatch reject in isassignable.
harec: resolve_function check.c:4279-4288 (obj->type=fntype),
EXP_ACCESS check.c:341-343 (bare ident yields obj->type, no decay),
call check.c:1573, assignability types.c:1001; cstage twin
check.c:1313 + build_fn_type check.c:2611. cgen is name-keyed on fn
rvalues (never reads the stamp): zero asm delta, byte-id held.
This commit is contained in:
2026-06-11 20:37:47 +09:00
parent 4c52660bab
commit fa17459558
3 changed files with 135 additions and 0 deletions

View File

@@ -2685,6 +2685,25 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
let s: *sym = scopelookupprefer(c.cur, c.curmod, e.str);
if (s == nil) { return nil; };
if (s.decl == nil) { return nil; };
// #34: a bare fn-name rvalue types as its FN TYPE, not its return
// type. decl.lhs is the RETURN type for an N_FNDECL, so synthesize
// the N_TFN over (ret=decl.lhs, params=decl.list) — the shape
// assignableaddrfn builds (:3841). Mirrors cstage: build_fn_type at
// fn-decl install (cmd/wcc/check.c:2915/2931) stored on the sym and
// returned verbatim by cexpr N_IDENT (check.c:1313); harec EXP_ACCESS
// yields the fn object's type with NO decay (ref/harec/src/check.c
// :341-343; the fn obj is built .storage=STORAGE_FUNCTION at :4279-
// 4288, assignable iff dealias-equal fn types, types.c:1001). Was the
// root of the #24 fn-family over-rejects (`let p: fn()i32 = g` compared
// i32 vs the fn type). cgen lowers a fn rvalue name-keyed via
// fnretlookup (cgenexpr.ww:1100), never off this stamp → byte-id-neutral.
if (s.skind == skind.SK_FN) {
let ft: *node = newnode(nkind.N_TFN, "", 0, 0);
ft.lhs = s.decl.lhs;
ft.list = s.decl.list;
e.type_ = tinfofornode(c, ft): *void;
return ft;
};
let t: *node = s.decl.lhs;
// Propagate the declared type's tinfo onto the use site so
// downstream cgen walkers can read n.type_ off an ident.
@@ -3176,6 +3195,17 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
if (ms.skind == skind.SK_USE || ms.use_alias != 0i32) {
let fs: *sym = scopelookupinmodule(c.cur, lhsn.str, e.str);
if (fs != nil) { if (fs.decl != nil) {
// #34: a module-qualified bare fn rvalue `mod.fn` types as
// its FN TYPE (twin of the N_IDENT arm, :2688); decl.lhs is
// the RETURN type for an N_FNDECL. Pins 706 (`let p1: fn()i32
// = mod1.ping`).
if (fs.skind == skind.SK_FN) {
let ft: *node = newnode(nkind.N_TFN, "", 0, 0);
ft.lhs = fs.decl.lhs;
ft.list = fs.decl.list;
e.type_ = tinfofornode(c, ft): *void;
return ft;
};
let tn: *node = fs.decl.lhs;
if (tn != nil) {
e.type_ = tinfofornode(c, tn): *void;
@@ -4145,6 +4175,21 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
};
};
};
// #34: two bare fn types reaching here are NOT structurally equal
// (typeeqast returned true at the top otherwise) — the fn signatures
// differ, a confident reject mirroring cstage's structural fn
// type_assignable (`init fn() str not assignable to declared fn() i32`;
// harec types.c:1001 dealias-equal fn types). Manifests only now that
// S1/S2 stamp a bare fn rvalue with its fn type: `let p: fn()i32 = h`
// (h: fn()str) was a silent mis-accept via the lenient catch-all below.
// The matched-sig case already returned true via typeeqast at the top.
// The `&fn` (*fn) vs bare-fn KIND mismatch (du N_TFN, su N_TPTR) is a
// different shape, closed by c3's aggregate-kind reject, not here.
if (du.kind == nkind.N_TFN) {
if (su.kind == nkind.N_TFN) {
return false;
};
};
// Anything else: don't claim confidence.
*confident = false;
return true;