fix: resolve closure/fn-pointer struct-field call types (issue 0201)

Calling a closure or function-pointer value stored in a struct data field
(`box.run(args)`) typed the call as 'unresolved': value returns marshalled
as garbage, failable fields could not be try/catch-ed. Lowering already
dispatched these (call_closure / call_indirect); only CallResolver.plan
lacked a field-access arm. Add a closure/fn-pointer field arm to plan
(before the instance-method check, mirroring lowering's precedence — a
closure-typed field shadows a same-named method) and extend the lowering
closure-field arm to also handle bare .function fields via call_indirect.

Lock: examples/closures/0315-closures-struct-field-call.sx.
This commit is contained in:
agra
2026-06-28 09:18:30 +03:00
parent 69a6ecfb57
commit 45bd561a0d
7 changed files with 201 additions and 0 deletions

View File

@@ -990,6 +990,28 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
} else self.alloc.dupe(Ref, args.items) catch unreachable;
return self.builder.emit(.{ .call_closure = .{ .callee = closure_val, .args = owned } }, fti.closure.ret);
}
// Bare function-pointer field (`fp: (T) -> R`, no env) —
// load the field value and call it via `call_indirect`,
// mirroring the bare-identifier / global fn-pointer paths
// (ctx prepend gated on the fn-ptr's own ABI).
if (fti == .function) {
var agg = obj;
const oi = self.module.types.get(obj_ty);
if (oi == .pointer) {
agg = self.builder.load(obj, oi.pointer.pointee);
}
const fp_val = self.builder.structGet(agg, @intCast(fi), f.ty);
// Coerce user args to the fn-ptr's param types (issue 0186).
coerceClosureCallArgs(self, args.items, fti.function.params);
var final_args = std.ArrayList(Ref).empty;
defer final_args.deinit(self.alloc);
if (self.fnPtrTypeWantsCtx(f.ty)) {
final_args.append(self.alloc, self.current_ctx_ref) catch unreachable;
}
final_args.appendSlice(self.alloc, args.items) catch unreachable;
const owned = self.alloc.dupe(Ref, final_args.items) catch unreachable;
return self.builder.emit(.{ .call_indirect = .{ .callee = fp_val, .args = owned } }, fti.function.ret);
}
}
}
}