aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Voetter <robin@voetter.nl>2021-12-16 01:45:41 +0100
committerRobin Voetter <robin@voetter.nl>2021-12-21 01:41:50 +0100
commitcd733ceb852369427301fbb526b82ad4407d0607 (patch)
tree9673ab520b07e73650be9038b7db62c628795e42
parentea913846c2d45a6e4862d5eaf94773ea880bfeab (diff)
downloadzig-cd733ceb852369427301fbb526b82ad4407d0607.tar.gz
zig-cd733ceb852369427301fbb526b82ad4407d0607.zip
stage2: replace ErrorSet and ErrorSetMerged arrays with hash maps
-rw-r--r--src/Module.zig8
-rw-r--r--src/Sema.zig117
-rw-r--r--src/type.zig17
3 files changed, 55 insertions, 87 deletions
diff --git a/src/Module.zig b/src/Module.zig
index 341b6bb56e..8341c3de60 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -796,15 +796,11 @@ pub const ErrorSet = struct {
owner_decl: *Decl,
/// Offset from Decl node index, points to the error set AST node.
node_offset: i32,
- names_len: u32,
/// The string bytes are stored in the owner Decl arena.
/// They are in the same order they appear in the AST.
- /// The length is given by `names_len`.
- names_ptr: [*]const []const u8,
+ names: NameMap,
- pub fn names(self: ErrorSet) []const []const u8 {
- return self.names_ptr[0..self.names_len];
- }
+ pub const NameMap = std.StringArrayHashMapUnmanaged(void);
pub fn srcLoc(self: ErrorSet) SrcLoc {
return .{
diff --git a/src/Sema.zig b/src/Sema.zig
index 044084a349..6e208f8b7f 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -2025,15 +2025,22 @@ fn zirErrorSetDecl(
}, type_name);
new_decl.owns_tv = true;
errdefer sema.mod.abortAnonDecl(new_decl);
- const names = try new_decl_arena_allocator.alloc([]const u8, fields.len);
- for (fields) |str_index, i| {
- names[i] = try new_decl_arena_allocator.dupe(u8, sema.code.nullTerminatedString(str_index));
+
+ var names = Module.ErrorSet.NameMap{};
+ try names.ensureUnusedCapacity(new_decl_arena_allocator, fields.len);
+ for (fields) |str_index| {
+ const name = try new_decl_arena_allocator.dupe(u8, sema.code.nullTerminatedString(str_index));
+
+ // TODO: This check should be performed in AstGen instead.
+ const result = names.getOrPutAssumeCapacity(name);
+ if (result.found_existing) {
+ return sema.fail(block, src, "duplicate error set field {s}", .{name});
+ }
}
error_set.* = .{
.owner_decl = new_decl,
.node_offset = inst_data.src_node,
- .names_ptr = names.ptr,
- .names_len = @intCast(u32, names.len),
+ .names = names,
};
try new_decl.finalizeNewArena(&new_decl_arena);
return sema.analyzeDeclVal(block, src, new_decl);
@@ -4556,63 +4563,43 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
return Air.Inst.Ref.anyerror_type;
}
// Resolve both error sets now.
- var set: std.StringHashMapUnmanaged(void) = .{};
- defer set.deinit(sema.gpa);
-
- switch (lhs_ty.tag()) {
- .error_set_single => {
- const name = lhs_ty.castTag(.error_set_single).?.data;
- try set.put(sema.gpa, name, {});
- },
- .error_set_merged => {
- const names = lhs_ty.castTag(.error_set_merged).?.data;
- for (names) |name| {
- try set.put(sema.gpa, name, {});
- }
- },
- .error_set => {
- const lhs_set = lhs_ty.castTag(.error_set).?.data;
- try set.ensureUnusedCapacity(sema.gpa, lhs_set.names_len);
- for (lhs_set.names_ptr[0..lhs_set.names_len]) |name| {
- set.putAssumeCapacityNoClobber(name, {});
- }
+ const lhs_names = switch (lhs_ty.tag()) {
+ .error_set_single => blk: {
+ // Work around coercion problems
+ const tmp: *const [1][]const u8 = &lhs_ty.castTag(.error_set_single).?.data;
+ break :blk tmp;
},
+ .error_set_merged => lhs_ty.castTag(.error_set_merged).?.data.keys(),
+ .error_set => lhs_ty.castTag(.error_set).?.data.names.keys(),
else => unreachable,
- }
- switch (rhs_ty.tag()) {
- .error_set_single => {
- const name = rhs_ty.castTag(.error_set_single).?.data;
- try set.put(sema.gpa, name, {});
- },
- .error_set_merged => {
- const names = rhs_ty.castTag(.error_set_merged).?.data;
- for (names) |name| {
- try set.put(sema.gpa, name, {});
- }
- },
- .error_set => {
- const rhs_set = rhs_ty.castTag(.error_set).?.data;
- try set.ensureUnusedCapacity(sema.gpa, rhs_set.names_len);
- for (rhs_set.names_ptr[0..rhs_set.names_len]) |name| {
- set.putAssumeCapacity(name, {});
- }
+ };
+
+ const rhs_names = switch (rhs_ty.tag()) {
+ .error_set_single => blk: {
+ const tmp: *const [1][]const u8 = &rhs_ty.castTag(.error_set_single).?.data;
+ break :blk tmp;
},
+ .error_set_merged => rhs_ty.castTag(.error_set_merged).?.data.keys(),
+ .error_set => rhs_ty.castTag(.error_set).?.data.names.keys(),
else => unreachable,
- }
+ };
// TODO do we really want to create a Decl for this?
// The reason we do it right now is for memory management.
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
- const new_names = try anon_decl.arena().alloc([]const u8, set.count());
- var it = set.keyIterator();
- var i: usize = 0;
- while (it.next()) |key| : (i += 1) {
- new_names[i] = key.*;
+ var names = Module.ErrorSet.NameMap{};
+ // TODO: Guess is an upper bound, but maybe this needs to be reduced by computing the exact size first.
+ try names.ensureUnusedCapacity(anon_decl.arena(), @intCast(u32, lhs_names.len + rhs_names.len));
+ for (lhs_names) |name| {
+ names.putAssumeCapacityNoClobber(name, {});
+ }
+ for (rhs_names) |name| {
+ names.putAssumeCapacity(name, {});
}
- const err_set_ty = try Type.Tag.error_set_merged.create(anon_decl.arena(), new_names);
+ const err_set_ty = try Type.Tag.error_set_merged.create(anon_decl.arena(), names);
const err_set_decl = try anon_decl.finish(
Type.type,
try Value.Tag.ty.create(anon_decl.arena(), err_set_ty),
@@ -11425,14 +11412,8 @@ fn fieldVal(
switch (child_type.zigTypeTag()) {
.ErrorSet => {
const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {
- const error_set = payload.data;
- // TODO this is O(N). I'm putting off solving this until we solve inferred
- // error sets at the same time.
- const names = error_set.names_ptr[0..error_set.names_len];
- for (names) |name| {
- if (mem.eql(u8, field_name, name)) {
- break :blk name;
- }
+ if (payload.data.names.getEntry(field_name)) |entry| {
+ break :blk entry.key_ptr.*;
}
return sema.fail(block, src, "no error named '{s}' in '{}'", .{
field_name, child_type,
@@ -11630,14 +11611,8 @@ fn fieldPtr(
.ErrorSet => {
// TODO resolve inferred error sets
const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {
- const error_set = payload.data;
- // TODO this is O(N). I'm putting off solving this until we solve inferred
- // error sets at the same time.
- const names = error_set.names_ptr[0..error_set.names_len];
- for (names) |name| {
- if (mem.eql(u8, field_name, name)) {
- break :blk name;
- }
+ if (payload.data.names.getEntry(field_name)) |entry| {
+ break :blk entry.key_ptr.*;
}
return sema.fail(block, src, "no error named '{s}' in '{}'", .{
field_name, child_type,
@@ -13916,16 +13891,12 @@ fn wrapErrorUnion(
if (mem.eql(u8, expected_name, n)) break :ok;
return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
},
- .error_set => ok: {
+ .error_set => {
const expected_name = val.castTag(.@"error").?.data.name;
const error_set = dest_err_set_ty.castTag(.error_set).?.data;
- const names = error_set.names_ptr[0..error_set.names_len];
- // TODO this is O(N). I'm putting off solving this until we solve inferred
- // error sets at the same time.
- for (names) |name| {
- if (mem.eql(u8, expected_name, name)) break :ok;
+ if (!error_set.names.contains(expected_name)) {
+ return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
}
- return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
},
.error_set_inferred => ok: {
const err_set_payload = dest_err_set_ty.castTag(.error_set_inferred).?.data;
diff --git a/src/type.zig b/src/type.zig
index c5ba6c03c7..4930c7057f 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -904,10 +904,11 @@ pub const Type = extern union {
});
},
.error_set_merged => {
- const names = self.castTag(.error_set_merged).?.data;
- const duped_names = try allocator.alloc([]const u8, names.len);
- for (duped_names) |*name, i| {
- name.* = try allocator.dupe(u8, names[i]);
+ const names = self.castTag(.error_set_merged).?.data.keys();
+ var duped_names = Module.ErrorSet.NameMap{};
+ try duped_names.ensureTotalCapacity(allocator, names.len);
+ for (names) |name| {
+ duped_names.putAssumeCapacityNoClobber(name, .{});
}
return Tag.error_set_merged.create(allocator, duped_names);
},
@@ -1206,7 +1207,7 @@ pub const Type = extern union {
return writer.print("(inferred error set of {s})", .{func.owner_decl.name});
},
.error_set_merged => {
- const names = ty.castTag(.error_set_merged).?.data;
+ const names = ty.castTag(.error_set_merged).?.data.keys();
try writer.writeAll("error{");
for (names) |name, i| {
if (i != 0) try writer.writeByte(',');
@@ -4148,7 +4149,7 @@ pub const Type = extern union {
pub const base_tag = Tag.error_set_merged;
base: Payload = Payload{ .tag = base_tag },
- data: []const []const u8,
+ data: Module.ErrorSet.NameMap,
};
pub const ErrorSetInferred = struct {
@@ -4168,7 +4169,7 @@ pub const Type = extern union {
pub fn addErrorSet(self: *Data, gpa: Allocator, err_set_ty: Type) !void {
switch (err_set_ty.tag()) {
.error_set => {
- const names = err_set_ty.castTag(.error_set).?.data.names();
+ const names = err_set_ty.castTag(.error_set).?.data.names.keys();
for (names) |name| {
try self.map.put(gpa, name, {});
}
@@ -4187,7 +4188,7 @@ pub const Type = extern union {
}
},
.error_set_merged => {
- const names = err_set_ty.castTag(.error_set_merged).?.data;
+ const names = err_set_ty.castTag(.error_set_merged).?.data.keys();
for (names) |name| {
try self.map.put(gpa, name, {});
}