aboutsummaryrefslogtreecommitdiff
path: root/src/InternPool.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-05-20 12:09:07 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-06-10 20:47:53 -0700
commit9ff514b6a35b7201f45f8bff31c61b4f8cfa7a7a (patch)
treedda74acc00690d1b3d31fd6e43d7ec0aa4acc882 /src/InternPool.zig
parent7bf91fc79ac9e4eae575baf3a2ca9549bc3bf6c2 (diff)
downloadzig-9ff514b6a35b7201f45f8bff31c61b4f8cfa7a7a.tar.gz
zig-9ff514b6a35b7201f45f8bff31c61b4f8cfa7a7a.zip
compiler: move error union types and error set types to InternPool
One change worth noting in this commit is that `module.global_error_set` is no longer kept strictly up-to-date. The previous code reserved integer error values when dealing with error set types, but this is no longer needed because the integer values are not needed for semantic analysis unless `@errorToInt` or `@intToError` are used and therefore may be assigned lazily.
Diffstat (limited to 'src/InternPool.zig')
-rw-r--r--src/InternPool.zig176
1 files changed, 160 insertions, 16 deletions
diff --git a/src/InternPool.zig b/src/InternPool.zig
index 81035bffc5..79506c4404 100644
--- a/src/InternPool.zig
+++ b/src/InternPool.zig
@@ -34,6 +34,14 @@ allocated_unions: std.SegmentedList(Module.Union, 0) = .{},
/// When a Union object is freed from `allocated_unions`, it is pushed into this stack.
unions_free_list: std.ArrayListUnmanaged(Module.Union.Index) = .{},
+/// InferredErrorSet objects are stored in this data structure because:
+/// * They contain pointers such as the errors map and the set of other inferred error sets.
+/// * They need to be mutated after creation.
+allocated_inferred_error_sets: std.SegmentedList(Module.Fn.InferredErrorSet, 0) = .{},
+/// When a Struct object is freed from `allocated_inferred_error_sets`, it is
+/// pushed into this stack.
+inferred_error_sets_free_list: std.ArrayListUnmanaged(Module.Fn.InferredErrorSet.Index) = .{},
+
/// Some types such as enums, structs, and unions need to store mappings from field names
/// to field index, or value to field index. In such cases, they will store the underlying
/// field names and values directly, relying on one of these maps, stored separately,
@@ -113,6 +121,12 @@ pub const NullTerminatedString = enum(u32) {
return std.hash.uint32(@enumToInt(a));
}
};
+
+ /// Compare based on integer value alone, ignoring the string contents.
+ pub fn indexLessThan(ctx: void, a: NullTerminatedString, b: NullTerminatedString) bool {
+ _ = ctx;
+ return @enumToInt(a) < @enumToInt(b);
+ }
};
/// An index into `string_bytes` which might be `none`.
@@ -135,10 +149,7 @@ pub const Key = union(enum) {
/// `anyframe->T`. The payload is the child type, which may be `none` to indicate
/// `anyframe`.
anyframe_type: Index,
- error_union_type: struct {
- error_set_type: Index,
- payload_type: Index,
- },
+ error_union_type: ErrorUnionType,
simple_type: SimpleType,
/// This represents a struct that has been explicitly declared in source code,
/// or was created with `@Type`. It is unique and based on a declaration.
@@ -152,6 +163,8 @@ pub const Key = union(enum) {
opaque_type: OpaqueType,
enum_type: EnumType,
func_type: FuncType,
+ error_set_type: ErrorSetType,
+ inferred_error_set_type: Module.Fn.InferredErrorSet.Index,
/// Typed `undefined`. This will never be `none`; untyped `undefined` is represented
/// via `simple_value` and has a named `Index` tag for it.
@@ -183,6 +196,26 @@ pub const Key = union(enum) {
pub const IntType = std.builtin.Type.Int;
+ pub const ErrorUnionType = struct {
+ error_set_type: Index,
+ payload_type: Index,
+ };
+
+ pub const ErrorSetType = struct {
+ /// Set of error names, sorted by null terminated string index.
+ names: []const NullTerminatedString,
+ /// This is ignored by `get` but will always be provided by `indexToKey`.
+ names_map: OptionalMapIndex = .none,
+
+ /// Look up field index based on field name.
+ pub fn nameIndex(self: ErrorSetType, ip: *const InternPool, name: NullTerminatedString) ?u32 {
+ const map = &ip.maps.items[@enumToInt(self.names_map.unwrap().?)];
+ const adapter: NullTerminatedString.Adapter = .{ .strings = self.names };
+ const field_index = map.getIndexAdapted(name, adapter) orelse return null;
+ return @intCast(u32, field_index);
+ }
+ };
+
pub const PtrType = struct {
elem_type: Index,
sentinel: Index = .none,
@@ -507,6 +540,7 @@ pub const Key = union(enum) {
.un,
.undef,
.enum_tag,
+ .inferred_error_set_type,
=> |info| std.hash.autoHash(hasher, info),
.opaque_type => |opaque_type| std.hash.autoHash(hasher, opaque_type.decl),
@@ -535,7 +569,7 @@ pub const Key = union(enum) {
.ptr => |ptr| {
std.hash.autoHash(hasher, ptr.ty);
// Int-to-ptr pointers are hashed separately than decl-referencing pointers.
- // This is sound due to pointer province rules.
+ // This is sound due to pointer provenance rules.
switch (ptr.addr) {
.int => |int| std.hash.autoHash(hasher, int),
.decl => @panic("TODO"),
@@ -547,6 +581,10 @@ pub const Key = union(enum) {
for (aggregate.fields) |field| std.hash.autoHash(hasher, field);
},
+ .error_set_type => |error_set_type| {
+ for (error_set_type.names) |elem| std.hash.autoHash(hasher, elem);
+ },
+
.anon_struct_type => |anon_struct_type| {
for (anon_struct_type.types) |elem| std.hash.autoHash(hasher, elem);
for (anon_struct_type.values) |elem| std.hash.autoHash(hasher, elem);
@@ -726,6 +764,14 @@ pub const Key = union(enum) {
std.mem.eql(Index, a_info.values, b_info.values) and
std.mem.eql(NullTerminatedString, a_info.names, b_info.names);
},
+ .error_set_type => |a_info| {
+ const b_info = b.error_set_type;
+ return std.mem.eql(NullTerminatedString, a_info.names, b_info.names);
+ },
+ .inferred_error_set_type => |a_info| {
+ const b_info = b.inferred_error_set_type;
+ return a_info == b_info;
+ },
.func_type => |a_info| {
const b_info = b.func_type;
@@ -752,6 +798,8 @@ pub const Key = union(enum) {
.opt_type,
.anyframe_type,
.error_union_type,
+ .error_set_type,
+ .inferred_error_set_type,
.simple_type,
.struct_type,
.union_type,
@@ -1207,8 +1255,14 @@ pub const Tag = enum(u8) {
/// If the child type is `none`, the type is `anyframe`.
type_anyframe,
/// An error union type.
- /// data is payload to ErrorUnion.
+ /// data is payload to `Key.ErrorUnionType`.
type_error_union,
+ /// An error set type.
+ /// data is payload to `ErrorSet`.
+ type_error_set,
+ /// The inferred error set type of a function.
+ /// data is `Module.Fn.InferredErrorSet.Index`.
+ type_inferred_error_set,
/// An enum type with auto-numbered tag values.
/// The enum is exhaustive.
/// data is payload index to `EnumAuto`.
@@ -1356,6 +1410,12 @@ pub const Tag = enum(u8) {
};
/// Trailing:
+/// 0. name: NullTerminatedString for each names_len
+pub const ErrorSet = struct {
+ names_len: u32,
+};
+
+/// Trailing:
/// 0. param_type: Index for each params_len
pub const TypeFunction = struct {
params_len: u32,
@@ -1539,11 +1599,6 @@ pub const Array = struct {
}
};
-pub const ErrorUnion = struct {
- error_set_type: Index,
- payload_type: Index,
-};
-
/// Trailing:
/// 0. field name: NullTerminatedString for each fields_len; declaration order
/// 1. tag value: Index for each fields_len; declaration order
@@ -1719,6 +1774,9 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
ip.unions_free_list.deinit(gpa);
ip.allocated_unions.deinit(gpa);
+ ip.inferred_error_sets_free_list.deinit(gpa);
+ ip.allocated_inferred_error_sets.deinit(gpa);
+
for (ip.maps.items) |*map| map.deinit(gpa);
ip.maps.deinit(gpa);
@@ -1798,7 +1856,18 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
.type_optional => .{ .opt_type = @intToEnum(Index, data) },
.type_anyframe => .{ .anyframe_type = @intToEnum(Index, data) },
- .type_error_union => @panic("TODO"),
+ .type_error_union => .{ .error_union_type = ip.extraData(Key.ErrorUnionType, data) },
+ .type_error_set => {
+ const error_set = ip.extraDataTrail(ErrorSet, data);
+ const names_len = error_set.data.names_len;
+ const names = ip.extra.items[error_set.end..][0..names_len];
+ return .{ .error_set_type = .{
+ .names = @ptrCast([]const NullTerminatedString, names),
+ } };
+ },
+ .type_inferred_error_set => .{
+ .inferred_error_set_type = @intToEnum(Module.Fn.InferredErrorSet.Index, data),
+ },
.type_opaque => .{ .opaque_type = ip.extraData(Key.OpaqueType, data) },
.type_struct => {
@@ -2179,11 +2248,29 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
.error_union_type => |error_union_type| {
ip.items.appendAssumeCapacity(.{
.tag = .type_error_union,
- .data = try ip.addExtra(gpa, ErrorUnion{
- .error_set_type = error_union_type.error_set_type,
- .payload_type = error_union_type.payload_type,
+ .data = try ip.addExtra(gpa, error_union_type),
+ });
+ },
+ .error_set_type => |error_set_type| {
+ assert(error_set_type.names_map == .none);
+ assert(std.sort.isSorted(NullTerminatedString, error_set_type.names, {}, NullTerminatedString.indexLessThan));
+ const names_map = try ip.addMap(gpa);
+ try addStringsToMap(ip, gpa, names_map, error_set_type.names);
+ const names_len = @intCast(u32, error_set_type.names.len);
+ try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(ErrorSet).Struct.fields.len + names_len);
+ ip.items.appendAssumeCapacity(.{
+ .tag = .type_error_set,
+ .data = ip.addExtraAssumeCapacity(ErrorSet{
+ .names_len = names_len,
}),
});
+ ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, error_set_type.names));
+ },
+ .inferred_error_set_type => |ies_index| {
+ ip.items.appendAssumeCapacity(.{
+ .tag = .type_inferred_error_set,
+ .data = @enumToInt(ies_index),
+ });
},
.simple_type => |simple_type| {
ip.items.appendAssumeCapacity(.{
@@ -3192,12 +3279,26 @@ pub fn indexToFuncType(ip: InternPool, val: Index) ?Key.FuncType {
}
}
+pub fn indexToInferredErrorSetType(ip: InternPool, val: Index) Module.Fn.InferredErrorSet.OptionalIndex {
+ assert(val != .none);
+ const tags = ip.items.items(.tag);
+ if (tags[@enumToInt(val)] != .type_inferred_error_set) return .none;
+ const datas = ip.items.items(.data);
+ return @intToEnum(Module.Fn.InferredErrorSet.Index, datas[@enumToInt(val)]).toOptional();
+}
+
pub fn isOptionalType(ip: InternPool, ty: Index) bool {
const tags = ip.items.items(.tag);
if (ty == .none) return false;
return tags[@enumToInt(ty)] == .type_optional;
}
+pub fn isInferredErrorSetType(ip: InternPool, ty: Index) bool {
+ const tags = ip.items.items(.tag);
+ assert(ty != .none);
+ return tags[@enumToInt(ty)] == .type_inferred_error_set;
+}
+
pub fn dump(ip: InternPool) void {
dumpFallible(ip, std.heap.page_allocator) catch return;
}
@@ -3258,7 +3359,12 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
.type_slice => 0,
.type_optional => 0,
.type_anyframe => 0,
- .type_error_union => @sizeOf(ErrorUnion),
+ .type_error_union => @sizeOf(Key.ErrorUnionType),
+ .type_error_set => b: {
+ const info = ip.extraData(ErrorSet, data);
+ break :b @sizeOf(ErrorSet) + (@sizeOf(u32) * info.names_len);
+ },
+ .type_inferred_error_set => @sizeOf(Module.Fn.InferredErrorSet),
.type_enum_explicit, .type_enum_nonexhaustive => @sizeOf(EnumExplicit),
.type_enum_auto => @sizeOf(EnumAuto),
.type_opaque => @sizeOf(Key.OpaqueType),
@@ -3359,6 +3465,14 @@ pub fn unionPtr(ip: *InternPool, index: Module.Union.Index) *Module.Union {
return ip.allocated_unions.at(@enumToInt(index));
}
+pub fn inferredErrorSetPtr(ip: *InternPool, index: Module.Fn.InferredErrorSet.Index) *Module.Fn.InferredErrorSet {
+ return ip.allocated_inferred_error_sets.at(@enumToInt(index));
+}
+
+pub fn inferredErrorSetPtrConst(ip: InternPool, index: Module.Fn.InferredErrorSet.Index) *const Module.Fn.InferredErrorSet {
+ return ip.allocated_inferred_error_sets.at(@enumToInt(index));
+}
+
pub fn createStruct(
ip: *InternPool,
gpa: Allocator,
@@ -3397,6 +3511,25 @@ pub fn destroyUnion(ip: *InternPool, gpa: Allocator, index: Module.Union.Index)
};
}
+pub fn createInferredErrorSet(
+ ip: *InternPool,
+ gpa: Allocator,
+ initialization: Module.Fn.InferredErrorSet,
+) Allocator.Error!Module.Fn.InferredErrorSet.Index {
+ if (ip.inferred_error_sets_free_list.popOrNull()) |index| return index;
+ const ptr = try ip.allocated_inferred_error_sets.addOne(gpa);
+ ptr.* = initialization;
+ return @intToEnum(Module.Fn.InferredErrorSet.Index, ip.allocated_inferred_error_sets.len - 1);
+}
+
+pub fn destroyInferredErrorSet(ip: *InternPool, gpa: Allocator, index: Module.Fn.InferredErrorSet.Index) void {
+ ip.inferredErrorSetPtr(index).* = undefined;
+ ip.inferred_error_sets_free_list.append(gpa, index) catch {
+ // In order to keep `destroyInferredErrorSet` a non-fallible function, we ignore memory
+ // allocation failures here, instead leaking the InferredErrorSet until garbage collection.
+ };
+}
+
pub fn getOrPutString(
ip: *InternPool,
gpa: Allocator,
@@ -3459,3 +3592,14 @@ pub fn aggregateTypeLen(ip: InternPool, ty: Index) u64 {
else => unreachable,
};
}
+
+pub fn isNoReturn(ip: InternPool, ty: InternPool.Index) bool {
+ return switch (ty) {
+ .noreturn_type => true,
+ else => switch (ip.indexToKey(ty)) {
+ .error_set_type => |error_set_type| error_set_type.names.len == 0,
+ .enum_type => |enum_type| enum_type.names.len == 0,
+ else => false,
+ },
+ };
+}