wcc: stamp fn-ptr-value call results from the callee fn-type
A call whose callee is a fn-VALUE (a fn-pointer struct field like w.emit(...), or a local/param) had no free SK_FN entry, so exprtype's name lookup missed and the N_CALL went nil-stamped (the fn-ptr-field class of the asserttyped gap audit — 3 warns at smoke). When the name lookup misses, fall back to harec's check_expr_call shape: read the result off the checked callee node's own type — autodereference + dealias to the TY_FN, take its result (ref/harec/src/check.c :1566-1581). Name lookup stays primary: a fn-NAME callee node carries its return type, not its fn-type, so an N_TFN check first would mis-yield void for a fn-returning-fn; only genuine fn-value callees reach the fallback. Drives the 901 gap-corpus B count to 0 — with A/D already closed, only the legitimate abort exemption (C) remains before the bail can arm. Byte-id holds (cgen's fn-ptr detection is structural, independent of the stamp; the lib/io return-forwarding site is cs==ww on both bootstrap combined.ww).
This commit is contained in:
@@ -12327,14 +12327,33 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
|||||||
s = scopelookup(c.cur, nm);
|
s = scopelookup(c.cur, nm);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (s == nil) { return nil; };
|
if (s != nil) { if (s.skind == skind.SK_FN) { if (s.decl != nil) {
|
||||||
if (s.skind != skind.SK_FN) { return nil; };
|
|
||||||
if (s.decl == nil) { return nil; };
|
|
||||||
// fn-decl's lhs is the return-type AST node. Mirrors cstage
|
// fn-decl's lhs is the return-type AST node. Mirrors cstage
|
||||||
// cmd/wcc/check.c:984+ regular-CALL `n->type = build_fn_type(c,
|
// cmd/wcc/check.c:984+ regular-CALL `n->type =
|
||||||
// s->decl)->ret` shape.
|
// build_fn_type(c, s->decl)->ret` shape.
|
||||||
e.type_ = tinfofornode(c, s.decl.lhs): *void;
|
e.type_ = tinfofornode(c, s.decl.lhs): *void;
|
||||||
return s.decl.lhs;
|
return s.decl.lhs;
|
||||||
|
}; }; };
|
||||||
|
// A callee that is a fn-VALUE — a fn-pointer struct field
|
||||||
|
// (`w.emit(...)`), local, or param — has no free SK_FN entry, so
|
||||||
|
// the name lookup above misses. Read the result off the checked
|
||||||
|
// callee node's own type instead: autodereference + dealias to
|
||||||
|
// the TY_FN, then take its result. Mirrors harec's check_expr_call
|
||||||
|
// `expr->result = type_dealias(check_autodereference(lvalue->
|
||||||
|
// result))->func.result` (ref/harec/src/check.c:1566-1581). The
|
||||||
|
// name path stays primary because a fn-NAME callee node in wwstage
|
||||||
|
// already carries its RETURN type (fn-decl.lhs), not its fn-type —
|
||||||
|
// so a `fn make() fn() void` callee would otherwise mis-yield void.
|
||||||
|
let ct: *node = resolvealias(c, unwrapbang(exprtype(c, callee, nil)));
|
||||||
|
for (ct != nil && ct.kind == nkind.N_TPTR) {
|
||||||
|
ct = resolvealias(c, unwrapbang(ct.lhs));
|
||||||
|
};
|
||||||
|
if (ct != nil) { if (ct.kind == nkind.N_TFN) {
|
||||||
|
let res: *node = ct.lhs;
|
||||||
|
e.type_ = tinfofornode(c, res): *void;
|
||||||
|
return res;
|
||||||
|
}; };
|
||||||
|
return nil;
|
||||||
};
|
};
|
||||||
if (k == nkind.N_DOT) {
|
if (k == nkind.N_DOT) {
|
||||||
// A.6.1.5a — fold cases only. Mirrors cstage cmd/wcc/check.c
|
// A.6.1.5a — fold cases only. Mirrors cstage cmd/wcc/check.c
|
||||||
|
|||||||
@@ -2297,14 +2297,33 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
|||||||
s = scopelookup(c.cur, nm);
|
s = scopelookup(c.cur, nm);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (s == nil) { return nil; };
|
if (s != nil) { if (s.skind == skind.SK_FN) { if (s.decl != nil) {
|
||||||
if (s.skind != skind.SK_FN) { return nil; };
|
|
||||||
if (s.decl == nil) { return nil; };
|
|
||||||
// fn-decl's lhs is the return-type AST node. Mirrors cstage
|
// fn-decl's lhs is the return-type AST node. Mirrors cstage
|
||||||
// cmd/wcc/check.c:984+ regular-CALL `n->type = build_fn_type(c,
|
// cmd/wcc/check.c:984+ regular-CALL `n->type =
|
||||||
// s->decl)->ret` shape.
|
// build_fn_type(c, s->decl)->ret` shape.
|
||||||
e.type_ = tinfofornode(c, s.decl.lhs): *void;
|
e.type_ = tinfofornode(c, s.decl.lhs): *void;
|
||||||
return s.decl.lhs;
|
return s.decl.lhs;
|
||||||
|
}; }; };
|
||||||
|
// A callee that is a fn-VALUE — a fn-pointer struct field
|
||||||
|
// (`w.emit(...)`), local, or param — has no free SK_FN entry, so
|
||||||
|
// the name lookup above misses. Read the result off the checked
|
||||||
|
// callee node's own type instead: autodereference + dealias to
|
||||||
|
// the TY_FN, then take its result. Mirrors harec's check_expr_call
|
||||||
|
// `expr->result = type_dealias(check_autodereference(lvalue->
|
||||||
|
// result))->func.result` (ref/harec/src/check.c:1566-1581). The
|
||||||
|
// name path stays primary because a fn-NAME callee node in wwstage
|
||||||
|
// already carries its RETURN type (fn-decl.lhs), not its fn-type —
|
||||||
|
// so a `fn make() fn() void` callee would otherwise mis-yield void.
|
||||||
|
let ct: *node = resolvealias(c, unwrapbang(exprtype(c, callee, nil)));
|
||||||
|
for (ct != nil && ct.kind == nkind.N_TPTR) {
|
||||||
|
ct = resolvealias(c, unwrapbang(ct.lhs));
|
||||||
|
};
|
||||||
|
if (ct != nil) { if (ct.kind == nkind.N_TFN) {
|
||||||
|
let res: *node = ct.lhs;
|
||||||
|
e.type_ = tinfofornode(c, res): *void;
|
||||||
|
return res;
|
||||||
|
}; };
|
||||||
|
return nil;
|
||||||
};
|
};
|
||||||
if (k == nkind.N_DOT) {
|
if (k == nkind.N_DOT) {
|
||||||
// A.6.1.5a — fold cases only. Mirrors cstage cmd/wcc/check.c
|
// A.6.1.5a — fold cases only. Mirrors cstage cmd/wcc/check.c
|
||||||
|
|||||||
@@ -12327,14 +12327,33 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
|||||||
s = scopelookup(c.cur, nm);
|
s = scopelookup(c.cur, nm);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (s == nil) { return nil; };
|
if (s != nil) { if (s.skind == skind.SK_FN) { if (s.decl != nil) {
|
||||||
if (s.skind != skind.SK_FN) { return nil; };
|
|
||||||
if (s.decl == nil) { return nil; };
|
|
||||||
// fn-decl's lhs is the return-type AST node. Mirrors cstage
|
// fn-decl's lhs is the return-type AST node. Mirrors cstage
|
||||||
// cmd/wcc/check.c:984+ regular-CALL `n->type = build_fn_type(c,
|
// cmd/wcc/check.c:984+ regular-CALL `n->type =
|
||||||
// s->decl)->ret` shape.
|
// build_fn_type(c, s->decl)->ret` shape.
|
||||||
e.type_ = tinfofornode(c, s.decl.lhs): *void;
|
e.type_ = tinfofornode(c, s.decl.lhs): *void;
|
||||||
return s.decl.lhs;
|
return s.decl.lhs;
|
||||||
|
}; }; };
|
||||||
|
// A callee that is a fn-VALUE — a fn-pointer struct field
|
||||||
|
// (`w.emit(...)`), local, or param — has no free SK_FN entry, so
|
||||||
|
// the name lookup above misses. Read the result off the checked
|
||||||
|
// callee node's own type instead: autodereference + dealias to
|
||||||
|
// the TY_FN, then take its result. Mirrors harec's check_expr_call
|
||||||
|
// `expr->result = type_dealias(check_autodereference(lvalue->
|
||||||
|
// result))->func.result` (ref/harec/src/check.c:1566-1581). The
|
||||||
|
// name path stays primary because a fn-NAME callee node in wwstage
|
||||||
|
// already carries its RETURN type (fn-decl.lhs), not its fn-type —
|
||||||
|
// so a `fn make() fn() void` callee would otherwise mis-yield void.
|
||||||
|
let ct: *node = resolvealias(c, unwrapbang(exprtype(c, callee, nil)));
|
||||||
|
for (ct != nil && ct.kind == nkind.N_TPTR) {
|
||||||
|
ct = resolvealias(c, unwrapbang(ct.lhs));
|
||||||
|
};
|
||||||
|
if (ct != nil) { if (ct.kind == nkind.N_TFN) {
|
||||||
|
let res: *node = ct.lhs;
|
||||||
|
e.type_ = tinfofornode(c, res): *void;
|
||||||
|
return res;
|
||||||
|
}; };
|
||||||
|
return nil;
|
||||||
};
|
};
|
||||||
if (k == nkind.N_DOT) {
|
if (k == nkind.N_DOT) {
|
||||||
// A.6.1.5a — fold cases only. Mirrors cstage cmd/wcc/check.c
|
// A.6.1.5a — fold cases only. Mirrors cstage cmd/wcc/check.c
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
*
|
*
|
||||||
* Gap classes (counts verified empirically at this revision):
|
* Gap classes (counts verified empirically at this revision):
|
||||||
* A module-qual N_DOT call result checked_test 0 (closed)
|
* A module-qual N_DOT call result checked_test 0 (closed)
|
||||||
* B fn-ptr struct-field call smoke 3
|
* B fn-ptr struct-field call smoke 0 (closed)
|
||||||
* C abort intrinsic callee utf8 8
|
* C abort intrinsic callee utf8 8
|
||||||
* D module-leaf == type/fn name fnmatch 0 (closed)
|
* D module-leaf == type/fn name fnmatch 0 (closed)
|
||||||
* D module-leaf == type/fn name random 0 (closed)
|
* D module-leaf == type/fn name random 0 (closed)
|
||||||
@@ -109,7 +109,7 @@ main(void)
|
|||||||
{ "lib/math/checked/checked_test.combined.ww",
|
{ "lib/math/checked/checked_test.combined.ww",
|
||||||
"A module-qual N_DOT call result", 0 },
|
"A module-qual N_DOT call result", 0 },
|
||||||
{ "selfhost/test/smoke.combined.ww",
|
{ "selfhost/test/smoke.combined.ww",
|
||||||
"B fn-ptr struct-field call", 3 },
|
"B fn-ptr struct-field call", 0 },
|
||||||
{ "lib/encoding/utf8/utf8.combined.ww",
|
{ "lib/encoding/utf8/utf8.combined.ww",
|
||||||
"C abort intrinsic callee", 8 },
|
"C abort intrinsic callee", 8 },
|
||||||
{ "lib/fnmatch/fnmatchtest.combined.ww",
|
{ "lib/fnmatch/fnmatchtest.combined.ww",
|
||||||
@@ -151,6 +151,6 @@ main(void)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
printf("asserttyped_gap: ww-stage checker warn set matches manifest "
|
printf("asserttyped_gap: ww-stage checker warn set matches manifest "
|
||||||
"on %d gap-corpus fixtures (B+C pinned, A+D closed)\n", n);
|
"on %d gap-corpus fixtures (C pinned, A+B+D closed)\n", n);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user