| author | |
| committer | |
| log | a2958a4ede0af4b4559eeb142c0400ae640db63e |
| tree | d70bec5423b9f712e010657509bd45407db871ba |
| parent | b2343e63bd06d1312ca80745236bb42358062115 |
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, |
| 1209 | 1209 | ||
| 1210 | /// These fields are used to keep track of any dependencies related to functions | 1210 | /// 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 function | 1211 | /// 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 | } = .{}, | ||
| 1227 | 1213 | ||
| 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 | }; |
| 1241 | 1227 | ||
| 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, | ||
| 1246 | 1236 | ||
| 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 should | 3899 | if (func_ty_info.return_type.tag() == .error_union) { |
| 3900 | // create a temporary error set which is used for the comptime/inline function | 3900 | 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); |
| 5068 | 5069 | ||
| 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_type | 5113 | 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, so | 9220 | // 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; |
| 12501 | 12517 | ||
| 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 error | 13917 | // 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; |
| 4157 | 4157 | ||
| 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 | }; |
| 4161 | 4161 | ||
| 4162 | pub const Pointer = struct { | 4162 | pub const Pointer = struct { |