lang 2.1: Pack as a type-system value

Add a `pack` variant to IR `TypeInfo` — an ordered, interned sequence of
per-position element types (`PackInfo { elements: []const TypeId }`) — with
constructor (`packType`), structural equality + hashing, and a `pack(T0, …)`
printer. A pack is comptime-only: it lowers to flat positional args before
codegen and has no runtime layout, so `sizeOf` and `toLLVMType` bail loudly
rather than inventing a size. 5 unit tests (N=0/1/3, dedup, order/arity
distinctness, distinct-from-tuple, printer).

Also: give TypeTable an arena for the slices its constructors dupe (freed at
deinit), and add the missing `usize`/`isize` arms to `sizeOf` (a latent
non-exhaustive switch) so types.test.zig compiles and runs leak-free.
This commit is contained in:
agra
2026-05-29 15:24:46 +03:00
parent 98526ab9b4
commit 92638ae9b5
3 changed files with 132 additions and 4 deletions

View File

@@ -119,3 +119,78 @@ test "typeName for builtins" {
try std.testing.expectEqualStrings("void", table.typeName(.void));
try std.testing.expectEqualStrings("Any", table.typeName(.any));
}
// ── Pack type (Feature 1, Step 2.1) ──────────────────────────────────
test "pack type: construct, element access, intern dedup (N=3)" {
const alloc = std.testing.allocator;
var table = TypeTable.init(alloc);
defer table.deinit();
const elems = &[_]TypeId{ .bool, .s32, .string };
const p1 = table.packType(elems);
const p2 = table.packType(elems);
try std.testing.expectEqual(p1, p2); // structural dedup
const info = table.get(p1);
try std.testing.expect(info == .pack);
try std.testing.expectEqual(@as(usize, 3), info.pack.elements.len);
try std.testing.expectEqual(TypeId.bool, info.pack.elements[0]);
try std.testing.expectEqual(TypeId.s32, info.pack.elements[1]);
try std.testing.expectEqual(TypeId.string, info.pack.elements[2]);
}
test "pack type: empty pack (N=0)" {
const alloc = std.testing.allocator;
var table = TypeTable.init(alloc);
defer table.deinit();
const empty1 = table.packType(&.{});
const empty2 = table.packType(&.{});
try std.testing.expectEqual(empty1, empty2);
const info = table.get(empty1);
try std.testing.expect(info == .pack);
try std.testing.expectEqual(@as(usize, 0), info.pack.elements.len);
}
test "pack type: single element (N=1)" {
const alloc = std.testing.allocator;
var table = TypeTable.init(alloc);
defer table.deinit();
const p = table.packType(&[_]TypeId{.f64});
const info = table.get(p);
try std.testing.expectEqual(@as(usize, 1), info.pack.elements.len);
try std.testing.expectEqual(TypeId.f64, info.pack.elements[0]);
}
test "pack type: distinct element lists are distinct types" {
const alloc = std.testing.allocator;
var table = TypeTable.init(alloc);
defer table.deinit();
const a = table.packType(&[_]TypeId{ .bool, .s32 });
const b = table.packType(&[_]TypeId{ .s32, .bool }); // order matters
const c = table.packType(&[_]TypeId{.bool}); // arity matters
try std.testing.expect(a != b);
try std.testing.expect(a != c);
try std.testing.expect(b != c);
// A pack is distinct from the tuple of the same elements.
const tup = table.intern(.{ .tuple = .{ .fields = &[_]TypeId{ .bool, .s32 }, .names = null } });
try std.testing.expect(a != tup);
}
test "pack type: formatTypeName" {
const alloc = std.testing.allocator;
var table = TypeTable.init(alloc);
defer table.deinit();
var arena = std.heap.ArenaAllocator.init(alloc);
defer arena.deinit();
const p = table.packType(&[_]TypeId{ .bool, .s32, .string });
try std.testing.expectEqualStrings("pack(bool, s32, string)", table.formatTypeName(arena.allocator(), p));
const empty = table.packType(&.{});
try std.testing.expectEqualStrings("pack()", table.formatTypeName(arena.allocator(), empty));
}