authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-27 11:46:48+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 10:26:08+00:00
log334189ce6d20d6d1100f115252d5589fadb064b1
tree489120f03743b1ba7ad5a18b0ed9083e365d8616
parente3e9ae12bd3a7c08f3ed41e801f6cf34bec01af2
signaturelock-open Commit is signed but in an unrecognized format.

compiler: simplify IESes

It is always a bug in Sema to check whether an IES is resolved. This is because whether the IES is resolved depends on whether the function which owns it has been analyzed yet, which depends on the order the compiler analyzes declarations in, which it is incorrect to have any dependency on. Instead, we must always either not look at the resolved set, or resolve it first (with `Sema.ensureFuncIesResolved`) and then look at the definitely-resolved concrete error set. Luckily, removing a bunch of the buggy logic which tried to opportunistically use already-resolved inferred error sets actually didn't regress anything! It seems this logic was mostly left over from before Andrew reworked inferred error sets, and had become essentially dead code. This is because inferred error sets are stricter than they used to be, and in particular, we make no attempt to support mutual recursion. I suspect that most of the logic touching IESes can be simplified even further than I have done here without regressing any existing code; my goal in this commit was just to remove any *buggy* code I could find.

5 files changed, 232 insertions(+), 380 deletions(-)

src/Sema.zig+217-343
......@@ -7870,21 +7870,21 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
78707870 return .anyerror_type;
78717871 }
78727872
7873 if (ip.isInferredErrorSetType(lhs_ty.toIntern())) {
7874 switch (try sema.resolveInferredErrorSet(block, src, lhs_ty.toIntern())) {
7875 // isAnyError might have changed from a false negative to a true
7876 // positive after resolution.
7877 .anyerror_type => return .anyerror_type,
7878 else => {},
7879 }
7873 switch (ip.indexToKey(lhs_ty.toIntern())) {
7874 .inferred_error_set_type => |func_index| {
7875 try sema.ensureFuncIesResolved(block, src, func_index);
7876 if (ip.funcIesResolvedUnordered(func_index) == .anyerror_type) return .anyerror_type;
7877 },
7878 .error_set_type => {},
7879 else => unreachable,
78807880 }
7881 if (ip.isInferredErrorSetType(rhs_ty.toIntern())) {
7882 switch (try sema.resolveInferredErrorSet(block, src, rhs_ty.toIntern())) {
7883 // isAnyError might have changed from a false negative to a true
7884 // positive after resolution.
7885 .anyerror_type => return .anyerror_type,
7886 else => {},
7887 }
7881 switch (ip.indexToKey(rhs_ty.toIntern())) {
7882 .inferred_error_set_type => |func_index| {
7883 try sema.ensureFuncIesResolved(block, src, func_index);
7884 if (ip.funcIesResolvedUnordered(func_index) == .anyerror_type) return .anyerror_type;
7885 },
7886 .error_set_type => {},
7887 else => unreachable,
78887888 }
78897889
78907890 const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty);
......@@ -12000,7 +12000,7 @@ fn wantSwitchProngBodyAnalysis(
1200012000 if (err_set and prong_is_comptime_unreach) {
1200112001 const item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable;
1200212002 const err_name = item_val.getErrorName(zcu).unwrap().?;
12003 if (!Type.errorSetHasFieldIp(&zcu.intern_pool, operand_ty.toIntern(), err_name)) return false;
12003 if (!operand_ty.errorSetHasField(err_name, zcu)) return false;
1200412004 }
1200512005 return true;
1200612006}
......@@ -21023,34 +21023,61 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
2102321023 else => unreachable,
2102421024 };
2102521025
21026 const disjoint = disjoint: {
21027 // Try avoiding resolving inferred error sets if we can
21028 if (!dest_err_ty.isAnyError(zcu) and dest_err_ty.errorSetIsEmpty(zcu)) break :disjoint true;
21029 if (!operand_err_ty.isAnyError(zcu) and operand_err_ty.errorSetIsEmpty(zcu)) break :disjoint true;
21030 if (dest_err_ty.isAnyError(zcu)) break :disjoint false;
21031 if (operand_err_ty.isAnyError(zcu)) break :disjoint false;
21032 const dest_err_names = dest_err_ty.errorSetNames(zcu);
21033 for (0..dest_err_names.len) |dest_err_index| {
21034 if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
21035 break :disjoint false;
21036 }
21037
21038 if (!ip.isInferredErrorSetType(dest_err_ty.toIntern()) and
21039 !ip.isInferredErrorSetType(operand_err_ty.toIntern()))
21040 {
21041 break :disjoint true;
21042 }
21043
21044 _ = try sema.resolveInferredErrorSetTy(block, src, dest_err_ty.toIntern());
21045 _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_err_ty.toIntern());
21046 for (0..dest_err_names.len) |dest_err_index| {
21047 if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
21048 break :disjoint false;
21049 }
21026 switch (ip.indexToKey(operand_err_ty.toIntern())) {
21027 .inferred_error_set_type => |func| try sema.ensureFuncIesResolved(block, src, func),
21028 else => {},
21029 }
2105021030
21051 break :disjoint true;
21031 const result: enum {
21032 /// The operand and destination error sets are disjoint, i.e. have no errors in common.
21033 disjoint,
21034 /// The destination error set is a superset of the operand error set, so the operation is
21035 /// effectively equivalent to a coercion.
21036 superset,
21037 /// The operand and destination error sets have *some* errors in common, but the destination
21038 /// is not a superset of the operand, so a safety check may be needed.
21039 overlap,
21040 } = if (operand_err_ty.errorSetIsEmpty(zcu)) res: {
21041 break :res .disjoint;
21042 } else check: switch (dest_err_ty.toIntern()) {
21043 .anyerror_type => .superset,
21044 .adhoc_inferred_error_set_type => {
21045 // `@errorCast` to this function's own error set.
21046 try sema.fn_ret_ty_ies.?.addErrorSet(operand_err_ty, ip, sema.arena);
21047 break :check .superset;
21048 },
21049 else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) {
21050 .inferred_error_set_type => |func_index| {
21051 if (sema.fn_ret_ty_ies) |dst_ies| {
21052 if (dst_ies.func == func_index) {
21053 // `@errorCast` to this function's own error set.
21054 try sema.fn_ret_ty_ies.?.addErrorSet(operand_err_ty, ip, sema.arena);
21055 break :check .superset;
21056 }
21057 }
21058 try sema.ensureFuncIesResolved(block, src, func_index);
21059 continue :check ip.funcIesResolvedUnordered(func_index);
21060 },
21061 .error_set_type => |dest| {
21062 if (operand_err_ty.isAnyError(zcu)) break :check .superset;
21063 var dest_has_all = true;
21064 var dest_has_any = false;
21065 for (operand_err_ty.errorSetNames(zcu).get(ip)) |operand_err_name| {
21066 if (dest.nameIndex(ip, operand_err_name) != null) {
21067 dest_has_any = true;
21068 } else {
21069 dest_has_all = false;
21070 }
21071 }
21072 if (!dest_has_any) break :check .disjoint;
21073 if (dest_has_all) break :check .superset;
21074 break :check .overlap;
21075 },
21076 else => unreachable,
21077 },
2105221078 };
21053 if (disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) {
21079
21080 if (result == .disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) {
2105421081 return sema.fail(block, src, "error sets '{f}' and '{f}' have no common errors", .{
2105521082 operand_err_ty.fmt(pt), dest_err_ty.fmt(pt),
2105621083 });
......@@ -21058,25 +21085,30 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
2105821085
2105921086 // operand must be defined since it can be an invalid error value
2106021087 if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
21061 const err_name: InternPool.NullTerminatedString = switch (operand_tag) {
21062 .error_set => ip.indexToKey(operand_val.toIntern()).err.name,
21063 .error_union => switch (ip.indexToKey(operand_val.toIntern()).error_union.val) {
21088 const err_name: InternPool.NullTerminatedString = switch (ip.indexToKey(operand_val.toIntern())) {
21089 .err => |err| err.name,
21090 .error_union => |eu| switch (eu.val) {
2106421091 .err_name => |name| name,
2106521092 .payload => |payload_val| {
2106621093 assert(dest_tag == .error_union); // should be guaranteed from the type checks above
21067 return sema.coerce(block, dest_ty, Air.internedToRef(payload_val), operand_src);
21094 const dest_payload_ty = dest_ty.errorUnionPayload(zcu);
21095 const coerced_payload = try sema.coerce(block, dest_payload_ty, .fromIntern(payload_val), operand_src);
21096 return sema.wrapErrorUnionPayload(block, dest_ty, coerced_payload, operand_src) catch |err| switch (err) {
21097 error.NotCoercible => unreachable,
21098 else => |e| return e,
21099 };
2106821100 },
2106921101 },
2107021102 else => unreachable,
2107121103 };
2107221104
21073 if (!dest_err_ty.isAnyError(zcu) and !Type.errorSetHasFieldIp(ip, dest_err_ty.toIntern(), err_name)) {
21105 if (!dest_err_ty.isAnyError(zcu) and !dest_err_ty.errorSetHasField(err_name, zcu)) {
2107421106 return sema.fail(block, src, "'error.{f}' not a member of error set '{f}'", .{
2107521107 err_name.fmt(ip), dest_err_ty.fmt(pt),
2107621108 });
2107721109 }
2107821110
21079 return Air.internedToRef(try pt.intern(switch (dest_tag) {
21111 return .fromIntern(try pt.intern(switch (dest_tag) {
2108021112 .error_set => .{ .err = .{
2108121113 .ty = dest_ty.toIntern(),
2108221114 .name = err_name,
......@@ -21090,21 +21122,17 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
2109021122 }
2109121123
2109221124 const err_int_ty = try pt.errorIntType();
21093 if (block.wantSafety() and !dest_err_ty.isAnyError(zcu) and
21094 dest_err_ty.toIntern() != .adhoc_inferred_error_set_type and
21095 zcu.backendSupportsFeature(.error_set_has_value))
21096 {
21125 if (block.wantSafety() and result != .superset and zcu.backendSupportsFeature(.error_set_has_value)) {
2109721126 const err_code_inst = switch (operand_tag) {
2109821127 .error_set => operand,
2109921128 .error_union => try block.addTyOp(.unwrap_errunion_err, operand_err_ty, operand),
2110021129 else => unreachable,
2110121130 };
2110221131 const err_int_inst = try block.addBitCast(err_int_ty, err_code_inst);
21103
2110421132 if (dest_tag == .error_union) {
2110521133 const zero_err = try pt.intRef(err_int_ty, 0);
2110621134 const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err);
21107 if (disjoint) {
21135 if (result == .disjoint) {
2110821136 // Error must be zero.
2110921137 try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code);
2111021138 } else {
......@@ -25599,31 +25627,28 @@ fn fieldVal(
2559925627
2560025628 switch (child_type.zigTypeTag(zcu)) {
2560125629 .error_set => {
25602 switch (ip.indexToKey(child_type.toIntern())) {
25603 .error_set_type => |error_set_type| blk: {
25604 if (error_set_type.nameIndex(ip, field_name) != null) break :blk;
25630 const err_set_ty: Type = err_set: switch (ip.indexToKey(child_type.toIntern())) {
25631 .inferred_error_set_type => |func_index| {
25632 try sema.ensureFuncIesResolved(block, src, func_index);
25633 const resolved_ies = ip.funcIesResolvedUnordered(func_index);
25634 continue :err_set ip.indexToKey(resolved_ies);
25635 },
25636 .error_set_type => |err_set| if (err_set.nameIndex(ip, field_name) == null) {
2560525637 return sema.fail(block, src, "no error named '{f}' in '{f}'", .{
2560625638 field_name.fmt(ip), child_type.fmt(pt),
2560725639 });
25608 },
25609 .inferred_error_set_type => {
25610 return sema.fail(block, src, "TODO handle inferred error sets here", .{});
25611 },
25640 } else child_type,
2561225641 .simple_type => |t| {
2561325642 assert(t == .anyerror);
2561425643 _ = try pt.getErrorValue(field_name);
25644 break :err_set try pt.singleErrorSetType(field_name);
2561525645 },
2561625646 else => unreachable,
25617 }
25618
25619 const error_set_type = if (!child_type.isAnyError(zcu))
25620 child_type
25621 else
25622 try pt.singleErrorSetType(field_name);
25623 return Air.internedToRef((try pt.intern(.{ .err = .{
25624 .ty = error_set_type.toIntern(),
25647 };
25648 return .fromIntern(try pt.intern(.{ .err = .{
25649 .ty = err_set_ty.toIntern(),
2562525650 .name = field_name,
25626 } })));
25651 } }));
2562725652 },
2562825653 .@"union" => {
2562925654 if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
......@@ -25832,31 +25857,26 @@ fn fieldPtr(
2583225857
2583325858 switch (child_type.zigTypeTag(zcu)) {
2583425859 .error_set => {
25835 switch (ip.indexToKey(child_type.toIntern())) {
25836 .error_set_type => |error_set_type| blk: {
25837 if (error_set_type.nameIndex(ip, field_name) != null) {
25838 break :blk;
25839 }
25860 const err_set_ty: Type = err_set: switch (ip.indexToKey(child_type.toIntern())) {
25861 .inferred_error_set_type => |func_index| {
25862 try sema.ensureFuncIesResolved(block, src, func_index);
25863 const resolved_ies = ip.funcIesResolvedUnordered(func_index);
25864 continue :err_set ip.indexToKey(resolved_ies);
25865 },
25866 .error_set_type => |err_set| if (err_set.nameIndex(ip, field_name) == null) {
2584025867 return sema.fail(block, src, "no error named '{f}' in '{f}'", .{
2584125868 field_name.fmt(ip), child_type.fmt(pt),
2584225869 });
25843 },
25844 .inferred_error_set_type => {
25845 return sema.fail(block, src, "TODO handle inferred error sets here", .{});
25846 },
25870 } else child_type,
2584725871 .simple_type => |t| {
2584825872 assert(t == .anyerror);
2584925873 _ = try pt.getErrorValue(field_name);
25874 break :err_set try pt.singleErrorSetType(field_name);
2585025875 },
2585125876 else => unreachable,
25852 }
25853
25854 const error_set_type = if (!child_type.isAnyError(zcu))
25855 child_type
25856 else
25857 try pt.singleErrorSetType(field_name);
25877 };
2585825878 return uavRef(sema, try pt.intern(.{ .err = .{
25859 .ty = error_set_type.toIntern(),
25879 .ty = err_set_ty.toIntern(),
2586025880 .name = field_name,
2586125881 } }));
2586225882 },
......@@ -27760,23 +27780,27 @@ fn coerceExtra(
2776027780 else => {},
2776127781 },
2776227782 .error_union => switch (inst_ty.zigTypeTag(zcu)) {
27763 .error_set => {
27764 // E to E!T
27765 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
27783 // E to E!T
27784 .error_set => if (sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src)) |res| {
27785 return res;
27786 } else |err| switch (err) {
27787 error.NotCoercible => if (in_memory_result == .no_match) {
27788 // Try to give more useful notes
27789 const err_set_type = dest_ty.errorUnionSet(zcu);
27790 in_memory_result = try sema.coerceInMemoryAllowed(block, err_set_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
27791 },
27792 else => |e| return e,
2776627793 },
27767 else => eu: {
27768 // T to E!T
27769 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) {
27770 error.NotCoercible => {
27771 if (in_memory_result == .no_match) {
27772 const payload_type = dest_ty.errorUnionPayload(zcu);
27773 // Try to give more useful notes
27774 in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
27775 }
27776 break :eu;
27777 },
27778 else => |e| return e,
27779 };
27794 // T to E!T
27795 else => if (sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src)) |res| {
27796 return res;
27797 } else |err| switch (err) {
27798 error.NotCoercible => if (in_memory_result == .no_match) {
27799 // Try to give more useful notes
27800 const payload_type = dest_ty.errorUnionPayload(zcu);
27801 in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
27802 },
27803 else => |e| return e,
2778027804 },
2778127805 },
2778227806 .@"union" => switch (inst_ty.zigTypeTag(zcu)) {
......@@ -28542,89 +28566,62 @@ fn coerceInMemoryAllowedErrorSets(
2854228566 const gpa = sema.gpa;
2854328567 const ip = &zcu.intern_pool;
2854428568
28545 // Coercion to `anyerror`. Note that this check can return false negatives
28546 // in case the error sets did not get resolved.
28547 if (dest_ty.isAnyError(zcu)) {
28548 return .ok;
28549 }
28550
28551 if (dest_ty.toIntern() == .adhoc_inferred_error_set_type) {
28552 // We are trying to coerce an error set to the current function's
28553 // inferred error set.
28554 const dst_ies = sema.fn_ret_ty_ies.?;
28555 try dst_ies.addErrorSet(src_ty, ip, sema.arena);
28556 return .ok;
28557 }
28558
28559 if (ip.isInferredErrorSetType(dest_ty.toIntern())) {
28560 const dst_ies_func_index = ip.iesFuncIndex(dest_ty.toIntern());
28561 if (sema.fn_ret_ty_ies) |dst_ies| {
28562 if (dst_ies.func == dst_ies_func_index) {
28563 // We are trying to coerce an error set to the current function's
28564 // inferred error set.
28565 try dst_ies.addErrorSet(src_ty, ip, sema.arena);
28566 return .ok;
28567 }
28568 }
28569 switch (try sema.resolveInferredErrorSet(block, dest_src, dest_ty.toIntern())) {
28570 // isAnyError might have changed from a false negative to a true
28571 // positive after resolution.
28572 .anyerror_type => return .ok,
28573 else => {},
28574 }
28575 }
28576
28577 var missing_error_buf = std.array_list.Managed(InternPool.NullTerminatedString).init(gpa);
28578 defer missing_error_buf.deinit();
28579
28580 switch (src_ty.toIntern()) {
28581 .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) {
28582 .simple_type => unreachable, // filtered out above
28583 .error_set_type, .inferred_error_set_type => return .from_anyerror,
28584 else => unreachable,
28569 const dest_set: InternPool.Key.ErrorSetType = err_set: switch (dest_ty.toIntern()) {
28570 .anyerror_type => return .ok,
28571 .adhoc_inferred_error_set_type => {
28572 // We are trying to coerce an error set to the current function's
28573 // inferred error set.
28574 const dst_ies = sema.fn_ret_ty_ies.?;
28575 try dst_ies.addErrorSet(src_ty, ip, sema.arena);
28576 return .ok;
2858528577 },
28586
28587 else => switch (ip.indexToKey(src_ty.toIntern())) {
28588 .inferred_error_set_type => {
28589 const resolved_src_ty = try sema.resolveInferredErrorSet(block, src_src, src_ty.toIntern());
28590 // src anyerror status might have changed after the resolution.
28591 if (resolved_src_ty == .anyerror_type) {
28592 // dest_ty.isAnyError(zcu) == true is already checked for at this point.
28593 return .from_anyerror;
28594 }
28595
28596 for (ip.indexToKey(resolved_src_ty).error_set_type.names.get(ip)) |key| {
28597 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) {
28598 try missing_error_buf.append(key);
28578 else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) {
28579 .inferred_error_set_type => |func_index| {
28580 if (sema.fn_ret_ty_ies) |dst_ies| {
28581 if (dst_ies.func == func_index) {
28582 // We are trying to coerce an error set to the current function's
28583 // inferred error set.
28584 try dst_ies.addErrorSet(src_ty, ip, sema.arena);
28585 return .ok;
2859928586 }
2860028587 }
28601
28602 if (missing_error_buf.items.len != 0) {
28603 return InMemoryCoercionResult{
28604 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
28605 };
28606 }
28607
28608 return .ok;
28588 try sema.ensureFuncIesResolved(block, dest_src, func_index);
28589 continue :err_set ip.funcIesResolvedUnordered(func_index);
2860928590 },
28610 .error_set_type => |error_set_type| {
28611 for (error_set_type.names.get(ip)) |name| {
28612 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) {
28613 try missing_error_buf.append(name);
28614 }
28615 }
28616
28617 if (missing_error_buf.items.len != 0) {
28618 return InMemoryCoercionResult{
28619 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
28620 };
28621 }
28591 .error_set_type => |err_set| err_set,
28592 else => unreachable,
28593 },
28594 };
2862228595
28623 return .ok;
28596 const src_names: InternPool.NullTerminatedString.Slice = err_set: switch (src_ty.toIntern()) {
28597 .anyerror_type => return .from_anyerror,
28598 else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) {
28599 .inferred_error_set_type => |func_index| {
28600 try sema.ensureFuncIesResolved(block, src_src, func_index);
28601 continue :err_set ip.funcIesResolvedUnordered(func_index);
2862428602 },
28603 .error_set_type => |err_set| err_set.names,
2862528604 else => unreachable,
2862628605 },
28606 };
28607
28608 var missing_error_buf: std.ArrayList(InternPool.NullTerminatedString) = .empty;
28609 defer missing_error_buf.deinit(gpa);
28610
28611 for (src_names.get(ip)) |name| {
28612 if (dest_set.nameIndex(ip, name) == null) {
28613 try missing_error_buf.append(gpa, name);
28614 }
28615 }
28616
28617 if (missing_error_buf.items.len != 0) {
28618 return .{ .missing_error = try sema.arena.dupe(
28619 InternPool.NullTerminatedString,
28620 missing_error_buf.items,
28621 ) };
2862728622 }
28623
28624 return .ok;
2862828625}
2862928626
2863028627fn coerceInMemoryAllowedFns(
......@@ -30357,76 +30354,34 @@ fn resolveIsNonErrFromType(
3035730354
3035830355 // exception if the error union error set is known to be empty,
3035930356 // we allow the comparison but always make it comptime-known.
30360 const set_ty = ip.errorUnionSet(operand_ty.toIntern());
30361 switch (set_ty) {
30362 .anyerror_type => {},
30363 .adhoc_inferred_error_set_type => if (sema.fn_ret_ty_ies) |ies| blk: {
30364 // If the error set is empty, we must return a comptime true or false.
30365 // However we want to avoid unnecessarily resolving an inferred error set
30366 // in case it is already non-empty.
30367 switch (ies.resolved) {
30368 .anyerror_type => break :blk,
30369 .none => {},
30370 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk,
30371 }
30372
30373 if (ies.errors.count() != 0) return null;
30374 switch (ies.resolved) {
30375 .anyerror_type => return null,
30376 .none => {},
30377 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
30378 0 => return .true,
30379 else => return null,
30380 },
30381 }
30382 // We do not have a comptime answer because this inferred error
30383 // set is not resolved, and an instruction later in this function
30384 // body may or may not cause an error to be added to this set.
30385 return null;
30386 },
30387 else => switch (ip.indexToKey(set_ty)) {
30388 .error_set_type => |error_set_type| {
30389 if (error_set_type.names.len == 0) return .true;
30357 return err_set: switch (ip.errorUnionSet(operand_ty.toIntern())) {
30358 .anyerror_type => null,
30359 .adhoc_inferred_error_set_type => {
30360 // This is *our* error set; that is, we're currently analyzing the function
30361 // which owns it. Trying to resolve it now would cause a dependency loop.
30362 // Instead, accept that we don't know.
30363 if (true) return null;
30364 },
30365 else => |set_ty| switch (ip.indexToKey(set_ty)) {
30366 .error_set_type => |error_set_type| switch (error_set_type.names.len) {
30367 0 => .true,
30368 else => null,
3039030369 },
30391 .inferred_error_set_type => |func_index| blk: {
30392 // If the error set is empty, we must return a comptime true or false.
30393 // However we want to avoid unnecessarily resolving an inferred error set
30394 // in case it is already non-empty.
30395 try zcu.maybeUnresolveIes(func_index);
30396 switch (ip.funcIesResolvedUnordered(func_index)) {
30397 .anyerror_type => break :blk,
30398 .none => {},
30399 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk,
30400 }
30370 .inferred_error_set_type => |func_index| {
3040130371 if (sema.fn_ret_ty_ies) |ies| {
3040230372 if (ies.func == func_index) {
30403 // Try to avoid resolving inferred error set if possible.
30404 if (ies.errors.count() != 0) return null;
30405 switch (ies.resolved) {
30406 .anyerror_type => return null,
30407 .none => {},
30408 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
30409 0 => return .true,
30410 else => return null,
30411 },
30412 }
30413 // We do not have a comptime answer because this inferred error
30414 // set is not resolved, and an instruction later in this function
30415 // body may or may not cause an error to be added to this set.
30373 // This is *our* error set; that is, we're currently analyzing the function
30374 // which owns it. Trying to resolve it now would cause a dependency loop.
30375 // Instead, accept that we don't know.
3041630376 return null;
3041730377 }
3041830378 }
30419 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty);
30420 if (resolved_ty == .anyerror_type)
30421 break :blk;
30422 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0)
30423 return .true;
30379 try sema.ensureFuncIesResolved(block, src, func_index);
30380 continue :err_set ip.funcIesResolvedUnordered(func_index);
3042430381 },
3042530382 else => unreachable,
3042630383 },
30427 }
30428
30429 return null;
30384 };
3043030385}
3043130386
3043230387fn analyzeIsNonErr(
......@@ -31384,58 +31339,16 @@ fn wrapErrorUnionSet(
3138431339 const pt = sema.pt;
3138531340 const zcu = pt.zcu;
3138631341 const ip = &zcu.intern_pool;
31387 const inst_ty = sema.typeOf(inst);
3138831342 const dest_err_set_ty = dest_ty.errorUnionSet(zcu);
31389 if (sema.resolveValue(inst)) |val| {
31390 const expected_name = zcu.intern_pool.indexToKey(val.toIntern()).err.name;
31391 switch (dest_err_set_ty.toIntern()) {
31392 .anyerror_type => {},
31393 .adhoc_inferred_error_set_type => ok: {
31394 const ies = sema.fn_ret_ty_ies.?;
31395 switch (ies.resolved) {
31396 .anyerror_type => break :ok,
31397 .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) {
31398 break :ok;
31399 },
31400 else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) {
31401 break :ok;
31402 },
31403 }
31404 return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty);
31405 },
31406 else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) {
31407 .error_set_type => |error_set_type| ok: {
31408 if (error_set_type.nameIndex(ip, expected_name) != null) break :ok;
31409 return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty);
31410 },
31411 .inferred_error_set_type => |func_index| ok: {
31412 // We carefully do this in an order that avoids unnecessarily
31413 // resolving the destination error set type.
31414 try zcu.maybeUnresolveIes(func_index);
31415 switch (ip.funcIesResolvedUnordered(func_index)) {
31416 .anyerror_type => break :ok,
31417 .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) {
31418 break :ok;
31419 },
31420 else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) {
31421 break :ok;
31422 },
31423 }
31424
31425 return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty);
31426 },
31427 else => unreachable,
31428 },
31429 }
31430 return Air.internedToRef((try pt.intern(.{ .error_union = .{
31343 const coerced = try sema.coerceExtra(block, dest_err_set_ty, inst, inst_src, .{ .report_err = false });
31344 if (try sema.resolveDefinedValue(block, inst_src, coerced)) |error_val| {
31345 return .fromIntern(try pt.intern(.{ .error_union = .{
3143131346 .ty = dest_ty.toIntern(),
31432 .val = .{ .err_name = expected_name },
31433 } })));
31347 .val = .{ .err_name = ip.indexToKey(error_val.toIntern()).err.name },
31348 } }));
31349 } else {
31350 return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
3143431351 }
31435
31436 try sema.requireRuntimeBlock(block, inst_src, null);
31437 const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src);
31438 return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
3143931352}
3144031353
3144131354fn unionToTag(
......@@ -32969,18 +32882,6 @@ fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike {
3296932882 };
3297032883}
3297132884
32972pub fn resolveIes(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!void {
32973 const pt = sema.pt;
32974 const zcu = pt.zcu;
32975 const ip = &zcu.intern_pool;
32976
32977 if (sema.fn_ret_ty_ies) |ies| {
32978 try sema.resolveInferredErrorSetPtr(block, src, ies);
32979 assert(ies.resolved != .none);
32980 ip.funcIesResolved(sema.func_index).* = ies.resolved;
32981 }
32982}
32983
3298432885fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
3298532886 const pt = sema.pt;
3298632887 if (!ty.isIndexable(pt.zcu)) {
......@@ -33017,63 +32918,31 @@ fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void
3301732918 return sema.failWithOwnedErrorMsg(block, msg);
3301832919}
3301932920
33020/// Returns a normal error set corresponding to the fully populated inferred
33021/// error set.
33022fn resolveInferredErrorSet(
32921/// Resolves the inferred error set of the given function, so that the corresponding concrete error
32922/// set is available by calling `InternPool.funcIesResolvedUnordered` on `func_index`.
32923///
32924/// Asserts that `func_index` is a function. Also asserts that it is not a coerced function, because
32925/// coerced functions do not own inferred error sets.
32926fn ensureFuncIesResolved(
3302332927 sema: *Sema,
3302432928 block: *Block,
3302532929 src: LazySrcLoc,
33026 ies_index: InternPool.Index,
33027) CompileError!InternPool.Index {
32930 func_index: InternPool.Index,
32931) CompileError!void {
3302832932 const pt = sema.pt;
3302932933 const zcu = pt.zcu;
3303032934 const ip = &zcu.intern_pool;
33031 const func_index = ip.iesFuncIndex(ies_index);
33032 const func = zcu.funcInfo(func_index);
3303332935
33034 try sema.declareDependency(.{ .func_ies = func_index });
32936 assert(ip.unwrapCoercedFunc(func_index) == func_index);
3303532937
33036 // MLUGG TODO: this feels kinda bad now... instead check for outdated whenver we grab this?
33037 try zcu.maybeUnresolveIes(func_index);
33038 const resolved_ty = func.resolvedErrorSetUnordered(ip);
33039 if (resolved_ty != .none) return resolved_ty;
32938 try sema.declareDependency(.{ .func_ies = func_index });
32939 try sema.addReferenceEntry(block, src, .wrap(.{ .func = func_index }));
3304032940
3304132941 if (zcu.analysis_in_progress.contains(.wrap(.{ .func = func_index }))) {
3304232942 return sema.fail(block, src, "unable to resolve inferred error set", .{});
3304332943 }
3304432944
33045 // In order to ensure that all dependencies are properly added to the set,
33046 // we need to ensure the function body is analyzed of the inferred error
33047 // set. However, in the case of comptime/inline function calls with
33048 // inferred error sets, each call gets an adhoc InferredErrorSet object, which
33049 // has no corresponding function body.
33050 const ies_func_info = zcu.typeToFunc(.fromInterned(func.ty)).?;
33051 // if ies declared by a inline function with generic return type, the return_type should be generic_poison,
33052 // because inline function does not create a new declaration, and the ies has been filled with analyzeCall,
33053 // so here we can simply skip this case.
33054 if (ies_func_info.return_type == .generic_poison_type) {
33055 assert(ies_func_info.cc == .@"inline");
33056 } else if (ip.errorUnionSet(ies_func_info.return_type) == ies_index) {
33057 if (!Type.fromInterned(func.ty).fnHasRuntimeBits(zcu)) {
33058 return sema.failWithOwnedErrorMsg(block, msg: {
33059 const msg = try sema.errMsg(src, "unable to resolve inferred error set of generic function", .{});
33060 errdefer msg.destroy(sema.gpa);
33061 try sema.errNote(zcu.navSrcLoc(func.owner_nav), msg, "generic function declared here", .{});
33062 break :msg msg;
33063 });
33064 }
33065 // In this case we are dealing with the actual InferredErrorSet object that
33066 // corresponds to the function, not one created to track an inline/comptime call.
33067 const orig_func_index = ip.unwrapCoercedFunc(func_index);
33068 try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_func_index }));
33069 try pt.ensureFuncBodyUpToDate(orig_func_index);
33070 }
33071
33072 // This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody`
33073 // which calls `resolveInferredErrorSetPtr`.
33074 const final_resolved_ty = func.resolvedErrorSetUnordered(ip);
33075 assert(final_resolved_ty != .none);
33076 return final_resolved_ty;
32945 try pt.ensureFuncBodyUpToDate(func_index);
3307732946}
3307832947
3307932948pub fn resolveInferredErrorSetPtr(
......@@ -33091,7 +32960,9 @@ pub fn resolveInferredErrorSetPtr(
3309132960
3309232961 for (ies.inferred_error_sets.keys()) |other_ies_index| {
3309332962 if (ies_index == other_ies_index) continue;
33094 switch (try sema.resolveInferredErrorSet(block, src, other_ies_index)) {
32963 const other_func_index = ip.iesFuncIndex(other_ies_index);
32964 try sema.ensureFuncIesResolved(block, src, other_func_index);
32965 switch (ip.funcIesResolvedUnordered(other_func_index)) {
3309532966 .anyerror_type => {
3309632967 ies.resolved = .anyerror_type;
3309732968 return;
......@@ -33164,7 +33035,10 @@ fn resolveInferredErrorSetTy(
3316433035 if (ty == .anyerror_type) return ty;
3316533036 switch (ip.indexToKey(ty)) {
3316633037 .error_set_type => return ty,
33167 .inferred_error_set_type => return sema.resolveInferredErrorSet(block, src, ty),
33038 .inferred_error_set_type => |func_index| {
33039 try sema.ensureFuncIesResolved(block, src, func_index);
33040 return ip.funcIesResolvedUnordered(func_index);
33041 },
3316833042 else => unreachable,
3316933043 }
3317033044}
src/Sema/type_resolution.zig+6-4
......@@ -132,7 +132,7 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
132132 .src_base_inst = struct_obj.zir_index,
133133 .type_name_ctx = struct_obj.name,
134134 };
135 defer assert(block.instructions.items.len == 0);
135 defer block.instructions.deinit(gpa);
136136
137137 // There may be old field names in here from a previous update.
138138 struct_obj.field_name_map.get(ip).clearRetainingCapacity();
......@@ -452,6 +452,8 @@ fn resolvePackedStructLayout(
452452pub fn resolveStructDefaults(sema: *Sema, struct_ty: Type) CompileError!void {
453453 const pt = sema.pt;
454454 const zcu = pt.zcu;
455 const comp = zcu.comp;
456 const gpa = comp.gpa;
455457 const ip = &zcu.intern_pool;
456458
457459 assert(sema.owner.unwrap().struct_defaults == struct_ty.toIntern());
......@@ -490,7 +492,7 @@ pub fn resolveStructDefaults(sema: *Sema, struct_ty: Type) CompileError!void {
490492 .src_base_inst = struct_obj.zir_index,
491493 .type_name_ctx = struct_obj.name,
492494 };
493 defer assert(block.instructions.items.len == 0);
495 defer block.instructions.deinit(gpa);
494496
495497 return resolveStructDefaultsInner(sema, &block, &struct_obj);
496498}
......@@ -565,7 +567,7 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {
565567 .src_base_inst = union_obj.zir_index,
566568 .type_name_ctx = union_obj.name,
567569 };
568 defer assert(block.instructions.items.len == 0);
570 defer block.instructions.deinit(gpa);
569571
570572 // MLUGG TODO: this is fucking ugly bro
571573 const explicit_enum_tag_ty: ?Type = if (union_obj.is_reified) ty: {
......@@ -1011,7 +1013,7 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void {
10111013 .src_base_inst = tracked_inst,
10121014 .type_name_ctx = enum_obj.name,
10131015 };
1014 defer assert(block.instructions.items.len == 0);
1016 defer block.instructions.deinit(gpa);
10151017
10161018 // There may be old field names in the map from a previous update.
10171019 enum_obj.field_name_map.get(ip).clearRetainingCapacity();
src/Type.zig+6-5
......@@ -1472,14 +1472,15 @@ pub fn isError(ty: Type, zcu: *const Zcu) bool {
14721472/// Returns whether ty, which must be an error set, includes an error `name`.
14731473/// Might return a false negative if `ty` is an inferred error set and not fully
14741474/// resolved yet.
1475pub fn errorSetHasFieldIp(
1476 ip: *const InternPool,
1477 ty: InternPool.Index,
1475pub fn errorSetHasField(
1476 ty: Type,
14781477 name: InternPool.NullTerminatedString,
1478 zcu: *const Zcu,
14791479) bool {
1480 return switch (ty) {
1480 const ip = &zcu.intern_pool;
1481 return switch (ty.toIntern()) {
14811482 .anyerror_type => true,
1482 else => switch (ip.indexToKey(ty)) {
1483 else => switch (ip.indexToKey(ty.toIntern())) {
14831484 .error_set_type => |error_set_type| error_set_type.nameIndex(ip, name) != null,
14841485 .inferred_error_set_type => |i| switch (ip.funcIesResolvedUnordered(i)) {
14851486 .anyerror_type => true,
src/Value.zig+2-2
......@@ -641,9 +641,9 @@ pub fn readFromPackedMemory(
641641 .optional => {
642642 assert(ty.isPtrLikeOptional(zcu));
643643 const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, arena)).toUnsignedInt(zcu);
644 return Value.fromInterned(try pt.intern(.{ .opt = .{
644 return .fromInterned(try pt.intern(.{ .opt = .{
645645 .ty = ty.toIntern(),
646 .val = (try pt.ptrIntValue(ty.childType(zcu), addr)).toIntern(),
646 .val = if (addr == 0) .none else (try pt.ptrIntValue(ty.childType(zcu), addr)).toIntern(),
647647 } }));
648648 },
649649 else => @panic("TODO implement readFromPackedMemory for more types"),
src/Zcu.zig+1-26
......@@ -4059,6 +4059,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R
40594059 implicit_tag: {
40604060 const loaded_union = zcu.typeToUnion(.fromInterned(ty)) orelse break :implicit_tag;
40614061 const tag_ty = loaded_union.enum_tag_type;
4062 if (tag_ty == .none) break :implicit_tag;
40624063 if (ip.indexToKey(tag_ty).enum_type != .generated_union_tag) break :implicit_tag;
40634064 const gop = try types.getOrPut(gpa, tag_ty);
40644065 if (gop.found_existing) break :implicit_tag;
......@@ -4383,32 +4384,6 @@ fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void
43834384 }
43844385}
43854386
4386/// Given the `InternPool.Index` of a function, set its resolved IES to `.none` if it
4387/// may be outdated. `Sema` should do this before ever loading a resolved IES.
4388pub fn maybeUnresolveIes(zcu: *Zcu, func_index: InternPool.Index) !void {
4389 const unit = AnalUnit.wrap(.{ .func = func_index });
4390 if (zcu.outdated.contains(unit) or zcu.potentially_outdated.contains(unit)) {
4391 // We're consulting the resolved IES now, but the function is outdated, so its
4392 // IES may have changed. We have to assume the IES is outdated and set the resolved
4393 // set back to `.none`.
4394 //
4395 // This will cause `PerThread.analyzeFnBody` to mark the IES as outdated when it's
4396 // eventually hit.
4397 //
4398 // Since the IES needs to be resolved, the function body will now definitely need
4399 // re-analysis (even if the IES turns out to be the same!), so mark it as
4400 // definitely-outdated if it's only PO.
4401 if (zcu.potentially_outdated.fetchSwapRemove(unit)) |kv| {
4402 const gpa = zcu.gpa;
4403 try zcu.outdated.putNoClobber(gpa, unit, kv.value);
4404 if (kv.value == 0) {
4405 try zcu.outdated_ready.put(gpa, unit, {});
4406 }
4407 }
4408 zcu.intern_pool.funcSetIesResolved(zcu.comp.io, func_index, .none);
4409 }
4410}
4411
44124387pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enum) {
44134388 ok,
44144389 bad_arch: []const std.Target.Cpu.Arch, // value is allowed archs for cc