diff options
| author | Robin Voetter <robin@voetter.nl> | 2021-12-16 01:45:41 +0100 |
|---|---|---|
| committer | Robin Voetter <robin@voetter.nl> | 2021-12-21 01:41:50 +0100 |
| commit | cd733ceb852369427301fbb526b82ad4407d0607 (patch) | |
| tree | 9673ab520b07e73650be9038b7db62c628795e42 /src | |
| parent | ea913846c2d45a6e4862d5eaf94773ea880bfeab (diff) | |
| download | zig-cd733ceb852369427301fbb526b82ad4407d0607.tar.gz zig-cd733ceb852369427301fbb526b82ad4407d0607.zip | |
stage2: replace ErrorSet and ErrorSetMerged arrays with hash maps
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 8 | ||||
| -rw-r--r-- | src/Sema.zig | 117 | ||||
| -rw-r--r-- | src/type.zig | 17 |
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, {}); } |
