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 {...@@ -1207,23 +1207,9 @@ pub const Fn = struct {
1207 is_cold: bool = false,1207 is_cold: bool = false,
1208 is_noinline: bool = false,1208 is_noinline: bool = false,
12091209
1210 /// These fields are used to keep track of any dependencies related to functions1210 /// Any inferred error sets that this function owns, both it's own inferred error set and
1211 /// that return inferred error sets. It's values are not used when the function1211 /// inferred error sets of any inline/comptime functions called.
1212 /// does not return an inferred error set.1212 inferred_error_sets: InferredErrorSetList = .{},
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 } = .{},
12271213
1228 pub const Analysis = enum {1214 pub const Analysis = enum {
1229 queued,1215 queued,
...@@ -1239,37 +1225,69 @@ pub const Fn = struct {...@@ -1239,37 +1225,69 @@ pub const Fn = struct {
1239 success,1225 success,
1240 };1226 };
12411227
1242 pub fn deinit(func: *Fn, gpa: Allocator) void {1228 /// This struct is used to keep track of any dependencies related to functions instances
1243 func.inferred_error_set.errors.deinit(gpa);1229 /// that return inferred error sets. Note that a function may be associated to multiple different error sets,
1244 func.inferred_error_set.functions.deinit(gpa);1230 /// for example an inferred error set which this function returns, but also any inferred error sets
1245 }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 {1237 /// All currently known errors that this error set contains. This includes direct additions
1248 switch (err_set_ty.tag()) {1238 /// via `return error.Foo;`, and possibly also errors that are returned from any dependent functions.
1249 .error_set => {1239 /// When the inferred error set is fully resolved, this map contains all the errors that the function might return.
1250 const names = err_set_ty.castTag(.error_set).?.data.names.keys();1240 errors: std.StringHashMapUnmanaged(void) = .{},
1251 for (names) |name| {1241
1252 try func.inferred_error_set.errors.put(gpa, name, {});1242 /// Other functions with inferred error sets which the inferred error set of this
1253 }1243 /// function should include.
1254 },1244 functions: std.AutoHashMapUnmanaged(*Fn, void) = .{},
1255 .error_set_single => {1245
1256 const name = err_set_ty.castTag(.error_set_single).?.data;1246 /// Whether the function returned anyerror. This is true if either of the dependent functions
1257 try func.inferred_error_set.errors.put(gpa, name, {});1247 /// returns anyerror.
1258 },1248 is_anyerror: bool = false,
1259 .error_set_inferred => {1249
1260 const dependent_func = err_set_ty.castTag(.error_set_inferred).?.data;1250 pub fn addErrorSet(self: *InferredErrorSet, gpa: Allocator, err_set_ty: Type) !void {
1261 try func.inferred_error_set.functions.put(gpa, dependent_func, {});1251 switch (err_set_ty.tag()) {
1262 },1252 .error_set => {
1263 .error_set_merged => {1253 const names = err_set_ty.castTag(.error_set).?.data.names.keys();
1264 const names = err_set_ty.castTag(.error_set_merged).?.data.keys();1254 for (names) |name| {
1265 for (names) |name| {1255 try self.errors.put(gpa, name, {});
1266 try func.inferred_error_set.errors.put(gpa, name, {});1256 }
1267 }1257 },
1268 },1258 .error_set_single => {
1269 .anyerror => {1259 const name = err_set_ty.castTag(.error_set_single).?.data;
1270 func.inferred_error_set.is_anyerror = true;1260 try self.errors.put(gpa, name, {});
1271 },1261 },
1272 else => unreachable,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;
1273 }1291 }
1274 }1292 }
1275};1293};
src/Sema.zig+31-15
...@@ -3896,11 +3896,12 @@ fn analyzeCall(...@@ -3896,11 +3896,12 @@ fn analyzeCall(
3896 const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);3896 const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
3897 // If the function has an inferred error set, `bare_return_type` is the payload type only.3897 // If the function has an inferred error set, `bare_return_type` is the payload type only.
3898 const fn_ret_ty = blk: {3898 const fn_ret_ty = blk: {
3899 // TODO instead of reusing the function's inferred error set, this code should3899 if (func_ty_info.return_type.tag() == .error_union) {
3900 // create a temporary error set which is used for the comptime/inline function3900 const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
3901 // call alone, independent from the runtime instantiation.3901 node.data = .{ .func = module_fn };
3902 if (func_ty_info.return_type.castTag(.error_union)) |payload| {3902 parent_func.?.inferred_error_sets.prepend(node);
3903 const error_set_ty = payload.data.error_set;3903
3904 const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data);
3904 break :blk try Type.Tag.error_union.create(sema.arena, .{3905 break :blk try Type.Tag.error_union.create(sema.arena, .{
3905 .error_set = error_set_ty,3906 .error_set = error_set_ty,
3906 .payload = bare_return_type,3907 .payload = bare_return_type,
...@@ -5066,6 +5067,10 @@ fn funcCommon(...@@ -5066,6 +5067,10 @@ fn funcCommon(
5066 };5067 };
5067 errdefer if (body_inst != 0) sema.gpa.destroy(new_func);5068 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
5069 const fn_ty: Type = fn_ty: {5074 const fn_ty: Type = fn_ty: {
5070 // Hot path for some common function types.5075 // Hot path for some common function types.
5071 // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.5076 // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.
...@@ -5107,7 +5112,11 @@ fn funcCommon(...@@ -5107,7 +5112,11 @@ fn funcCommon(
5107 const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison)5112 const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison)
5108 bare_return_type5113 bare_return_type
5109 else blk: {5114 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);
5111 break :blk try Type.Tag.error_union.create(sema.arena, .{5120 break :blk try Type.Tag.error_union.create(sema.arena, .{
5112 .error_set = error_set_ty,5121 .error_set = error_set_ty,
5113 .payload = bare_return_type,5122 .payload = bare_return_type,
...@@ -5198,7 +5207,14 @@ fn funcCommon(...@@ -5198,7 +5207,14 @@ fn funcCommon(
5198 .rbrace_line = src_locs.rbrace_line,5207 .rbrace_line = src_locs.rbrace_line,
5199 .lbrace_column = @truncate(u16, src_locs.columns),5208 .lbrace_column = @truncate(u16, src_locs.columns),
5200 .rbrace_column = @truncate(u16, src_locs.columns >> 16),5209 .rbrace_column = @truncate(u16, src_locs.columns >> 16),
5210 .inferred_error_sets = .{
5211 .first = maybe_inferred_error_set_node,
5212 },
5201 };5213 };
5214 if (maybe_inferred_error_set_node) |node| {
5215 new_func.inferred_error_sets.prepend(node);
5216 }
5217 maybe_inferred_error_set_node = null;
5202 fn_payload.* = .{5218 fn_payload.* = .{
5203 .base = .{ .tag = .function },5219 .base = .{ .tag = .function },
5204 .data = new_func,5220 .data = new_func,
...@@ -9204,14 +9220,14 @@ fn analyzeRet(...@@ -9204,14 +9220,14 @@ fn analyzeRet(
9204 // add the error tag to the inferred error set of the in-scope function, so9220 // add the error tag to the inferred error set of the in-scope function, so
9205 // that the coercion below works correctly.9221 // that the coercion below works correctly.
9206 if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {9222 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| {
9208 const op_ty = sema.typeOf(uncasted_operand);9224 const op_ty = sema.typeOf(uncasted_operand);
9209 switch (op_ty.zigTypeTag()) {9225 switch (op_ty.zigTypeTag()) {
9210 .ErrorSet => {9226 .ErrorSet => {
9211 try sema.func.?.addErrorSet(sema.gpa, op_ty);9227 try payload.data.addErrorSet(sema.gpa, op_ty);
9212 },9228 },
9213 .ErrorUnion => {9229 .ErrorUnion => {
9214 try sema.func.?.addErrorSet(sema.gpa, op_ty.errorUnionSet());9230 try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet());
9215 },9231 },
9216 else => {},9232 else => {},
9217 }9233 }
...@@ -12496,10 +12512,10 @@ fn coerceInMemoryAllowedErrorSets(...@@ -12496,10 +12512,10 @@ fn coerceInMemoryAllowedErrorSets(
12496 // of inferred error sets.12512 // of inferred error sets.
12497 if (src_ty.castTag(.error_set_inferred)) |src_payload| {12513 if (src_ty.castTag(.error_set_inferred)) |src_payload| {
12498 if (dest_ty.castTag(.error_set_inferred)) |dst_payload| {12514 if (dest_ty.castTag(.error_set_inferred)) |dst_payload| {
12499 const src_func = src_payload.data;12515 const src_func = src_payload.data.func;
12500 const dst_func = dst_payload.data;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)) {
12503 return .ok;12519 return .ok;
12504 }12520 }
12505 }12521 }
...@@ -13894,10 +13910,10 @@ fn wrapErrorUnion(...@@ -13894,10 +13910,10 @@ fn wrapErrorUnion(
13894 }13910 }
13895 },13911 },
13896 .error_set_inferred => ok: {13912 .error_set_inferred => ok: {
13897 const func = dest_err_set_ty.castTag(.error_set_inferred).?.data;13913 const data = dest_err_set_ty.castTag(.error_set_inferred).?.data;
13898 if (func.inferred_error_set.is_anyerror) break :ok;13914 if (data.is_anyerror) break :ok;
13899 const expected_name = val.castTag(.@"error").?.data.name;13915 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;
13901 // TODO error set resolution here before emitting a compile error13917 // TODO error set resolution here before emitting a compile error
13902 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);13918 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
13903 },13919 },
src/codegen/c.zig+1-1
...@@ -722,7 +722,7 @@ pub const DeclGen = struct {...@@ -722,7 +722,7 @@ pub const DeclGen = struct {
722 try bw.writeAll(" payload; uint16_t error; } ");722 try bw.writeAll(" payload; uint16_t error; } ");
723 const name_index = buffer.items.len;723 const name_index = buffer.items.len;
724 if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {724 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;
726 try bw.writeAll("zig_E_");726 try bw.writeAll("zig_E_");
727 try dg.renderDeclName(func.owner_decl, bw);727 try dg.renderDeclName(func.owner_decl, bw);
728 try bw.writeAll(";\n");728 try bw.writeAll(";\n");
src/type.zig+2-2
...@@ -2869,7 +2869,7 @@ pub const Type = extern union {...@@ -2869,7 +2869,7 @@ pub const Type = extern union {
2869 pub fn isAnyError(ty: Type) bool {2869 pub fn isAnyError(ty: Type) bool {
2870 return switch (ty.tag()) {2870 return switch (ty.tag()) {
2871 .anyerror => true,2871 .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,
2873 else => false,2873 else => false,
2874 };2874 };
2875 }2875 }
...@@ -4156,7 +4156,7 @@ pub const Type = extern union {...@@ -4156,7 +4156,7 @@ pub const Type = extern union {
4156 pub const base_tag = Tag.error_set_inferred;4156 pub const base_tag = Tag.error_set_inferred;
41574157
4158 base: Payload = Payload{ .tag = base_tag },4158 base: Payload = Payload{ .tag = base_tag },
4159 data: *Module.Fn,4159 data: *Module.Fn.InferredErrorSet,
4160 };4160 };
41614161
4162 pub const Pointer = struct {4162 pub const Pointer = struct {