diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 3c5e41fa..06856511 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -13105,6 +13105,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. @@ -13596,6 +13615,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; @@ -14565,6 +14595,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; diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 5576fe31..7a238248 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -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; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 06509582..d92ddfa9 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -13105,6 +13105,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. @@ -13596,6 +13615,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; @@ -14565,6 +14595,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;