authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-12-17 03:40:48+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-12-21 01:41:50+01:00
loga2958a4ede0af4b4559eeb142c0400ae640db63e
treed70bec5423b9f712e010657509bd45407db871ba
parentb2343e63bd06d1312ca80745236bb42358062115

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.

4 files changed, 99 insertions(+), 65 deletions(-)

src/Module.zig+65-47
......@@ -1207,23 +1207,9 @@ pub const Fn = struct {
12071207 is_cold: bool = false,
12081208 is_noinline: bool = false,
12091209
1210 /// These fields are used to keep track of any dependencies related to functions
1211 /// that return inferred error sets. It's values are not used when the function
1212 /// does not return an inferred error set.
1213 inferred_error_set: struct {
1214 /// All currently known errors that this function returns. This includes direct additions
1215 /// via `return error.Foo;`, and possibly also errors that are returned from any dependent functions.
1216 /// When the inferred error set is fully resolved, this map contains all the errors that the function might return.
1217 errors: std.StringHashMapUnmanaged(void) = .{},
1218
1219 /// Other functions with inferred error sets which the inferred error set of this
1220 /// function should include.
1221 functions: std.AutoHashMapUnmanaged(*Fn, void) = .{},
1222
1223 /// Whether the function returned anyerror. This is true if either of the dependent functions
1224 /// returns anyerror.
1225 is_anyerror: bool = false,
1226 } = .{},
1210 /// Any inferred error sets that this function owns, both it's own inferred error set and
1211 /// inferred error sets of any inline/comptime functions called.
1212 inferred_error_sets: InferredErrorSetList = .{},
12271213
12281214 pub const Analysis = enum {
12291215 queued,
......@@ -1239,37 +1225,69 @@ pub const Fn = struct {
12391225 success,
12401226 };
12411227
1242 pub fn deinit(func: *Fn, gpa: Allocator) void {
1243 func.inferred_error_set.errors.deinit(gpa);
1244 func.inferred_error_set.functions.deinit(gpa);
1245 }
1228 /// This struct is used to keep track of any dependencies related to functions instances
1229 /// that return inferred error sets. Note that a function may be associated to multiple different error sets,
1230 /// for example an inferred error set which this function returns, but also any inferred error sets
1231 /// of called inline or comptime functions.
1232 pub const InferredErrorSet = struct {
1233 /// The function from which this error set originates.
1234 /// Note: may be the function itself.
1235 func: *Fn,
12461236
1247 pub fn addErrorSet(func: *Fn, gpa: Allocator, err_set_ty: Type) !void {
1248 switch (err_set_ty.tag()) {
1249 .error_set => {
1250 const names = err_set_ty.castTag(.error_set).?.data.names.keys();
1251 for (names) |name| {
1252 try func.inferred_error_set.errors.put(gpa, name, {});
1253 }
1254 },
1255 .error_set_single => {
1256 const name = err_set_ty.castTag(.error_set_single).?.data;
1257 try func.inferred_error_set.errors.put(gpa, name, {});
1258 },
1259 .error_set_inferred => {
1260 const dependent_func = err_set_ty.castTag(.error_set_inferred).?.data;
1261 try func.inferred_error_set.functions.put(gpa, dependent_func, {});
1262 },
1263 .error_set_merged => {
1264 const names = err_set_ty.castTag(.error_set_merged).?.data.keys();
1265 for (names) |name| {
1266 try func.inferred_error_set.errors.put(gpa, name, {});
1267 }
1268 },
1269 .anyerror => {
1270 func.inferred_error_set.is_anyerror = true;
1271 },
1272 else => unreachable,
1237 /// All currently known errors that this error set contains. This includes direct additions
1238 /// via `return error.Foo;`, and possibly also errors that are returned from any dependent functions.
1239 /// When the inferred error set is fully resolved, this map contains all the errors that the function might return.
1240 errors: std.StringHashMapUnmanaged(void) = .{},
1241
1242 /// Other functions with inferred error sets which the inferred error set of this
1243 /// function should include.
1244 functions: std.AutoHashMapUnmanaged(*Fn, void) = .{},
1245
1246 /// Whether the function returned anyerror. This is true if either of the dependent functions
1247 /// returns anyerror.
1248 is_anyerror: bool = false,
1249
1250 pub fn addErrorSet(self: *InferredErrorSet, gpa: Allocator, err_set_ty: Type) !void {
1251 switch (err_set_ty.tag()) {
1252 .error_set => {
1253 const names = err_set_ty.castTag(.error_set).?.data.names.keys();
1254 for (names) |name| {
1255 try self.errors.put(gpa, name, {});
1256 }
1257 },
1258 .error_set_single => {
1259 const name = err_set_ty.castTag(.error_set_single).?.data;
1260 try self.errors.put(gpa, name, {});
1261 },
1262 .error_set_inferred => {
1263 const dependent_func = err_set_ty.castTag(.error_set_inferred).?.data.func;
1264 try self.functions.put(gpa, dependent_func, {});
1265 },
1266 .error_set_merged => {
1267 const names = err_set_ty.castTag(.error_set_merged).?.data.keys();
1268 for (names) |name| {
1269 try self.errors.put(gpa, name, {});
1270 }
1271 },
1272 .anyerror => {
1273 self.is_anyerror = true;
1274 },
1275 else => unreachable,
1276 }
1277 }
1278 };
1279
1280 pub const InferredErrorSetList = std.SinglyLinkedList(InferredErrorSet);
1281 pub const InferredErrorSetListNode = InferredErrorSetList.Node;
1282
1283 pub fn deinit(func: *Fn, gpa: Allocator) void {
1284 var it = func.inferred_error_sets.first;
1285 while (it) |node| {
1286 const next = node.next;
1287 node.data.errors.deinit(gpa);
1288 node.data.functions.deinit(gpa);
1289 gpa.destroy(node);
1290 it = next;
12731291 }
12741292 }
12751293};
src/Sema.zig+31-15
......@@ -3896,11 +3896,12 @@ fn analyzeCall(
38963896 const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
38973897 // If the function has an inferred error set, `bare_return_type` is the payload type only.
38983898 const fn_ret_ty = blk: {
3899 // TODO instead of reusing the function's inferred error set, this code should
3900 // create a temporary error set which is used for the comptime/inline function
3901 // call alone, independent from the runtime instantiation.
3902 if (func_ty_info.return_type.castTag(.error_union)) |payload| {
3903 const error_set_ty = payload.data.error_set;
3899 if (func_ty_info.return_type.tag() == .error_union) {
3900 const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
3901 node.data = .{ .func = module_fn };
3902 parent_func.?.inferred_error_sets.prepend(node);
3903
3904 const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data);
39043905 break :blk try Type.Tag.error_union.create(sema.arena, .{
39053906 .error_set = error_set_ty,
39063907 .payload = bare_return_type,
......@@ -5066,6 +5067,10 @@ fn funcCommon(
50665067 };
50675068 errdefer if (body_inst != 0) sema.gpa.destroy(new_func);
50685069
5070 var maybe_inferred_error_set_node: ?*Module.Fn.InferredErrorSetListNode = null;
5071 errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node);
5072 // Note: no need to errdefer since this will still be in its default state at the end of the function.
5073
50695074 const fn_ty: Type = fn_ty: {
50705075 // Hot path for some common function types.
50715076 // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.
......@@ -5107,7 +5112,11 @@ fn funcCommon(
51075112 const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison)
51085113 bare_return_type
51095114 else blk: {
5110 const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, new_func);
5115 const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
5116 node.data = .{ .func = new_func };
5117 maybe_inferred_error_set_node = node;
5118
5119 const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data);
51115120 break :blk try Type.Tag.error_union.create(sema.arena, .{
51125121 .error_set = error_set_ty,
51135122 .payload = bare_return_type,
......@@ -5198,7 +5207,14 @@ fn funcCommon(
51985207 .rbrace_line = src_locs.rbrace_line,
51995208 .lbrace_column = @truncate(u16, src_locs.columns),
52005209 .rbrace_column = @truncate(u16, src_locs.columns >> 16),
5210 .inferred_error_sets = .{
5211 .first = maybe_inferred_error_set_node,
5212 },
52015213 };
5214 if (maybe_inferred_error_set_node) |node| {
5215 new_func.inferred_error_sets.prepend(node);
5216 }
5217 maybe_inferred_error_set_node = null;
52025218 fn_payload.* = .{
52035219 .base = .{ .tag = .function },
52045220 .data = new_func,
......@@ -9204,14 +9220,14 @@ fn analyzeRet(
92049220 // add the error tag to the inferred error set of the in-scope function, so
92059221 // that the coercion below works correctly.
92069222 if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
9207 if (sema.fn_ret_ty.errorUnionSet().tag() == .error_set_inferred) {
9223 if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
92089224 const op_ty = sema.typeOf(uncasted_operand);
92099225 switch (op_ty.zigTypeTag()) {
92109226 .ErrorSet => {
9211 try sema.func.?.addErrorSet(sema.gpa, op_ty);
9227 try payload.data.addErrorSet(sema.gpa, op_ty);
92129228 },
92139229 .ErrorUnion => {
9214 try sema.func.?.addErrorSet(sema.gpa, op_ty.errorUnionSet());
9230 try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet());
92159231 },
92169232 else => {},
92179233 }
......@@ -12496,10 +12512,10 @@ fn coerceInMemoryAllowedErrorSets(
1249612512 // of inferred error sets.
1249712513 if (src_ty.castTag(.error_set_inferred)) |src_payload| {
1249812514 if (dest_ty.castTag(.error_set_inferred)) |dst_payload| {
12499 const src_func = src_payload.data;
12500 const dst_func = dst_payload.data;
12515 const src_func = src_payload.data.func;
12516 const dst_func = dst_payload.data.func;
1250112517
12502 if (src_func == dst_func or dst_func.inferred_error_set.functions.contains(src_func)) {
12518 if (src_func == dst_func or dst_payload.data.functions.contains(src_func)) {
1250312519 return .ok;
1250412520 }
1250512521 }
......@@ -13894,10 +13910,10 @@ fn wrapErrorUnion(
1389413910 }
1389513911 },
1389613912 .error_set_inferred => ok: {
13897 const func = dest_err_set_ty.castTag(.error_set_inferred).?.data;
13898 if (func.inferred_error_set.is_anyerror) break :ok;
13913 const data = dest_err_set_ty.castTag(.error_set_inferred).?.data;
13914 if (data.is_anyerror) break :ok;
1389913915 const expected_name = val.castTag(.@"error").?.data.name;
13900 if (func.inferred_error_set.errors.contains(expected_name)) break :ok;
13916 if (data.errors.contains(expected_name)) break :ok;
1390113917 // TODO error set resolution here before emitting a compile error
1390213918 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
1390313919 },
src/codegen/c.zig+1-1
......@@ -722,7 +722,7 @@ pub const DeclGen = struct {
722722 try bw.writeAll(" payload; uint16_t error; } ");
723723 const name_index = buffer.items.len;
724724 if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
725 const func = inf_err_set_payload.data;
725 const func = inf_err_set_payload.data.func;
726726 try bw.writeAll("zig_E_");
727727 try dg.renderDeclName(func.owner_decl, bw);
728728 try bw.writeAll(";\n");
src/type.zig+2-2
......@@ -2869,7 +2869,7 @@ pub const Type = extern union {
28692869 pub fn isAnyError(ty: Type) bool {
28702870 return switch (ty.tag()) {
28712871 .anyerror => true,
2872 .error_set_inferred => ty.castTag(.error_set_inferred).?.data.inferred_error_set.is_anyerror,
2872 .error_set_inferred => ty.castTag(.error_set_inferred).?.data.is_anyerror,
28732873 else => false,
28742874 };
28752875 }
......@@ -4156,7 +4156,7 @@ pub const Type = extern union {
41564156 pub const base_tag = Tag.error_set_inferred;
41574157
41584158 base: Payload = Payload{ .tag = base_tag },
4159 data: *Module.Fn,
4159 data: *Module.Fn.InferredErrorSet,
41604160 };
41614161
41624162 pub const Pointer = struct {