ffi M5.A.next.1c.B: pack type rep — Closure(..$args) parses + interns

`parseTypeExpr`'s `Closure(...)` arm now accepts a trailing
`..$name` (sigil optional) as a variadic-pack marker. Pack must
be terminal — `)` is the only token accepted after the name.
`ClosureTypeExpr` AST gains `pack_name: ?[]const u8` carrying the
identifier so later slices can name the binding.

`FunctionInfo` / `ClosureInfo` in src/ir/types.zig grow a
`pack_start: ?u32 = null` field. `Closure(..$args) -> R` interns
as `params = []`, `pack_start = Some(0)` — distinct from any
concrete `Closure(...) -> R` shape thanks to updated hash/eql
arms. New constructor pair `closureTypePack` /
`functionTypePack` keeps the existing single-shape constructors
unchanged.

`type_bridge.resolveClosureType` calls `closureTypePack` when
`pack_name != null`. The pack starts after the fixed prefix,
so `Closure(Prefix, ..$args)` resolves with `params = [Prefix]`,
`pack_start = Some(1)`.

No semantic effect yet — the signature exists in the type table
but no matching code reads `pack_start`. Step 1d wires impl
matching: `Closure(..$args) -> $R` binds against any concrete
closure source type in `tryUserConversion` / `registerParamImpl`.

`examples/154-pack-type-rep.sx` flips from rejecting-with-error
to positive parse smoke (prints "pack type rep ok").

192/192 example tests + `zig build test` green.
This commit is contained in:
agra
2026-05-27 12:12:16 +03:00
parent bb6eca6b91
commit 65824494a7
6 changed files with 58 additions and 2 deletions

View File

@@ -136,6 +136,11 @@ pub const TypeInfo = union(enum) {
params: []const TypeId,
ret: TypeId,
call_conv: CallConv = .default,
/// Pack-variadic shape marker. When set, the signature represents a
/// heterogeneous type pack: `params` is the fixed prefix, and a
/// per-call-site type list binds the remainder. `pack_start == 0`
/// with `params.len == 0` denotes `fn(..$args)`.
pack_start: ?u32 = null,
};
pub const CallConv = enum { default, c };
@@ -143,6 +148,9 @@ pub const TypeInfo = union(enum) {
pub const ClosureInfo = struct {
params: []const TypeId,
ret: TypeId,
/// Pack-variadic shape marker — same semantics as FunctionInfo.
/// `Closure(..$args) -> $R` => params = [], pack_start = 0.
pack_start: ?u32 = null,
};
pub const OptionalInfo = struct {
@@ -354,11 +362,21 @@ pub const TypeTable = struct {
return self.intern(.{ .function = .{ .params = owned_params, .ret = ret, .call_conv = cc } });
}
pub fn functionTypePack(self: *TypeTable, params: []const TypeId, ret: TypeId, cc: TypeInfo.CallConv, pack_start: u32) TypeId {
const owned_params = self.alloc.dupe(TypeId, params) catch unreachable;
return self.intern(.{ .function = .{ .params = owned_params, .ret = ret, .call_conv = cc, .pack_start = pack_start } });
}
pub fn closureType(self: *TypeTable, params: []const TypeId, ret: TypeId) TypeId {
const owned_params = self.alloc.dupe(TypeId, params) catch unreachable;
return self.intern(.{ .closure = .{ .params = owned_params, .ret = ret } });
}
pub fn closureTypePack(self: *TypeTable, params: []const TypeId, ret: TypeId, pack_start: u32) TypeId {
const owned_params = self.alloc.dupe(TypeId, params) catch unreachable;
return self.intern(.{ .closure = .{ .params = owned_params, .ret = ret, .pack_start = pack_start } });
}
pub fn vectorOf(self: *TypeTable, element: TypeId, length: u32) TypeId {
return self.intern(.{ .vector = .{ .element = element, .length = length } });
}
@@ -679,10 +697,16 @@ fn hashTypeInfo(h: *std.hash.Wyhash, info: TypeInfo) void {
h.update(std.mem.asBytes(&f.ret));
const cc_byte: u8 = @intFromEnum(f.call_conv);
h.update(&.{cc_byte});
const pack_present: u8 = if (f.pack_start != null) 1 else 0;
h.update(&.{pack_present});
if (f.pack_start) |ps| h.update(std.mem.asBytes(&ps));
},
.closure => |c| {
for (c.params) |p| h.update(std.mem.asBytes(&p));
h.update(std.mem.asBytes(&c.ret));
const pack_present: u8 = if (c.pack_start != null) 1 else 0;
h.update(&.{pack_present});
if (c.pack_start) |ps| h.update(std.mem.asBytes(&ps));
},
.@"struct" => |s| h.update(std.mem.asBytes(&s.name)),
.@"enum" => |e| h.update(std.mem.asBytes(&e.name)),
@@ -719,6 +743,8 @@ fn typeInfoEql(a: TypeInfo, b: TypeInfo) bool {
if (fp != gp) return false;
}
if (f.call_conv != g.call_conv) return false;
if ((f.pack_start == null) != (g.pack_start == null)) return false;
if (f.pack_start) |fp| if (fp != g.pack_start.?) return false;
return f.ret == g.ret;
},
.closure => |c| {
@@ -727,6 +753,8 @@ fn typeInfoEql(a: TypeInfo, b: TypeInfo) bool {
for (c.params, d.params) |cp, dp| {
if (cp != dp) return false;
}
if ((c.pack_start == null) != (d.pack_start == null)) return false;
if (c.pack_start) |cp| if (cp != d.pack_start.?) return false;
return c.ret == d.ret;
},
.@"struct" => |s| s.name == b.@"struct".name,