Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Represent error sets through InternPool #2201

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/DocumentScope.zig
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ fn getDeclNameToken(tree: Ast, node: Ast.Node.Index) ?Ast.TokenIndex {
.tagged_union_two_trailing,
.tagged_union_enum_tag,
.tagged_union_enum_tag_trailing,
.error_set_decl,
.block,
.block_semicolon,
.block_two,
Expand All @@ -143,7 +142,6 @@ pub const Declaration = union(enum) {
/// - `.root`
/// - `.container_decl`
/// - `.tagged_union`
/// - `.error_set_decl`
/// - `.container_field`
/// - `.fn_proto`
/// - `.fn_decl`
Expand Down
56 changes: 56 additions & 0 deletions src/analyser/InternPool.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1518,6 +1518,56 @@ pub fn getUnionMut(ip: *InternPool, index: Union.Index) *Union {
return ip.unions.at(@intFromEnum(index));
}

pub fn createDeclFromVarDecl(
ip: *InternPool,
gpa: Allocator,
index: InternPool.Index,
tree: std.zig.Ast,
node_idx: std.zig.Ast.Node.Index,
var_decl: std.zig.Ast.full.VarDecl,
) Allocator.Error!Decl.Index {
const main_tokens = tree.nodes.items(.main_token);
const node_tags = tree.nodes.items(.tag);
const token_tags = tree.tokens.items(.tag);

const align_node = var_decl.ast.align_node;
const addrspace_node = var_decl.ast.addrspace_node;

var alignment: ?u16 = null;
if (align_node != 0 and node_tags[align_node] == .number_literal) {
const s = tree.tokenSlice(main_tokens[align_node]);
alignment = std.fmt.parseInt(u16, s, 0) catch null;
}

var address_space: ?std.builtin.AddressSpace = null;
if (addrspace_node != 0 and node_tags[addrspace_node] == .enum_literal) {
const s = tree.tokenSlice(main_tokens[addrspace_node]);
address_space = std.meta.stringToEnum(std.builtin.AddressSpace, s);
}

var is_pub = false;
if (var_decl.visib_token) |token| {
is_pub = token_tags[token] == .keyword_pub;
}

var is_exported = false;
if (var_decl.extern_export_token) |token| {
is_exported = token_tags[token] == .keyword_export;
}

const name = tree.tokenSlice(var_decl.ast.mut_token + 1);
return ip.createDecl(gpa, .{
.name = try ip.string_pool.getOrPutString(gpa, name),
.node_idx = node_idx,
.index = index,
.alignment = alignment orelse 0,
.address_space = address_space orelse .generic,
.src_namespace = .none, // TODO
.is_pub = is_pub,
.is_exported = is_exported,
});
}

pub fn createDecl(ip: *InternPool, gpa: Allocator, decl: Decl) Allocator.Error!Decl.Index {
ip.lock.lock();
defer ip.lock.unlock();
Expand Down Expand Up @@ -3780,6 +3830,8 @@ const FormatContext = struct {
// TODO add options for controlling how types show be formatted
pub const FormatOptions = struct {
debug: bool = false,
// TODO: truncate structs, unions, enums
truncate_container: bool = false,
};

fn format(
Expand Down Expand Up @@ -3929,6 +3981,10 @@ fn printInternal(ip: *InternPool, ty: Index, writer: anytype, options: FormatOpt
try writer.print("{}", .{ip.fmtId(decl.name)});
return null;
}
if (options.truncate_container and error_set_info.names.len > 2) {
try writer.writeAll("error{...}");
return null;
}
try writer.writeAll("error{");
for (0..error_set_info.names.len) |i| {
if (i != 0) try writer.writeByte(',');
Expand Down
106 changes: 66 additions & 40 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -425,13 +425,6 @@ pub fn getVariableSignature(
}

const end_token = switch (node_tags[init_node]) {
.merge_error_sets => {
if (!include_name) return "error";
return try std.fmt.allocPrint(arena, "{s} error", .{
offsets.tokensToSlice(tree, start_token, tree.firstToken(init_node) - 1),
});
},
.error_set_decl => tree.firstToken(init_node),
.container_decl,
.container_decl_trailing,
.container_decl_arg,
Expand Down Expand Up @@ -1583,7 +1576,25 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e

if (var_decl.ast.init_node != 0) blk: {
const value: NodeWithHandle = .{ .node = var_decl.ast.init_node, .handle = handle };
return try analyser.resolveTypeOfNodeInternal(value) orelse break :blk;
const ty = try analyser.resolveTypeOfNodeInternal(value) orelse break :blk;
switch (ty.data) {
.ip_index => |payload| {
const index = payload.index orelse return ty;
switch (analyser.ip.indexToKey(index)) {
.error_set_type => |info| {
if (info.owner_decl != .none) return ty;
const decl = try analyser.ip.createDeclFromVarDecl(analyser.gpa, index, tree, node, var_decl);
const ip_index = try analyser.ip.get(analyser.gpa, .{ .error_set_type = .{
.owner_decl = decl.toOptional(),
.names = info.names,
} });
return Type.fromIP(analyser, .type_type, ip_index);
},
else => return ty,
}
},
else => return ty,
}
}

return fallback_type;
Expand Down Expand Up @@ -1857,10 +1868,39 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
};
},

// TODO represent through InternPool
.merge_error_sets => return Type.typeVal(node_handle),
.merge_error_sets => {
const lhs = try analyser.resolveTypeOfNodeInternal(.{ .node = datas[node].lhs, .handle = handle }) orelse return null;
const rhs = try analyser.resolveTypeOfNodeInternal(.{ .node = datas[node].rhs, .handle = handle }) orelse return null;
if (!lhs.is_type_val) return null;
if (!rhs.is_type_val) return null;
const lhs_index = switch (lhs.data) {
.ip_index => |payload| payload.index orelse return null,
else => return null,
};
const rhs_index = switch (rhs.data) {
.ip_index => |payload| payload.index orelse return null,
else => return null,
};
if (analyser.ip.zigTypeTag(lhs_index) != .error_set) return null;
if (analyser.ip.zigTypeTag(rhs_index) != .error_set) return null;
const ip_index = try analyser.ip.errorSetMerge(analyser.gpa, lhs_index, rhs_index);
return Type.fromIP(analyser, .type_type, ip_index);
},

.error_set_decl => {
var strings: std.ArrayListUnmanaged(InternPool.String) = .empty;
defer strings.deinit(analyser.gpa);
var it: ast.ErrorSetIterator = .init(tree, node);
while (it.next()) |identifier_token| {
const name = offsets.tokenToSlice(tree, identifier_token);
const index = try analyser.ip.string_pool.getOrPutString(analyser.gpa, name);
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 } });
return Type.fromIP(analyser, .type_type, ip_index);
},

.error_set_decl, // TODO represent through InternPool
.container_decl,
.container_decl_arg,
.container_decl_arg_trailing,
Expand Down Expand Up @@ -2549,10 +2589,8 @@ pub const Type = struct {
/// - `enum {}`
/// - `union {}`
/// - `opaque {}`
/// - `error {}`
container: ScopeWithHandle,

/// - Error type: `Foo || Bar`, `Foo!Bar`
/// - Function: `fn () Foo`, `fn foo() Foo`
/// - `start..end`
other: NodeWithHandle,
Expand Down Expand Up @@ -2960,6 +2998,17 @@ pub const Type = struct {
}
}

pub fn isErrorSetType(self: Type, analyser: *Analyser) bool {
if (!self.is_type_val) return false;
switch (self.data) {
.ip_index => |payload| {
const ip_index = payload.index orelse return false;
return analyser.ip.zigTypeTag(ip_index) == .error_set;
},
else => return false,
}
}

pub fn isEnumLiteral(self: Type) bool {
switch (self.data) {
.ip_index => |payload| return payload.type == .enum_literal_type,
Expand Down Expand Up @@ -3184,7 +3233,6 @@ pub const Type = struct {
.container_decl_trailing,
.container_decl_two,
.container_decl_two_trailing,
.error_set_decl,
.tagged_union,
.tagged_union_trailing,
.tagged_union_two,
Expand Down Expand Up @@ -3217,29 +3265,6 @@ pub const Type = struct {
return;
}

if (tree.nodes.items(.tag)[node] == .error_set_decl) {
const field_count = ast.errorSetFieldCount(tree, node);
if (field_count > 2) {
try writer.writeAll("error{...}");
return;
}

var it: ast.ErrorSetIterator = .init(tree, node);
var i: usize = 0;

try writer.writeAll("error{");
while (it.next()) |identifier_token| : (i += 1) {
if (i != 0) {
try writer.writeByte(',');
}
const name = offsets.tokenToSlice(tree, identifier_token);
try writer.writeAll(name);
}
try writer.writeByte('}');

return;
}

var buffer: [2]Ast.Node.Index = undefined;
const container_decl = tree.fullContainerDecl(&buffer, node).?;

Expand Down Expand Up @@ -3287,10 +3312,12 @@ pub const Type = struct {
.snippet_placeholders = false,
})});
},
.merge_error_sets => if (ctx.options.truncate_container_decls) try writer.writeAll("error{...}") else try writer.writeAll(offsets.nodeToSlice(node_handle.handle.tree, node_handle.node)),
else => try writer.writeAll(offsets.nodeToSlice(node_handle.handle.tree, node_handle.node)),
},
.ip_index => |payload| try analyser.ip.print(payload.index orelse try analyser.ip.getUnknown(analyser.gpa, payload.type), writer, .{}),
.ip_index => |payload| {
const ip_index = payload.index orelse try analyser.ip.getUnknown(analyser.gpa, payload.type);
try analyser.ip.print(ip_index, writer, .{ .truncate_container = true });
},
.either => try writer.writeAll("either type"), // TODO
.compile_error => |node_handle| try writer.writeAll(offsets.nodeToSlice(node_handle.handle.tree, node_handle.node)),
}
Expand Down Expand Up @@ -5268,7 +5295,6 @@ fn addReferencedTypes(
.container_decl_trailing,
.container_decl_two,
.container_decl_two_trailing,
.error_set_decl,
.tagged_union,
.tagged_union_trailing,
.tagged_union_two,
Expand Down
13 changes: 1 addition & 12 deletions src/features/completions.zig
Original file line number Diff line number Diff line change
Expand Up @@ -127,18 +127,6 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi
});
}
},
.other => |node_handle| switch (node_handle.handle.tree.nodes.items(.tag)[node_handle.node]) {
.merge_error_sets => {
const node_data = node_handle.handle.tree.nodes.items(.data)[node_handle.node];
if (try builder.analyser.resolveTypeOfNode(.{ .node = node_data.lhs, .handle = node_handle.handle })) |lhs_ty| {
try typeToCompletion(builder, lhs_ty);
}
if (try builder.analyser.resolveTypeOfNode(.{ .node = node_data.rhs, .handle = node_handle.handle })) |rhs_ty| {
try typeToCompletion(builder, rhs_ty);
}
},
else => {},
},
.ip_index => |payload| try analyser_completions.dotCompletions(
builder.arena,
&builder.completions,
Expand All @@ -153,6 +141,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi
},
.error_union,
.union_tag,
.other,
.compile_error,
=> {},
}
Expand Down
8 changes: 4 additions & 4 deletions src/features/semantic_tokens.zig
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,10 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v
return;
};
const lhs_type = try builder.analyser.resolveDerefType(lhs) orelse lhs;
if (lhs_type.isErrorSetType(builder.analyser)) {
try writeToken(builder, data.rhs, .errorTag);
return;
}
if (try lhs_type.lookupSymbol(builder.analyser, symbol_name)) |decl_type| {
switch (decl_type.decl) {
.ast_node => |decl_node| {
Expand All @@ -886,10 +890,6 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v
}
}
},
.error_token => {
try writeToken(builder, data.rhs, .errorTag);
return;
},
else => {},
}

