check: autodereference a single-level *fn callee before the call gate (C6a)
cstage rejected a deref-less function-pointer call `f(21)` (f: *fn...) with "calling non-function" while wwstage accepted it and ran correctly -- a cs!=ww divergence. Hare auto-dereferences a pointer callee to its fn type before the call. Peel one pointer level after the named-type chase so cstage accepts a `*fn` callee, matching wwstage and Hare. One level only, deliberately: the #181-cgen indirect-call path lowers the callee VALUE as the target (CALL AX), which is the fn address for a single `*fn` but only the address-of the fn-ptr for `**fn`. A multi-level peel would accept `**fn` past what cgen can lower -- a silent both-stage miscompile (returns garbage). cstage stays loud on `**fn` (rule 7); wwstage's loop-accept of all levels (check.ww:3763) is the over-permissive side, filed for align-down plus the deeper cgen multi-level autoderef. Surfaced by the codegen miscompile hunt (finding C6a). Pinned by test/lang/fnptr_derefless_call_test.ww (deref-less + canonical (*f)(21), multi-arg, alias-chain, tuple-return; reverting the peel reds the deref-less rows at compile).
This commit is contained in:
@@ -1898,6 +1898,18 @@ cexpr(Checker *c, Node *n)
|
||||
return n->type = ty_err;
|
||||
}
|
||||
Type *u = type_chase_named(ft);
|
||||
/* Autodereference a pointer callee to its fn type before the
|
||||
* gate (harec check_autodereference → type_dereference,
|
||||
* ref/harec/src/check.c:1566, types.c:13). ONE level only: the
|
||||
* #181-cgen path lowers an indirect call by using the callee
|
||||
* VALUE as the target (CALL AX) — the fn address for a single
|
||||
* `*fn`, but only the *address of* the fn-ptr for `**fn`. So a
|
||||
* deref-less `**fn` call drops a `MOVQ (AX),AX` and miscompiles
|
||||
* on BOTH stages (byte-id-blind, returns garbage). Stay loud on
|
||||
* `**fn` (rule 7) rather than match wwstage's loop-accept
|
||||
* (selfhost/cmd/wcc/check.ww:3763, which over-accepts past
|
||||
* #181-cgen); multi-level fn-ptr autoderef is deferred (#181). */
|
||||
if (u && u->kind == TY_PTR) u = type_chase_named(u->sub);
|
||||
if (u == NULL || u->kind != TY_FN)
|
||||
return n->type = err(c, n->pos, "calling non-function %s",
|
||||
type_name(c->a, ft));
|
||||
|
||||
Reference in New Issue
Block a user