aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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 {