ffi M5.A.next.2b.fu2.B: generic pack-fn return — infer ret_ty from body
Fix for follow-up #2 from step 2b. When a pack-fn declares `(..\$args) -> \$R` (return type a generic name), the mono now infers ret_ty from the body's first explicit `return X;` or falls back to the tail expression of an arrow-form body. Plumbing in src/ir/lower.zig: - `inferPackBodyReturnType(body)` walks the body via the existing `findReturnValueType` helper (return stmts) and falls through to `inferExprType` on the tail expression for arrow-form / tail-expr bodies. - `monomorphizePackFn` now pre-installs `pack_arg_nodes` and `pack_param_count` BEFORE resolving the return type so the inference can substitute `args[<lit>]` to call-site arg AST nodes during type lookup. - Generic-ret detection: `fd.return_type` AST node is a `type_expr` with `is_generic = true`. Concrete returns stay on the standard `resolveReturnType` path. `examples/159-pack-generic-ret.sx` flips from `0 0` (silent- zero coercion through opaque struct ret_ty) to `42 99`. 198/198 example tests + `zig build test` green.
This commit is contained in:
@@ -8066,6 +8066,27 @@ pub const Lowering = struct {
|
||||
return self.builder.constInt(0, .void);
|
||||
}
|
||||
|
||||
/// Infer the return type of a pack-fn body for the generic-`$R`
|
||||
/// case. Walks the body looking for the first concrete return
|
||||
/// type: a `return X;` statement's value type, or — failing that —
|
||||
/// the tail expression of an arrow-form body. Caller must have
|
||||
/// `pack_arg_nodes` installed so `args[<lit>]` substitutes during
|
||||
/// inference. Falls back to `.s64` if nothing concrete is found
|
||||
/// (matches the broader "default to .s64" convention elsewhere).
|
||||
fn inferPackBodyReturnType(self: *Lowering, body: *const Node) TypeId {
|
||||
// First try explicit `return X;` — walks past structured
|
||||
// control flow but stops at nested fn / lambda bodies.
|
||||
if (self.findReturnValueType(body)) |ty| return ty;
|
||||
// Arrow-form / tail-expression body: the body IS the value.
|
||||
// For block bodies whose last stmt is an expression, walk down.
|
||||
if (body.data == .block) {
|
||||
const stmts = body.data.block.stmts;
|
||||
if (stmts.len == 0) return .void;
|
||||
return self.inferExprType(stmts[stmts.len - 1]);
|
||||
}
|
||||
return self.inferExprType(body);
|
||||
}
|
||||
|
||||
/// Per-call-shape monomorphisation entry for pack-fns
|
||||
/// (`isPackFn(fd) == true`). Computes a mangled name from the
|
||||
/// call-site arg types, builds the mono if it's not cached, and
|
||||
@@ -8169,13 +8190,12 @@ pub const Lowering = struct {
|
||||
self.builder.inst_counter = saved_counter;
|
||||
}
|
||||
|
||||
const ret_ty = self.resolveReturnType(fd);
|
||||
self.target_type = ret_ty;
|
||||
|
||||
const wants_ctx = self.funcWantsImplicitCtx(fd);
|
||||
|
||||
// Synthesise pack-param names + AST ident nodes used to bind
|
||||
// `args[<lit>]` substitutions during body lowering.
|
||||
// `args[<lit>]` substitutions during body lowering. Built
|
||||
// BEFORE return-type resolution so the generic-`$R` path can
|
||||
// pre-install the binding for type inference.
|
||||
var pack_synth_names = std.ArrayList([]const u8).empty;
|
||||
defer pack_synth_names.deinit(self.alloc);
|
||||
var pack_arg_idents = std.ArrayList(*const Node).empty;
|
||||
@@ -8191,6 +8211,32 @@ pub const Lowering = struct {
|
||||
pack_arg_idents.append(self.alloc, ident_node) catch return;
|
||||
}
|
||||
|
||||
// Resolve return type. When the declared type is a generic
|
||||
// name (e.g. `(..$args) -> $R`), `resolveReturnType` would
|
||||
// return an opaque struct TypeId and the mono's signature
|
||||
// would be wrong. Pre-install the pack bindings + infer the
|
||||
// ret type from the body's tail expression / first explicit
|
||||
// `return X;` instead.
|
||||
var pre_pan = std.StringHashMap([]const *const Node).init(self.alloc);
|
||||
defer pre_pan.deinit();
|
||||
pre_pan.put(pack_name, pack_arg_idents.items) catch return;
|
||||
var pre_ppc = std.StringHashMap(u32).init(self.alloc);
|
||||
defer pre_ppc.deinit();
|
||||
pre_ppc.put(pack_name, @intCast(arg_types.len)) catch return;
|
||||
self.pack_arg_nodes = pre_pan;
|
||||
self.pack_param_count = pre_ppc;
|
||||
|
||||
const declared_is_generic_ret = blk: {
|
||||
const rt = fd.return_type orelse break :blk false;
|
||||
if (rt.data != .type_expr) break :blk false;
|
||||
break :blk rt.data.type_expr.is_generic;
|
||||
};
|
||||
const ret_ty: TypeId = if (declared_is_generic_ret)
|
||||
self.inferPackBodyReturnType(fd.body)
|
||||
else
|
||||
self.resolveReturnType(fd);
|
||||
self.target_type = ret_ty;
|
||||
|
||||
// Param list: ctx (if needed) + fixed prefix + N pack params.
|
||||
// NOT deinit'd — `params.items` is stored by reference in
|
||||
// `Function.init` and read back later via `func.params`.
|
||||
@@ -8248,16 +8294,8 @@ pub const Lowering = struct {
|
||||
param_idx += 1;
|
||||
}
|
||||
|
||||
// Install pack bindings for the body lowering.
|
||||
var pan_map = std.StringHashMap([]const *const Node).init(self.alloc);
|
||||
defer pan_map.deinit();
|
||||
pan_map.put(pack_name, pack_arg_idents.items) catch return;
|
||||
self.pack_arg_nodes = pan_map;
|
||||
|
||||
var ppc_map = std.StringHashMap(u32).init(self.alloc);
|
||||
defer ppc_map.deinit();
|
||||
ppc_map.put(pack_name, @intCast(arg_types.len)) catch return;
|
||||
self.pack_param_count = ppc_map;
|
||||
// Pack bindings remain installed from the pre-resolution
|
||||
// (generic-`$R`) inference step above. No need to reinstall.
|
||||
|
||||
if (ret_ty != .void) {
|
||||
const body_val = self.lowerBlockValue(fd.body);
|
||||
|
||||
@@ -1 +1 @@
|
||||
0 0
|
||||
42 99
|
||||
|
||||
Reference in New Issue
Block a user