lang: opt-in UFCS — ufcs-marked fns + alias dot-dispatch, generic binding via receiver; one binding builder for plan-side generic returns
This commit is contained in:
@@ -315,6 +315,36 @@ pub const CallResolver = struct {
|
||||
// the plan carries `prepends_receiver`, distinct from a true
|
||||
// namespace call (`pkg.fn()`), which must NOT prepend.
|
||||
if (self.objectIsValue(cfa.object)) {
|
||||
// Free-fn dot-dispatch is OPT-IN (mirror lowerCall's gate so
|
||||
// plan and dispatch agree): only a `ufcs` alias or a fn
|
||||
// declared `name :: ufcs (...)` classifies as free_fn_ufcs.
|
||||
// A plain fn falls through (lowering emits the tailored
|
||||
// not-a-ufcs-function diagnostic).
|
||||
const alias_target = self.l.program_index.ufcs_alias_map.get(cfa.field);
|
||||
const eff_field = alias_target orelse cfa.field;
|
||||
const ufcs_fd = self.l.program_index.fn_ast_map.get(eff_field);
|
||||
const opted_in = alias_target != null or (ufcs_fd != null and ufcs_fd.?.is_ufcs);
|
||||
if (!opted_in) return .{ .kind = .unresolved, .return_type = .unresolved };
|
||||
// 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) {
|
||||
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);
|
||||
var c2 = c.*;
|
||||
c2.args = eff_call_args;
|
||||
return .{
|
||||
.kind = .free_fn_ufcs,
|
||||
.return_type = self.l.genericResolver().inferGenericReturnType(fd, &c2),
|
||||
.target = .{ .named = eff_field },
|
||||
.prepends_receiver = true,
|
||||
.expands_defaults = defaultsFor(fd, c.args.len + 1),
|
||||
};
|
||||
}
|
||||
}
|
||||
// Value-receiver free-fn UFCS (`recv.fn(args)` → `fn(recv, args)`)
|
||||
// routes through the SAME author producer `selectedFreeAuthor` as a
|
||||
// bare call, so the planned target / return type IS the author
|
||||
@@ -335,7 +365,7 @@ pub const CallResolver = struct {
|
||||
},
|
||||
.ambiguous, .none => {},
|
||||
}
|
||||
if (self.l.resolveFuncByName(cfa.field)) |fid| {
|
||||
if (self.l.resolveFuncByName(eff_field)) |fid| {
|
||||
const func = &self.l.module.functions.items[@intFromEnum(fid)];
|
||||
return .{
|
||||
.kind = .free_fn_ufcs,
|
||||
@@ -343,14 +373,14 @@ pub const CallResolver = struct {
|
||||
.target = .{ .func = fid },
|
||||
.prepends_receiver = true,
|
||||
.prepends_ctx = func.has_implicit_ctx,
|
||||
.expands_defaults = if (self.l.program_index.fn_ast_map.get(cfa.field)) |fd| defaultsFor(fd, c.args.len + 1) else false,
|
||||
.expands_defaults = if (ufcs_fd) |fd| defaultsFor(fd, c.args.len + 1) else false,
|
||||
};
|
||||
}
|
||||
if (self.l.program_index.fn_ast_map.get(cfa.field)) |bfd| {
|
||||
if (ufcs_fd) |bfd| {
|
||||
return .{
|
||||
.kind = .free_fn_ufcs,
|
||||
.return_type = if (bfd.return_type) |rt| self.l.resolveType(rt) else .void,
|
||||
.target = .{ .named = cfa.field },
|
||||
.target = .{ .named = eff_field },
|
||||
.prepends_receiver = true,
|
||||
.expands_defaults = defaultsFor(bfd, c.args.len + 1),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user