fibers: address adversarial review of the B1 changes (6 findings)
UFCS generic overload resolution (issue 0157 follow-ups): - P1-a: call planning (calls.zig) used the last-wins fn_ast_map winner while lowering reselected by receiver, so the planned result type could disagree with the dispatched function and misbox the result. Both now share selectUfcsGenericByReceiver(.., fd0). - P1-b: selection scanned module_decls globally, flagging a transitively-hidden same-named overload as a false ambiguity. Now two-tier: directly-visible authors first (ambiguity only among those), global fallback for receiver-reachable namespaced methods (e.g. Task.cancel) that defers to fd0 on a hidden tie. - P2-b: boolean specificity tied *$T with *Box($T). Now peels pointer layers so the structurally-narrower receiver wins. Scheduler (sched.sx): - P1-c: a second concurrent Task.wait overwrote the single waiter slot -> silent deadlock. Now one-awaiter-per-task loud abort. - P2-c: sleep(negative) rewound the monotonic virtual clock. Rejected loudly. (P2-a, non-generic-winner-hides-generic, did not reproduce -- the non-generic arm already falls through.) Regressions: examples/generics/0218 (receiver specificity + plan/lowering agreement), examples/concurrency/1818 (negative-sleep abort), 1819 (double-wait abort). Suite green 758/0.
This commit is contained in:
@@ -316,12 +316,27 @@ pub const CallResolver = struct {
|
||||
// Generic ufcs target: infer the return type with the
|
||||
// RECEIVER prepended so binding positions align with
|
||||
// fd.params[0] (mirrors the lowering side's eff_args).
|
||||
if (ufcs_fd) |fd| {
|
||||
if (fd.type_params.len > 0) {
|
||||
if (ufcs_fd) |fd0p| {
|
||||
if (fd0p.type_params.len > 0) {
|
||||
const eff_call_args = self.l.alloc.alloc(*ast.Node, c.args.len + 1) catch
|
||||
return .{ .kind = .unresolved, .return_type = .unresolved };
|
||||
eff_call_args[0] = cfa.object;
|
||||
@memcpy(eff_call_args[1..], c.args);
|
||||
// RESELECT by receiver — the same selector + `fd0`
|
||||
// lowering uses — so the PLANNED return type matches the
|
||||
// function lowering actually dispatches. The last-wins
|
||||
// `fd0p` can be a wrong-receiver overload (e.g. a
|
||||
// `*Other($T)->string` winner over the `*Box($T)->i64`
|
||||
// receiver-match); typing the call by `fd0p` while
|
||||
// lowering calls the other one misboxes the result
|
||||
// (issue 0157 review P1). A `*const Node` view of the
|
||||
// args drives the receiver-aware selection.
|
||||
const sel_args = self.l.alloc.alloc(*const ast.Node, c.args.len + 1) catch
|
||||
return .{ .kind = .unresolved, .return_type = .unresolved };
|
||||
sel_args[0] = cfa.object;
|
||||
for (c.args, 0..) |a, i| sel_args[i + 1] = a;
|
||||
var amb = false;
|
||||
const fd = self.l.selectUfcsGenericByReceiver(eff_field, sel_args, &amb, fd0p) orelse fd0p;
|
||||
var c2 = c.*;
|
||||
c2.args = eff_call_args;
|
||||
return .{
|
||||
|
||||
Reference in New Issue
Block a user