aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobin Voetter <robin@voetter.nl>2021-12-17 03:40:48 +0100
committerRobin Voetter <robin@voetter.nl>2021-12-21 01:41:50 +0100
commita2958a4ede0af4b4559eeb142c0400ae640db63e (patch)
treed70bec5423b9f712e010657509bd45407db871ba /src
parentb2343e63bd06d1312ca80745236bb42358062115 (diff)
downloadzig-a2958a4ede0af4b4559eeb142c0400ae640db63e.tar.gz
zig-a2958a4ede0af4b4559eeb142c0400ae640db63e.zip
stage2: allow multiple inferred error sets per Fn
This allows the inferred error set of comptime and inline invocations to be resolved separately from the inferred error set of the runtime version or other comptime/inline invocations.
Diffstat (limited to 'src')
-rw-r--r--src/Module.zig112
-rw-r--r--src/Sema.zig46
-rw-r--r--src/codegen/c.zig2
-rw-r--r--src/type.zig4
4 files changed, 99 insertions, 65 deletions
diff --git a/src/Module.zig b/src/Module.zig
index b5d856efd0..7031dc20a5 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -1207,23 +1207,9 @@ pub const Fn = struct {
is_cold: bool = false,
is_noinline: bool = false,
- /// These fields are used to keep track of any dependencies related to functions
- /// that return inferred error sets. It's values are not used when the function
- /// does not return an inferred error set.
- inferred_error_set: struct {
- /// All currently known errors that this function returns. This includes direct additions
- /// via `return error.Foo;`, and possibly also errors that are returned from any dependent functions.
- /// When the inferred error set is fully resolved, this map contains all the errors that the function might return.
- errors: std.StringHashMapUnmanaged(void) = .{},
-
- /// Other functions with inferred error sets which the inferred error set of this
- /// function should include.
- functions: std.AutoHashMapUnmanaged(*Fn, void) = .{},
-
- /// Whether the function returned anyerror. This is true if either of the dependent functions
- /// returns anyerror.
- is_anyerror: bool = false,
- } = .{},
+ /// Any inferred error sets that this function owns, both it's own inferred error set and
+ /// inferred error sets of any inline/comptime functions called.
+ inferred_error_sets: InferredErrorSetList = .{},
pub const Analysis = enum {
queued,
@@ -1239,37 +1225,69 @@ pub const Fn = struct {
success,
};
- pub fn deinit(func: *Fn, gpa: Allocator) void {
- func.inferred_error_set.errors.deinit(gpa);
- func.inferred_error_set.functions.deinit(gpa);
- }
+ /// This struct is used to keep track of any dependencies related to functions instances
+ /// that return inferred error sets. Note that a function may be associated to multiple different error sets,
+ /// for example an inferred error set which this function returns, but also any inferred error sets
+ /// of called inline or comptime functions.
+ pub const InferredErrorSet = struct {
+ /// The function from which this error set originates.
+ /// Note: may be the function itself.
+ func: *Fn,
- pub fn addErrorSet(func: *Fn, gpa: Allocator, err_set_ty: Type) !void {
- switch (err_set_ty.tag()) {
- .error_set => {
- const names = err_set_ty.castTag(.error_set).?.data.names.keys();
- for (names) |name| {
- try func.inferred_error_set.errors.put(gpa, name, {});
- }
- },
- .error_set_single => {
- const name = err_set_ty.castTag(.error_set_single).?.data;
- try func.inferred_error_set.errors.put(gpa, name, {});
- },
- .error_set_inferred => {
- const dependent_func = err_set_ty.castTag(.error_set_inferred).?.data;
- try func.inferred_error_set.functions.put(gpa, dependent_func, {});
- },
- .error_set_merged => {
- const names = err_set_ty.castTag(.error_set_merged).?.data.keys();
- for (names) |name| {
- try func.inferred_error_set.errors.put(gpa, name, {});
- }
- },
- .anyerror => {
- func.inferred_error_set.is_anyerror = true;
- },
- else => unreachable,
+ /// All currently known errors that this error set contains. This includes direct additions
+ /// via `return error.Foo;`, and possibly also errors that are returned from any dependent functions.
+ /// When the inferred error set is fully resolved, this map contains all the errors that the function might return.
+ errors: std.StringHashMapUnmanaged(void) = .{},
+
+ /// Other functions with inferred error sets which the inferred error set of this
+ /// function should include.
+ functions: std.AutoHashMapUnmanaged(*Fn, void) = .{},
+
+ /// Whether the function returned anyerror. This is true if either of the dependent functions
+ /// returns anyerror.
+ is_anyerror: bool = false,
+
+ pub fn addErrorSet(self: *InferredErrorSet, gpa: Allocator, err_set_ty: Type) !void {
+ switch (err_set_ty.tag()) {
+ .error_set => {
+ const names = err_set_ty.castTag(.error_set).?.data.names.keys();
+ for (names) |name| {
+ try self.errors.put(gpa, name, {});
+ }
+ },
+ .error_set_single => {
+ const name = err_set_ty.castTag(.error_set_single).?.data;
+ try self.errors.put(gpa, name, {});
+ },
+ .error_set_inferred => {
+ const dependent_func = err_set_ty.castTag(.error_set_inferred).?.data.func;
+ try self.functions.put(gpa, dependent_func, {});
+ },
+ .error_set_merged => {
+ const names = err_set_ty.castTag(.error_set_merged).?.data.keys();
+ for (names) |name| {
+ try self.errors.put(gpa, name, {});
+ }
+ },
+ .anyerror => {
+ self.is_anyerror = true;
+ },
+ else => unreachable,
+ }
+ }
+ };
+
+ pub const InferredErrorSetList = std.SinglyLinkedList(InferredErrorSet);
+ pub const InferredErrorSetListNode = InferredErrorSetList.Node;
+
+ pub fn deinit(func: *Fn, gpa: Allocator) void {
+ var it = func.inferred_error_sets.first;
+ while (it) |node| {
+ const next = node.next;
+ node.data.errors.deinit(gpa);
+ node.data.functions.deinit(gpa);
+ gpa.destroy(node);
+ it = next;
}
}
};
diff --git a/src/Sema.zig b/src/Sema.zig
index 30c59f9efa..f23ffe24c0 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -3896,11 +3896,12 @@ fn analyzeCall(
const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
// If the function has an inferred error set, `bare_return_type` is the payload type only.
const fn_ret_ty = blk: {
- // TODO instead of reusing the function's inferred error set, this code should
- // create a temporary error set which is used for the comptime/inline function
- // call alone, independent from the runtime instantiation.
- if (func_ty_info.return_type.castTag(.error_union)) |payload| {
- const error_set_ty = payload.data.error_set;
+ if (func_ty_info.return_type.tag() == .error_union) {
+ const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
+ node.data = .{ .func = module_fn };
+ parent_func.?.inferred_error_sets.prepend(node);
+
+ const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data);
break :blk try Type.Tag.error_union.create(sema.arena, .{
.error_set = error_set_ty,
.payload = bare_return_type,
@@ -5066,6 +5067,10 @@ fn funcCommon(
};
errdefer if (body_inst != 0) sema.gpa.destroy(new_func);
+ var maybe_inferred_error_set_node: ?*Module.Fn.InferredErrorSetListNode = null;
+ errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node);
+ // Note: no need to errdefer since this will still be in its default state at the end of the function.
+
const fn_ty: Type = fn_ty: {
// Hot path for some common function types.
// TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.
@@ -5107,7 +5112,11 @@ fn funcCommon(
const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison)
bare_return_type
else blk: {
- const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, new_func);
+ const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
+ node.data = .{ .func = new_func };
+ maybe_inferred_error_set_node = node;
+
+ const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data);
break :blk try Type.Tag.error_union.create(sema.arena, .{
.error_set = error_set_ty,
.payload = bare_return_type,
@@ -5198,7 +5207,14 @@ fn funcCommon(
.rbrace_line = src_locs.rbrace_line,
.lbrace_column = @truncate(u16, src_locs.columns),
.rbrace_column = @truncate(u16, src_locs.columns >> 16),
+ .inferred_error_sets = .{
+ .first = maybe_inferred_error_set_node,
+ },
};
+ if (maybe_inferred_error_set_node) |node| {
+ new_func.inferred_error_sets.prepend(node);
+ }
+ maybe_inferred_error_set_node = null;
fn_payload.* = .{
.base = .{ .tag = .function },
.data = new_func,
@@ -9204,14 +9220,14 @@ fn analyzeRet(
// add the error tag to the inferred error set of the in-scope function, so
// that the coercion below works correctly.
if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
- if (sema.fn_ret_ty.errorUnionSet().tag() == .error_set_inferred) {
+ if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
const op_ty = sema.typeOf(uncasted_operand);
switch (op_ty.zigTypeTag()) {
.ErrorSet => {
- try sema.func.?.addErrorSet(sema.gpa, op_ty);
+ try payload.data.addErrorSet(sema.gpa, op_ty);
},
.ErrorUnion => {
- try sema.func.?.addErrorSet(sema.gpa, op_ty.errorUnionSet());
+ try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet());
},
else => {},
}
@@ -12496,10 +12512,10 @@ fn coerceInMemoryAllowedErrorSets(
// of inferred error sets.
if (src_ty.castTag(.error_set_inferred)) |src_payload| {
if (dest_ty.castTag(.error_set_inferred)) |dst_payload| {
- const src_func = src_payload.data;
- const dst_func = dst_payload.data;
+ const src_func = src_payload.data.func;
+ const dst_func = dst_payload.data.func;
- if (src_func == dst_func or dst_func.inferred_error_set.functions.contains(src_func)) {
+ if (src_func == dst_func or dst_payload.data.functions.contains(src_func)) {
return .ok;
}
}
@@ -13894,10 +13910,10 @@ fn wrapErrorUnion(
}
},
.error_set_inferred => ok: {
- const func = dest_err_set_ty.castTag(.error_set_inferred).?.data;
- if (func.inferred_error_set.is_anyerror) break :ok;
+ const data = dest_err_set_ty.castTag(.error_set_inferred).?.data;
+ if (data.is_anyerror) break :ok;
const expected_name = val.castTag(.@"error").?.data.name;
- if (func.inferred_error_set.errors.contains(expected_name)) break :ok;
+ if (data.errors.contains(expected_name)) break :ok;
// TODO error set resolution here before emitting a compile error
return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
},
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 8babcb9a83..f54ae7f76d 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -722,7 +722,7 @@ pub const DeclGen = struct {
try bw.writeAll(" payload; uint16_t error; } ");
const name_index = buffer.items.len;
if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
- const func = inf_err_set_payload.data;
+ const func = inf_err_set_payload.data.func;
try bw.writeAll("zig_E_");
try dg.renderDeclName(func.owner_decl, bw);
try bw.writeAll(";\n");
diff --git a/src/type.zig b/src/type.zig
index 298df2cffc..3360477a03 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -2869,7 +2869,7 @@ pub const Type = extern union {
pub fn isAnyError(ty: Type) bool {
return switch (ty.tag()) {
.anyerror => true,
- .error_set_inferred => ty.castTag(.error_set_inferred).?.data.inferred_error_set.is_anyerror,
+ .error_set_inferred => ty.castTag(.error_set_inferred).?.data.is_anyerror,
else => false,
};
}
@@ -4156,7 +4156,7 @@ pub const Type = extern union {
pub const base_tag = Tag.error_set_inferred;
base: Payload = Payload{ .tag = base_tag },
- data: *Module.Fn,
+ data: *Module.Fn.InferredErrorSet,
};
pub const Pointer = struct {