diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 112 | ||||
| -rw-r--r-- | src/Sema.zig | 46 | ||||
| -rw-r--r-- | src/codegen/c.zig | 2 | ||||
| -rw-r--r-- | src/type.zig | 4 |
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 { |