Expand Down
4 changes: 2 additions & 2 deletions tests/lsp_features/completion.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1879,7 +1879,7 @@ test "error set" {
\\const baz = E2.<cursor>
, &.{
.{ .label = "baz", .kind = .Constant, .detail = "error.baz" },
.{ .label = "qux", .kind = .Constant, .detail = "error.qux", .documentation = "hello" },
.{ .label = "qux", .kind = .Constant, .detail = "error.qux" },
});
}

Expand Down Expand Up @@ -1969,7 +1969,7 @@ test "merged error sets" {
\\const Error = error{Foo} || error{Bar};
\\const E = <cursor>
, &.{
.{ .label = "Error", .kind = .Constant, .detail = "type" },
.{ .label = "Error", .kind = .Constant, .detail = "Error" },
});
}

Expand Down
6 changes: 3 additions & 3 deletions tests/lsp_features/hover.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,7 @@ test "function" {
\\fn foo(a: A, b: B) E!C
\\```
\\
\\Go to [A](file:///test.zig#L1) | [B](file:///test.zig#L2) | [E](file:///test.zig#L4) | [C](file:///test.zig#L3)
\\Go to [A](file:///test.zig#L1) | [B](file:///test.zig#L2) | [C](file:///test.zig#L3)
);
try testHover(
\\const S = struct { a: i32 };
Expand All @@ -1032,7 +1032,7 @@ test "function" {
\\fn foo(a: S, b: S) E!S
\\```
\\
\\Go to [S](file:///test.zig#L1) | [E](file:///test.zig#L2)
\\Go to [S](file:///test.zig#L1)
);
try testHover(
\\fn foo(b<cursor>ar: enum { fizz, buzz }) void {}
Expand Down Expand Up @@ -1192,7 +1192,7 @@ test "error union" {
\\(E!S)
\\```
\\
\\Go to [E](file:///test.zig#L2) | [S](file:///test.zig#L1)
\\Go to [S](file:///test.zig#L1)
);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/lsp_features/inlay_hints.zig
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ test "truncate anonymous error sets" {

test "truncate merged error sets" {
try testInlayHints(
\\const A<error{...}> = @as(error{ Foo } || error{ Bar }, undefined);
\\const A<error{Foo,Bar}> = @as(error{ Foo } || error{ Bar }, undefined);
, .{ .kind = .Type });
}

Expand Down