ERR/E1.1 (slice 1): error-set type + global tag registry + decl registration
First sema/types step. Implemented in the IR layer (ir/types.zig +
type_bridge.zig + lower.zig), NOT src/sema.zig — lowering doesn't consume
sema; the frontend Type is LSP-only. Mirrors how enums are handled.
- ir/types.zig: new `.error_set` TypeInfo kind (ErrorSetInfo {name, tags:
[]u32}; identity = name, like enum) with a u32 runtime layout (size/align
4, LLVM i32) per the locked error-slot ABI. New TagRegistry on TypeTable
(global tag pool: name -> u32, monotonic, id 0 reserved for "no error").
internTag/getTagName/errorSetType helpers; `.error_set` arms in all 7
exhaustive switches + findByName.
- emit_llvm: toLLVMTypeInfo -> i32. print: writeType -> set name.
- type_bridge: resolveInlineErrorSet (mirrors resolveInlineUnion) +
.error_set_decl arm.
- lower.zig: registerErrorSetDecl (rejects empty `error { }` with a
diagnostic) wired into both top-level decl switches + the block-local one.
- tests: ir/types.test (TagRegistry 0-reserved + identity; errorSetType u32
layout + named display + dedup; sorted storage) and ir/type_bridge.test
(decl -> type + tag interning + re-resolve dedup).
End-to-end: `Foo :: error { A, B }` + main compiles + runs (exit 0) — first
ERR syntax to survive the full pipeline; empty set rejects with a diagnostic.
Inferred bare `!`, error.X value, and == typing deferred to slice 2 / E1.2.
zig build, zig build test, and 254/254 examples green.
This commit is contained in:
@@ -4182,6 +4182,7 @@ pub const LLVMEmitter = struct {
|
|||||||
.f64 => self.cached_f64,
|
.f64 => self.cached_f64,
|
||||||
.void => self.cached_void,
|
.void => self.cached_void,
|
||||||
.bool => self.cached_i1,
|
.bool => self.cached_i1,
|
||||||
|
.error_set => self.cached_i32, // u32 tag id on the error channel
|
||||||
.string => self.getStringStructType(),
|
.string => self.getStringStructType(),
|
||||||
.pointer, .many_pointer, .function => self.cached_ptr,
|
.pointer, .many_pointer, .function => self.cached_ptr,
|
||||||
.closure => self.getClosureStructType(),
|
.closure => self.getClosureStructType(),
|
||||||
|
|||||||
@@ -477,6 +477,9 @@ pub const Lowering = struct {
|
|||||||
.union_decl => {
|
.union_decl => {
|
||||||
_ = type_bridge.resolveAstType(decl, &self.module.types);
|
_ = type_bridge.resolveAstType(decl, &self.module.types);
|
||||||
},
|
},
|
||||||
|
.error_set_decl => {
|
||||||
|
self.registerErrorSetDecl(decl);
|
||||||
|
},
|
||||||
.protocol_decl => {
|
.protocol_decl => {
|
||||||
self.registerProtocolDecl(&decl.data.protocol_decl);
|
self.registerProtocolDecl(&decl.data.protocol_decl);
|
||||||
},
|
},
|
||||||
@@ -710,6 +713,9 @@ pub const Lowering = struct {
|
|||||||
// Register plain union types in the type table
|
// Register plain union types in the type table
|
||||||
_ = type_bridge.resolveAstType(decl, &self.module.types);
|
_ = type_bridge.resolveAstType(decl, &self.module.types);
|
||||||
},
|
},
|
||||||
|
.error_set_decl => {
|
||||||
|
self.registerErrorSetDecl(decl);
|
||||||
|
},
|
||||||
.protocol_decl => {
|
.protocol_decl => {
|
||||||
self.registerProtocolDecl(&decl.data.protocol_decl);
|
self.registerProtocolDecl(&decl.data.protocol_decl);
|
||||||
},
|
},
|
||||||
@@ -1486,6 +1492,7 @@ pub const Lowering = struct {
|
|||||||
.enum_decl, .union_decl => {
|
.enum_decl, .union_decl => {
|
||||||
_ = type_bridge.resolveAstType(node, &self.module.types);
|
_ = type_bridge.resolveAstType(node, &self.module.types);
|
||||||
},
|
},
|
||||||
|
.error_set_decl => self.registerErrorSetDecl(node),
|
||||||
.ufcs_alias => |ua| {
|
.ufcs_alias => |ua| {
|
||||||
self.ufcs_alias_map.put(ua.name, ua.target) catch {};
|
self.ufcs_alias_map.put(ua.name, ua.target) catch {};
|
||||||
},
|
},
|
||||||
@@ -12063,6 +12070,20 @@ pub const Lowering = struct {
|
|||||||
// ── Type registration ───────────────────────────────────────────
|
// ── Type registration ───────────────────────────────────────────
|
||||||
|
|
||||||
/// Register a struct declaration's fields and methods in the IR type table.
|
/// Register a struct declaration's fields and methods in the IR type table.
|
||||||
|
/// Register a `Foo :: error { A, B }` declaration as an error-set type.
|
||||||
|
/// Rejects an empty set here (sema gate) since type_bridge has no
|
||||||
|
/// diagnostics; non-empty sets are interned via type_bridge.
|
||||||
|
fn registerErrorSetDecl(self: *Lowering, node: *const Node) void {
|
||||||
|
const esd = node.data.error_set_decl;
|
||||||
|
if (esd.tag_names.len == 0) {
|
||||||
|
if (self.diagnostics) |diags| {
|
||||||
|
diags.addFmt(.err, node.span, "error set '{s}' must declare at least one tag", .{esd.name});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ = type_bridge.resolveAstType(node, &self.module.types);
|
||||||
|
}
|
||||||
|
|
||||||
fn registerStructDecl(self: *Lowering, sd: *const ast.StructDecl) void {
|
fn registerStructDecl(self: *Lowering, sd: *const ast.StructDecl) void {
|
||||||
const table = &self.module.types;
|
const table = &self.module.types;
|
||||||
const name_id = table.internString(sd.name);
|
const name_id = table.internString(sd.name);
|
||||||
|
|||||||
@@ -438,6 +438,7 @@ fn writeType(id: TypeId, tt: *const TypeTable, writer: Writer) !void {
|
|||||||
.@"union" => |u| try writer.writeAll(tt.getString(u.name)),
|
.@"union" => |u| try writer.writeAll(tt.getString(u.name)),
|
||||||
.tagged_union => |u| try writer.writeAll(tt.getString(u.name)),
|
.tagged_union => |u| try writer.writeAll(tt.getString(u.name)),
|
||||||
.protocol => |p| try writer.writeAll(tt.getString(p.name)),
|
.protocol => |p| try writer.writeAll(tt.getString(p.name)),
|
||||||
|
.error_set => |e| try writer.writeAll(tt.getString(e.name)),
|
||||||
.pointer => |p| {
|
.pointer => |p| {
|
||||||
try writer.writeByte('*');
|
try writer.writeByte('*');
|
||||||
try writeType(p.pointee, tt, writer);
|
try writeType(p.pointee, tt, writer);
|
||||||
|
|||||||
@@ -155,3 +155,27 @@ test "resolveAstType: TypeTable.aliases resolves named alias" {
|
|||||||
const ptr_id = type_bridge.resolveAstType(ptr_node, &table);
|
const ptr_id = type_bridge.resolveAstType(ptr_node, &table);
|
||||||
try std.testing.expectEqual(TypeInfo{ .pointer = .{ .pointee = .u64 } }, table.get(ptr_id));
|
try std.testing.expectEqual(TypeInfo{ .pointer = .{ .pointee = .u64 } }, table.get(ptr_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "resolveAstType: error_set_decl registers an error-set type + interns tags" {
|
||||||
|
const alloc = std.testing.allocator;
|
||||||
|
var table = TypeTable.init(alloc);
|
||||||
|
defer table.deinit();
|
||||||
|
|
||||||
|
const node = try alloc.create(Node);
|
||||||
|
defer alloc.destroy(node);
|
||||||
|
const tag_names = [_][]const u8{ "BadDigit", "Overflow" };
|
||||||
|
node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .error_set_decl = .{
|
||||||
|
.name = "ParseErr",
|
||||||
|
.tag_names = &tag_names,
|
||||||
|
} } };
|
||||||
|
|
||||||
|
const id = type_bridge.resolveAstType(node, &table);
|
||||||
|
const info = table.get(id);
|
||||||
|
try std.testing.expect(info == .error_set);
|
||||||
|
try std.testing.expectEqualStrings("ParseErr", table.getString(info.error_set.name));
|
||||||
|
try std.testing.expectEqual(@as(usize, 2), info.error_set.tags.len);
|
||||||
|
// Tags were interned into the global pool (round-trip a name through it).
|
||||||
|
try std.testing.expectEqualStrings("BadDigit", table.getTagName(table.internTag("BadDigit")));
|
||||||
|
// Re-resolving the same decl dedups to the same TypeId.
|
||||||
|
try std.testing.expectEqual(id, type_bridge.resolveAstType(node, &table));
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ pub fn resolveAstType(node: ?*const Node, table: *TypeTable) TypeId {
|
|||||||
.enum_decl => |ed| resolveInlineEnum(&ed, table),
|
.enum_decl => |ed| resolveInlineEnum(&ed, table),
|
||||||
.struct_decl => |sd| resolveInlineStruct(&sd, table),
|
.struct_decl => |sd| resolveInlineStruct(&sd, table),
|
||||||
.union_decl => |ud| resolveInlineUnion(&ud, table),
|
.union_decl => |ud| resolveInlineUnion(&ud, table),
|
||||||
|
.error_set_decl => |esd| resolveInlineErrorSet(&esd, table),
|
||||||
else => {
|
else => {
|
||||||
// A non-type AST node reached type resolution — a caller bug.
|
// A non-type AST node reached type resolution — a caller bug.
|
||||||
// Returning a plausible `.s64` would silently fabricate an 8-byte
|
// Returning a plausible `.s64` would silently fabricate an 8-byte
|
||||||
@@ -635,3 +636,21 @@ fn resolveInlineUnion(ud: *const ast.UnionDecl, table: *TypeTable) TypeId {
|
|||||||
table.update(id, info);
|
table.update(id, info);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Foo :: error { A, B }` → a registered `.error_set` type. Tag names are
|
||||||
|
/// interned into the global tag pool; the set stores their (sorted) ids. The
|
||||||
|
/// caller (lowering) is responsible for rejecting an empty set, so this only
|
||||||
|
/// sees non-empty declarations.
|
||||||
|
fn resolveInlineErrorSet(esd: *const ast.ErrorSetDecl, table: *TypeTable) TypeId {
|
||||||
|
const alloc = table.alloc;
|
||||||
|
const name_id = table.internString(esd.name);
|
||||||
|
|
||||||
|
if (table.findByName(name_id)) |existing| return existing;
|
||||||
|
|
||||||
|
var tag_ids = std.ArrayList(u32).empty;
|
||||||
|
defer tag_ids.deinit(alloc);
|
||||||
|
for (esd.tag_names) |tn| {
|
||||||
|
tag_ids.append(alloc, table.internTag(tn)) catch unreachable;
|
||||||
|
}
|
||||||
|
return table.errorSetType(name_id, tag_ids.items);
|
||||||
|
}
|
||||||
|
|||||||
@@ -194,3 +194,62 @@ test "pack type: formatTypeName" {
|
|||||||
const empty = table.packType(&.{});
|
const empty = table.packType(&.{});
|
||||||
try std.testing.expectEqualStrings("pack()", table.formatTypeName(arena.allocator(), empty));
|
try std.testing.expectEqualStrings("pack()", table.formatTypeName(arena.allocator(), empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── ERR E1.1 (Slice 1) — error sets + tag registry ──
|
||||||
|
|
||||||
|
test "TagRegistry interns tags, id 0 reserved, global identity" {
|
||||||
|
const alloc = std.testing.allocator;
|
||||||
|
var table = TypeTable.init(alloc);
|
||||||
|
defer table.deinit();
|
||||||
|
|
||||||
|
const bad = table.internTag("BadDigit");
|
||||||
|
const over = table.internTag("Overflow");
|
||||||
|
const bad_again = table.internTag("BadDigit");
|
||||||
|
|
||||||
|
try std.testing.expect(bad >= 1); // id 0 reserved for "no error"
|
||||||
|
try std.testing.expect(bad != over); // distinct names → distinct ids
|
||||||
|
try std.testing.expectEqual(bad, bad_again); // same name → same id (global-flat)
|
||||||
|
try std.testing.expectEqualStrings("BadDigit", table.getTagName(bad));
|
||||||
|
try std.testing.expectEqualStrings("Overflow", table.getTagName(over));
|
||||||
|
try std.testing.expectEqualStrings("", table.getTagName(0)); // reserved slot
|
||||||
|
}
|
||||||
|
|
||||||
|
test "errorSetType: u32 layout, named display, dedup by name" {
|
||||||
|
const alloc = std.testing.allocator;
|
||||||
|
var table = TypeTable.init(alloc);
|
||||||
|
defer table.deinit();
|
||||||
|
|
||||||
|
const name = table.internString("ParseErr");
|
||||||
|
const tags = [_]u32{ table.internTag("BadDigit"), table.internTag("Overflow"), table.internTag("Empty") };
|
||||||
|
const set = table.errorSetType(name, &tags);
|
||||||
|
|
||||||
|
// u32 runtime layout (the error channel's tag value).
|
||||||
|
try std.testing.expectEqual(@as(u32, 4), table.sizeOf(set));
|
||||||
|
try std.testing.expectEqual(@as(usize, 4), table.typeSizeBytes(set));
|
||||||
|
try std.testing.expectEqual(@as(usize, 4), table.typeAlignBytes(set));
|
||||||
|
// Displays by name; resolvable by name.
|
||||||
|
try std.testing.expectEqualStrings("ParseErr", table.typeName(set));
|
||||||
|
try std.testing.expectEqual(set, table.findByName(name).?);
|
||||||
|
// Info shape.
|
||||||
|
const info = table.get(set);
|
||||||
|
try std.testing.expect(info == .error_set);
|
||||||
|
try std.testing.expectEqual(@as(usize, 3), info.error_set.tags.len);
|
||||||
|
// Identity is the name → re-constructing the same set dedups.
|
||||||
|
try std.testing.expectEqual(set, table.errorSetType(name, &tags));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "errorSetType: tags stored sorted by global id" {
|
||||||
|
const alloc = std.testing.allocator;
|
||||||
|
var table = TypeTable.init(alloc);
|
||||||
|
defer table.deinit();
|
||||||
|
|
||||||
|
const name = table.internString("E");
|
||||||
|
const c = table.internTag("C");
|
||||||
|
const a = table.internTag("A");
|
||||||
|
const b = table.internTag("B");
|
||||||
|
// Pass out of order; errorSetType sorts for canonical storage.
|
||||||
|
const set = table.errorSetType(name, &[_]u32{ c, a, b });
|
||||||
|
const stored = table.get(set).error_set.tags;
|
||||||
|
try std.testing.expectEqual(@as(usize, 3), stored.len);
|
||||||
|
try std.testing.expect(stored[0] <= stored[1] and stored[1] <= stored[2]);
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ pub const TypeInfo = union(enum) {
|
|||||||
pack: PackInfo,
|
pack: PackInfo,
|
||||||
any,
|
any,
|
||||||
protocol: ProtocolInfo,
|
protocol: ProtocolInfo,
|
||||||
|
error_set: ErrorSetInfo,
|
||||||
noreturn,
|
noreturn,
|
||||||
usize,
|
usize,
|
||||||
isize,
|
isize,
|
||||||
@@ -191,6 +192,15 @@ pub const TypeInfo = union(enum) {
|
|||||||
sig: TypeId, // function type
|
sig: TypeId, // function type
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A declared error set `Foo :: error { A, B }`. `tags` are GLOBAL tag
|
||||||
|
/// ids from the TypeTable's `TagRegistry` (sorted, canonical). Identity is
|
||||||
|
/// the `name` (like an enum). Runtime layout is u32 — the error channel's
|
||||||
|
/// tag value; id 0 is reserved for "no error".
|
||||||
|
pub const ErrorSetInfo = struct {
|
||||||
|
name: StringId,
|
||||||
|
tags: []const u32, // sorted global tag ids
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── StringId ────────────────────────────────────────────────────────────
|
// ── StringId ────────────────────────────────────────────────────────────
|
||||||
@@ -256,12 +266,61 @@ pub const StringPool = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ── TagRegistry ─────────────────────────────────────────────────────────
|
||||||
|
// Global error-tag pool: tag name → u32 id, monotonic, id 0 reserved for
|
||||||
|
// "no error". Tag identity is the name, program-wide — two declared sets that
|
||||||
|
// list the same tag share its id (the design's global-flat tag identity). A
|
||||||
|
// separate namespace from StringPool so tag ids stay dense (compact id→name
|
||||||
|
// table for `{}` interpolation + traces).
|
||||||
|
|
||||||
|
pub const TagRegistry = struct {
|
||||||
|
/// tag name → id. Keys point to owned allocations in `names`.
|
||||||
|
map: std.StringHashMap(u32),
|
||||||
|
/// id → tag name. Index 0 is the reserved "" (no-error) slot.
|
||||||
|
names: std.ArrayList([]const u8),
|
||||||
|
next_id: u32,
|
||||||
|
|
||||||
|
pub fn init(alloc: Allocator) TagRegistry {
|
||||||
|
var reg = TagRegistry{
|
||||||
|
.map = std.StringHashMap(u32).init(alloc),
|
||||||
|
.names = std.ArrayList([]const u8).empty,
|
||||||
|
.next_id = 1, // 0 reserved for "no error"
|
||||||
|
};
|
||||||
|
reg.names.append(alloc, "") catch unreachable; // slot 0
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *TagRegistry, alloc: Allocator) void {
|
||||||
|
for (self.names.items[1..]) |n| alloc.free(@constCast(n));
|
||||||
|
self.names.deinit(alloc);
|
||||||
|
self.map.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intern(self: *TagRegistry, alloc: Allocator, name: []const u8) u32 {
|
||||||
|
if (self.map.get(name)) |id| return id;
|
||||||
|
const id = self.next_id;
|
||||||
|
self.next_id += 1;
|
||||||
|
const owned = alloc.dupe(u8, name) catch unreachable;
|
||||||
|
self.names.append(alloc, owned) catch unreachable;
|
||||||
|
self.map.put(owned, id) catch unreachable;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getName(self: *const TagRegistry, id: u32) []const u8 {
|
||||||
|
if (id >= self.names.items.len) return "";
|
||||||
|
return self.names.items[id];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// ── TypeTable ───────────────────────────────────────────────────────────
|
// ── TypeTable ───────────────────────────────────────────────────────────
|
||||||
// Holds all resolved types. Builtins in slots 0–15, user types interned from 16+.
|
// Holds all resolved types. Builtins in slots 0–15, user types interned from 16+.
|
||||||
|
|
||||||
pub const TypeTable = struct {
|
pub const TypeTable = struct {
|
||||||
infos: std.ArrayList(TypeInfo),
|
infos: std.ArrayList(TypeInfo),
|
||||||
strings: StringPool,
|
strings: StringPool,
|
||||||
|
/// Global error-tag pool (string → u32 id). Populated as `error { ... }`
|
||||||
|
/// sets are registered; queried when lowering `error.X` value expressions.
|
||||||
|
tags: TagRegistry,
|
||||||
/// Maps TypeInfo → TypeId for dedup of structural types
|
/// Maps TypeInfo → TypeId for dedup of structural types
|
||||||
intern_map: std.HashMap(TypeKey, TypeId, TypeKeyContext, 80),
|
intern_map: std.HashMap(TypeKey, TypeId, TypeKeyContext, 80),
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
@@ -286,6 +345,7 @@ pub const TypeTable = struct {
|
|||||||
var table = TypeTable{
|
var table = TypeTable{
|
||||||
.infos = std.ArrayList(TypeInfo).empty,
|
.infos = std.ArrayList(TypeInfo).empty,
|
||||||
.strings = StringPool.init(alloc),
|
.strings = StringPool.init(alloc),
|
||||||
|
.tags = TagRegistry.init(alloc),
|
||||||
.intern_map = std.HashMap(TypeKey, TypeId, TypeKeyContext, 80).init(alloc),
|
.intern_map = std.HashMap(TypeKey, TypeId, TypeKeyContext, 80).init(alloc),
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.slice_arena = std.heap.ArenaAllocator.init(alloc),
|
.slice_arena = std.heap.ArenaAllocator.init(alloc),
|
||||||
@@ -322,6 +382,7 @@ pub const TypeTable = struct {
|
|||||||
pub fn deinit(self: *TypeTable) void {
|
pub fn deinit(self: *TypeTable) void {
|
||||||
self.infos.deinit(self.alloc);
|
self.infos.deinit(self.alloc);
|
||||||
self.strings.deinit(self.alloc);
|
self.strings.deinit(self.alloc);
|
||||||
|
self.tags.deinit(self.alloc);
|
||||||
self.intern_map.deinit();
|
self.intern_map.deinit();
|
||||||
self.slice_arena.deinit();
|
self.slice_arena.deinit();
|
||||||
}
|
}
|
||||||
@@ -361,6 +422,7 @@ pub const TypeTable = struct {
|
|||||||
.@"union" => |u| u.name,
|
.@"union" => |u| u.name,
|
||||||
.tagged_union => |u| u.name,
|
.tagged_union => |u| u.name,
|
||||||
.@"enum" => |e| e.name,
|
.@"enum" => |e| e.name,
|
||||||
|
.error_set => |e| e.name,
|
||||||
else => null,
|
else => null,
|
||||||
};
|
};
|
||||||
if (n != null and n.? == name) return TypeId.fromIndex(@intCast(i));
|
if (n != null and n.? == name) return TypeId.fromIndex(@intCast(i));
|
||||||
@@ -425,6 +487,24 @@ pub const TypeTable = struct {
|
|||||||
return self.intern(.{ .pack = .{ .elements = owned } });
|
return self.intern(.{ .pack = .{ .elements = owned } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Intern an error-tag name into the global tag pool, returning its id.
|
||||||
|
pub fn internTag(self: *TypeTable, name: []const u8) u32 {
|
||||||
|
return self.tags.intern(self.alloc, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Look up a tag name from its global id.
|
||||||
|
pub fn getTagName(self: *const TypeTable, id: u32) []const u8 {
|
||||||
|
return self.tags.getName(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct (and intern) a named error-set type. `tag_ids` are global tag
|
||||||
|
/// ids (from `internTag`); they are sorted here for canonical storage.
|
||||||
|
pub fn errorSetType(self: *TypeTable, name: StringId, tag_ids: []const u32) TypeId {
|
||||||
|
const owned = self.slice_arena.allocator().dupe(u32, tag_ids) catch unreachable;
|
||||||
|
std.mem.sort(u32, owned, {}, std.sort.asc(u32));
|
||||||
|
return self.intern(.{ .error_set = .{ .name = name, .tags = owned } });
|
||||||
|
}
|
||||||
|
|
||||||
/// Size in bytes for a type (pointer-sized = 8 on 64-bit).
|
/// Size in bytes for a type (pointer-sized = 8 on 64-bit).
|
||||||
pub fn sizeOf(self: *const TypeTable, id: TypeId) u32 {
|
pub fn sizeOf(self: *const TypeTable, id: TypeId) u32 {
|
||||||
const info = self.get(id);
|
const info = self.get(id);
|
||||||
@@ -485,6 +565,7 @@ pub const TypeTable = struct {
|
|||||||
return if (total == 0) 8 else total;
|
return if (total == 0) 8 else total;
|
||||||
},
|
},
|
||||||
.protocol => 16, // {ctx, vtable}
|
.protocol => 16, // {ctx, vtable}
|
||||||
|
.error_set => 4, // u32 tag id on the error channel
|
||||||
.usize, .isize => 8, // pointer-sized (this path is not target-aware; see typeSizeBytes)
|
.usize, .isize => 8, // pointer-sized (this path is not target-aware; see typeSizeBytes)
|
||||||
// Comptime-only: a pack must be expanded to flat positional args
|
// Comptime-only: a pack must be expanded to flat positional args
|
||||||
// before codegen. Reaching runtime layout means a pack leaked.
|
// before codegen. Reaching runtime layout means a pack leaked.
|
||||||
@@ -593,6 +674,7 @@ pub const TypeTable = struct {
|
|||||||
},
|
},
|
||||||
.any => 2 * ptr_size, // {type_tag, data_ptr}
|
.any => 2 * ptr_size, // {type_tag, data_ptr}
|
||||||
.protocol => 2 * ptr_size, // {ctx, vtable}
|
.protocol => 2 * ptr_size, // {ctx, vtable}
|
||||||
|
.error_set => 4, // u32 tag id
|
||||||
.@"enum" => |e| {
|
.@"enum" => |e| {
|
||||||
if (e.backing_type) |bt| return self.typeSizeBytes(bt);
|
if (e.backing_type) |bt| return self.typeSizeBytes(bt);
|
||||||
return 8;
|
return 8;
|
||||||
@@ -638,6 +720,7 @@ pub const TypeTable = struct {
|
|||||||
break :blk max_a;
|
break :blk max_a;
|
||||||
},
|
},
|
||||||
.@"union", .tagged_union => 8,
|
.@"union", .tagged_union => 8,
|
||||||
|
.error_set => 4, // u32 tag id
|
||||||
.@"enum" => |e| {
|
.@"enum" => |e| {
|
||||||
if (e.backing_type) |bt| return self.typeAlignBytes(bt);
|
if (e.backing_type) |bt| return self.typeAlignBytes(bt);
|
||||||
return 8;
|
return 8;
|
||||||
@@ -699,6 +782,7 @@ pub const TypeTable = struct {
|
|||||||
.@"union" => |u| self.getString(u.name),
|
.@"union" => |u| self.getString(u.name),
|
||||||
.tagged_union => |u| self.getString(u.name),
|
.tagged_union => |u| self.getString(u.name),
|
||||||
.protocol => |p| self.getString(p.name),
|
.protocol => |p| self.getString(p.name),
|
||||||
|
.error_set => |e| self.getString(e.name),
|
||||||
else => "?",
|
else => "?",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -719,6 +803,7 @@ pub const TypeTable = struct {
|
|||||||
.@"union" => |u| self.getString(u.name),
|
.@"union" => |u| self.getString(u.name),
|
||||||
.tagged_union => |u| self.getString(u.name),
|
.tagged_union => |u| self.getString(u.name),
|
||||||
.protocol => |p| self.getString(p.name),
|
.protocol => |p| self.getString(p.name),
|
||||||
|
.error_set => |e| self.getString(e.name),
|
||||||
.pointer => |p| blk: {
|
.pointer => |p| blk: {
|
||||||
const inner = self.formatTypeName(alloc, p.pointee);
|
const inner = self.formatTypeName(alloc, p.pointee);
|
||||||
break :blk std.fmt.allocPrint(alloc, "*{s}", .{inner}) catch "*?";
|
break :blk std.fmt.allocPrint(alloc, "*{s}", .{inner}) catch "*?";
|
||||||
@@ -863,6 +948,7 @@ fn hashTypeInfo(h: *std.hash.Wyhash, info: TypeInfo) void {
|
|||||||
.@"union" => |u| h.update(std.mem.asBytes(&u.name)),
|
.@"union" => |u| h.update(std.mem.asBytes(&u.name)),
|
||||||
.tagged_union => |u| h.update(std.mem.asBytes(&u.name)),
|
.tagged_union => |u| h.update(std.mem.asBytes(&u.name)),
|
||||||
.protocol => |p| h.update(std.mem.asBytes(&p.name)),
|
.protocol => |p| h.update(std.mem.asBytes(&p.name)),
|
||||||
|
.error_set => |e| h.update(std.mem.asBytes(&e.name)),
|
||||||
.tuple => |t| {
|
.tuple => |t| {
|
||||||
for (t.fields) |f| h.update(std.mem.asBytes(&f));
|
for (t.fields) |f| h.update(std.mem.asBytes(&f));
|
||||||
if (t.names) |ns| for (ns) |n| h.update(std.mem.asBytes(&n));
|
if (t.names) |ns| for (ns) |n| h.update(std.mem.asBytes(&n));
|
||||||
@@ -915,6 +1001,7 @@ fn typeInfoEql(a: TypeInfo, b: TypeInfo) bool {
|
|||||||
.@"union" => |u| u.name == b.@"union".name,
|
.@"union" => |u| u.name == b.@"union".name,
|
||||||
.tagged_union => |u| u.name == b.tagged_union.name,
|
.tagged_union => |u| u.name == b.tagged_union.name,
|
||||||
.protocol => |p| p.name == b.protocol.name,
|
.protocol => |p| p.name == b.protocol.name,
|
||||||
|
.error_set => |e| e.name == b.error_set.name,
|
||||||
.tuple => |t| {
|
.tuple => |t| {
|
||||||
const u = b.tuple;
|
const u = b.tuple;
|
||||||
if (t.fields.len != u.fields.len) return false;
|
if (t.fields.len != u.fields.len) return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user