diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index ec8b434db..cd2398886 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -111,6 +111,7 @@ pub const Key = union(enum) { pub const ErrorSet = struct { owner_decl: Decl.OptionalIndex, names: StringSlice, + source_node: u32, }; pub const Function = struct { @@ -346,6 +347,7 @@ pub const Key = union(enum) { .error_set_type => |error_set_type| { std.hash.autoHash(hasher, error_set_type.owner_decl); error_set_type.names.hashWithHasher(hasher, ip); + std.hash.autoHash(hasher, error_set_type.source_node); }, .function_type => |function_type| { std.hash.autoHash(hasher, function_type.args_is_comptime); @@ -435,6 +437,7 @@ pub const Key = union(enum) { const b_info = b.error_set_type; if (a_info.owner_decl != b_info.owner_decl) return false; + if (a_info.source_node != b_info.source_node) return false; if (a_info.names.len != b_info.names.len) return false; @@ -3545,6 +3548,7 @@ pub fn errorSetMerge(ip: *InternPool, gpa: Allocator, a_ty: Index, b_ty: Index) .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(gpa, set.keys()), + .source_node = 0, }, }); } @@ -4543,16 +4547,19 @@ test "error set type" { const empty_error_set = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = StringSlice.empty, + .source_node = 0, } }); const foo_bar_baz_set = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(gpa, &.{ foo_name, bar_name, baz_name }), + .source_node = 0, } }); const foo_bar_set = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(gpa, &.{ foo_name, bar_name }), + .source_node = 0, } }); try expect(empty_error_set != foo_bar_baz_set); @@ -4572,6 +4579,7 @@ test "error union type" { const empty_error_set = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = StringSlice.empty, + .source_node = 0, } }); const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); @@ -4932,18 +4940,22 @@ test "coerceInMemoryAllowed error set" { const foo_bar_baz_set = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(gpa, &.{ baz_name, bar_name, foo_name }), + .source_node = 0, } }); const foo_bar_set = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(gpa, &.{ foo_name, bar_name }), + .source_node = 0, } }); const foo_set = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(gpa, &.{foo_name}), + .source_node = 0, } }); const empty_set = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = StringSlice.empty, + .source_node = 0, } }); try expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, foo_bar_baz_set, true, builtin.target) == .ok); @@ -5224,21 +5236,25 @@ test "resolvePeerTypes error sets" { const @"error{foo}" = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(gpa, &.{foo_name}), + .source_node = 0, } }); const @"error{bar}" = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(gpa, &.{bar_name}), + .source_node = 0, } }); const @"error{foo,bar}" = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(gpa, &.{ foo_name, bar_name }), + .source_node = 0, } }); const @"error{bar,foo}" = try ip.get(gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = try ip.getStringSlice(gpa, &.{ bar_name, foo_name }), + .source_node = 0, } }); try ip.testResolvePeerTypesInOrder(@"error{foo}", @"error{bar}", @"error{foo,bar}"); diff --git a/src/analyser/degibberish.zig b/src/analyser/degibberish.zig index 34bbd2de0..12fdb2fb5 100644 --- a/src/analyser/degibberish.zig +++ b/src/analyser/degibberish.zig @@ -204,6 +204,7 @@ test "degibberish - error union types" { const @"error{foo,bar,baz}" = try ip.get(gpa, .{ .error_set_type = .{ .names = try ip.getStringSlice(gpa, &.{ foo_string, bar_string, baz_string }), .owner_decl = .none, + .source_node = 0, } }); const @"error{foo,bar,baz}!u32" = try ip.get(gpa, .{ .error_union_type = .{ @@ -226,6 +227,7 @@ test "degibberish - error set types" { const @"error{foo,bar,baz}" = try ip.get(gpa, .{ .error_set_type = .{ .names = try ip.getStringSlice(gpa, &.{ foo_string, bar_string, baz_string }), .owner_decl = .none, + .source_node = 0, } }); try std.testing.expectFmt("error set of (foo,bar,baz)", "{f}", .{fmtDegibberish(&ip, @"error{foo,bar,baz}")}); diff --git a/src/analysis.zig b/src/analysis.zig index 2bbbc3d67..c4def6c4e 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2125,7 +2125,11 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) error try strings.append(analyser.gpa, index); } const names = try analyser.ip.getStringSlice(analyser.gpa, strings.items); - const ip_index = try analyser.ip.get(analyser.gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = names } }); + const ip_index = try analyser.ip.get(analyser.gpa, .{ .error_set_type = .{ + .owner_decl = .none, + .names = names, + .source_node = @intFromEnum(node), + } }); return Type.fromIP(analyser, .type_type, ip_index); }, @@ -2700,6 +2704,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) error const error_set_type = try analyser.ip.get(analyser.gpa, .{ .error_set_type = .{ .owner_decl = .none, .names = try analyser.ip.getStringSlice(analyser.gpa, &.{name_index}), + .source_node = 0, } }); const error_value = try analyser.ip.get(analyser.gpa, .{ .error_value = .{ .ty = error_set_type, @@ -4125,6 +4130,9 @@ pub const Type = struct { return decl; } } + if (self.isErrorSetType(analyser)) { + return try self.lookupErrorSetField(analyser, symbol); + } return try lookupSymbolContainer(self, symbol, .other); } else { if (try lookupSymbolContainer(self, symbol, .other)) |decl| { @@ -4141,6 +4149,52 @@ pub const Type = struct { } } + fn lookupErrorSetField(self: Type, analyser: *Analyser, field_name: []const u8) error{OutOfMemory}!?DeclWithHandle { + const ip_index = self.data.ip_index.index orelse return null; + const error_set = analyser.ip.indexToKey(ip_index).error_set_type; + + var field_index: ?usize = null; + const names = analyser.ip.extra.items[error_set.names.start..][0..error_set.names.len]; + for (names, 0..) |name_index, i| { + const name_locked = analyser.ip.string_pool.stringToSliceLock(@enumFromInt(name_index)); + defer name_locked.release(&analyser.ip.string_pool); + if (std.mem.eql(u8, name_locked.slice, field_name)) { + field_index = i; + break; + } + } + + const found_index = field_index orelse return null; + if (error_set.source_node == 0) return null; + + const node_index = error_set.source_node; + + for (analyser.store.handles.values()) |handle| { + const tree = handle.tree; + if (node_index >= tree.nodes.len) continue; + + const node: Ast.Node.Index = @enumFromInt(node_index); + if (tree.nodeTag(node) != .error_set_decl) continue; + + const lbrace, const rbrace = tree.nodeData(node).token_and_token; + + var error_i: usize = 0; + for (lbrace + 1..rbrace) |tok_i| { + if (tree.tokenTag(@intCast(tok_i)) != .identifier) continue; + if (error_i == found_index) { + const identifier_token: Ast.TokenIndex = @intCast(tok_i); + return .{ + .decl = .{ .error_token = identifier_token }, + .handle = handle, + }; + } + error_i += 1; + } + } + + return null; + } + pub fn stringifyTypeOf(ty: Type, analyser: *Analyser, options: FormatOptions) error{OutOfMemory}![]const u8 { const typeof = try ty.typeOf(analyser); var aw: std.io.Writer.Allocating = .init(analyser.arena); diff --git a/src/features/references.zig b/src/features/references.zig index ce420d2a9..c44a2683f 100644 --- a/src/features/references.zig +++ b/src/features/references.zig @@ -311,7 +311,7 @@ fn symbolReferences( => |payload| payload.node, .function_parameter => |payload| payload.func, .label => unreachable, // handled separately by labelReferences - .error_token => return .empty, + .error_token => null, }; var builder: Builder = .{