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:
@@ -550,16 +550,35 @@ pub const Parser = struct {
|
||||
}
|
||||
|
||||
// Closure type: Closure(params...) -> R
|
||||
// Variadic-pack trailing form: `Closure(Prefix..., ..$pack) -> R`
|
||||
// binds `pack` to a heterogeneous comptime type list at impl
|
||||
// match time (see plan: variadic heterogeneous type packs).
|
||||
if (std.mem.eql(u8, name, "Closure") and self.current.tag == .l_paren) {
|
||||
self.advance(); // skip '('
|
||||
var param_types = std.ArrayList(*Node).empty;
|
||||
var param_names = std.ArrayList(?[]const u8).empty;
|
||||
var has_names = false;
|
||||
var pack_name: ?[]const u8 = null;
|
||||
while (self.current.tag != .r_paren and self.current.tag != .eof) {
|
||||
if (param_types.items.len > 0) {
|
||||
try self.expect(.comma);
|
||||
if (self.current.tag == .r_paren) break; // trailing comma ok
|
||||
}
|
||||
// Trailing pack marker: `..$name` (terminal only).
|
||||
if (self.current.tag == .dot_dot) {
|
||||
self.advance(); // skip '..'
|
||||
if (self.current.tag == .dollar) self.advance(); // optional sigil
|
||||
if (!self.isIdentLike()) {
|
||||
return self.fail("expected pack name after '..' in Closure type");
|
||||
}
|
||||
pack_name = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
// Pack must be the LAST item — only `)` accepted next.
|
||||
if (self.current.tag != .r_paren) {
|
||||
return self.fail("variadic pack must be the last parameter in Closure type");
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Check for optional param name: `name: Type`
|
||||
if (self.current.tag == .identifier and self.peekNext() == .colon) {
|
||||
const pname = self.tokenSlice(self.current);
|
||||
@@ -582,6 +601,7 @@ pub const Parser = struct {
|
||||
.param_types = try param_types.toOwnedSlice(self.allocator),
|
||||
.param_names = if (has_names) try param_names.toOwnedSlice(self.allocator) else null,
|
||||
.return_type = return_type,
|
||||
.pack_name = pack_name,
|
||||
} });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user