authorgravatar for mitchell.hashimoto@gmail.comMitchell Hashimoto <mitchell.hashimoto@gmail.com> 2022-02-27 17:30:15-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-28 20:42:13-07:00
logbfada7c5144ce7f99ab92a76f65ca310440a2df9
treecbce207709d897d0a64583f40e33ea82d0c07fd0
parentd5131e91eba9324eda3a2ae47eb2aa4530c87e83

stage2: implement peer type resolution between error unions


3 files changed, 218 insertions(+), 2 deletions(-)

src/Sema.zig+201
...@@ -17610,6 +17610,21 @@ fn resolvePeerTypes(...@@ -17610,6 +17610,21 @@ fn resolvePeerTypes(
17610 const target = sema.mod.getTarget();17610 const target = sema.mod.getTarget();
1761117611
17612 var chosen = instructions[0];17612 var chosen = instructions[0];
17613 var err_set_ty: ?Type = blk: {
17614 const chosen_ty = sema.typeOf(chosen);
17615
17616 // TODO: is this the right handling of generic poison?
17617 if (chosen_ty.tag() == .generic_poison or chosen_ty.zigTypeTag() != .ErrorSet)
17618 break :blk null;
17619
17620 // If our chosen type is inferred, we have to resolve it now.
17621 if (chosen_ty.castTag(.error_set_inferred)) |inferred| {
17622 try sema.resolveInferredErrorSet(inferred.data);
17623 }
17624
17625 break :blk chosen_ty;
17626 };
17627
17613 var any_are_null = false;17628 var any_are_null = false;
17614 var make_the_slice_const = false;17629 var make_the_slice_const = false;
17615 var convert_to_slice = false;17630 var convert_to_slice = false;
...@@ -17711,7 +17726,175 @@ fn resolvePeerTypes(...@@ -17711,7 +17726,175 @@ fn resolvePeerTypes(
17711 },17726 },
17712 else => {},17727 else => {},
17713 },17728 },
17729 .ErrorSet => {
17730 if (chosen_ty_tag == .ErrorSet) {
17731 assert(err_set_ty != null);
17732
17733 // If chosen type is anyerror, then we can use the prev type
17734 if (err_set_ty.?.isAnyError()) continue;
17735
17736 // At this point, we must resolve any inferred error sets
17737 if (candidate_ty.castTag(.error_set_inferred)) |inferred| {
17738 try sema.resolveInferredErrorSet(inferred.data);
17739 }
17740
17741 // If candidate is anyerror then we use it because it
17742 // is trivially a supserset of previous error set
17743 if (candidate_ty.isAnyError()) {
17744 err_set_ty = candidate_ty;
17745 chosen = candidate;
17746 chosen_i = candidate_i + 1;
17747 continue;
17748 }
17749
17750 // If chosen is superset of candidate, keep it.
17751 // If candidate is superset of chosen, switch it.
17752 // If neither is a superset, merge errors.
17753 var prev_is_superset = true;
17754 for (candidate_ty.errorSetNames()) |name| {
17755 if (!err_set_ty.?.errorSetHasField(name)) {
17756 prev_is_superset = false;
17757 break;
17758 }
17759 }
17760 if (prev_is_superset) continue; // use previous
17761
17762 var cand_is_superset = true;
17763 for (err_set_ty.?.errorSetNames()) |name| {
17764 if (!candidate_ty.errorSetHasField(name)) {
17765 cand_is_superset = false;
17766 break;
17767 }
17768 }
17769 if (cand_is_superset) {
17770 // Swap to candidate
17771 err_set_ty = candidate_ty;
17772 chosen = candidate;
17773 chosen_i = candidate_i + 1;
17774 continue;
17775 }
17776
17777 // Merge errors
17778 err_set_ty = try err_set_ty.?.errorSetMerge(sema.arena, candidate_ty);
17779 chosen = candidate;
17780 chosen_i = candidate_i + 1;
17781 continue;
17782 }
17783 },
17714 .ErrorUnion => {17784 .ErrorUnion => {
17785 if (chosen_ty_tag == .ErrorSet) {
17786 if (err_set_ty.?.isAnyError()) {
17787 chosen = candidate;
17788 chosen_i = candidate_i + 1;
17789 continue;
17790 }
17791
17792 const eu_set_ty = candidate_ty.errorUnionSet();
17793 if (eu_set_ty.castTag(.error_set_inferred)) |inferred| {
17794 try sema.resolveInferredErrorSet(inferred.data);
17795 }
17796 if (eu_set_ty.isAnyError()) {
17797 err_set_ty = eu_set_ty;
17798 chosen = candidate;
17799 chosen_i = candidate_i + 1;
17800 continue;
17801 }
17802
17803 // If candidate is a superset of the error type, then use it.
17804 var cand_is_superset = true;
17805 for (err_set_ty.?.errorSetNames()) |name| {
17806 if (!candidate_ty.errorSetHasField(name)) {
17807 cand_is_superset = false;
17808 break;
17809 }
17810 }
17811 if (cand_is_superset) {
17812 // Swap to candidate
17813 err_set_ty = candidate_ty;
17814 chosen = candidate;
17815 chosen_i = candidate_i + 1;
17816 continue;
17817 }
17818
17819 // Not a superset, create merged error set
17820 err_set_ty = try err_set_ty.?.errorSetMerge(sema.arena, eu_set_ty);
17821 chosen = candidate;
17822 chosen_i = candidate_i + 1;
17823 continue;
17824 }
17825
17826 if (chosen_ty_tag == .ErrorUnion) {
17827 const chosen_payload_ty = chosen_ty.errorUnionPayload();
17828 const candidate_payload_ty = candidate_ty.errorUnionPayload();
17829
17830 const coerce_chosen = (try sema.coerceInMemoryAllowed(block, chosen_payload_ty, candidate_payload_ty, false, target, src, src)) == .ok;
17831 const coerce_candidate = (try sema.coerceInMemoryAllowed(block, candidate_payload_ty, chosen_payload_ty, false, target, src, src)) == .ok;
17832
17833 if (coerce_chosen or coerce_candidate) {
17834 // If we can coerce to the candidate, we switch to that
17835 // type. This is the same logic as the bare (non-union)
17836 // coercion check we do at the top of this func.
17837 if (coerce_candidate) {
17838 chosen = candidate;
17839 chosen_i = candidate_i + 1;
17840 }
17841
17842 const chosen_set_ty = chosen_ty.errorUnionSet();
17843 const candidate_set_ty = chosen_ty.errorUnionSet();
17844
17845 // If our error sets match already, then we are done.
17846 if (chosen_set_ty.eql(candidate_set_ty)) continue;
17847
17848 // They don't match, so we need to figure out if we
17849 // need to merge them, use the superset, etc. This
17850 // requires resolution.
17851 if (chosen_set_ty.castTag(.error_set_inferred)) |inferred| {
17852 try sema.resolveInferredErrorSet(inferred.data);
17853 }
17854 if (candidate_set_ty.castTag(.error_set_inferred)) |inferred| {
17855 try sema.resolveInferredErrorSet(inferred.data);
17856 }
17857
17858 if (chosen_set_ty.isAnyError()) {
17859 err_set_ty = chosen_set_ty;
17860 continue;
17861 }
17862
17863 if (candidate_set_ty.isAnyError()) {
17864 err_set_ty = candidate_set_ty;
17865 continue;
17866 }
17867
17868 if (err_set_ty == null) err_set_ty = chosen_set_ty;
17869
17870 // If the previous error set type is a superset, we're done.
17871 var prev_is_superset = true;
17872 for (candidate_set_ty.errorSetNames()) |name| {
17873 if (!chosen_set_ty.errorSetHasField(name)) {
17874 prev_is_superset = false;
17875 break;
17876 }
17877 }
17878 if (prev_is_superset) continue; // use previous
17879
17880 var cand_is_superset = true;
17881 for (chosen_set_ty.errorSetNames()) |name| {
17882 if (!candidate_set_ty.errorSetHasField(name)) {
17883 cand_is_superset = false;
17884 break;
17885 }
17886 }
17887 if (cand_is_superset) {
17888 err_set_ty = candidate_ty;
17889 continue;
17890 }
17891
17892 // Merge errors
17893 err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_ty);
17894 continue;
17895 }
17896 }
17897
17715 const payload_ty = candidate_ty.errorUnionPayload();17898 const payload_ty = candidate_ty.errorUnionPayload();
17716 if (chosen_ty_tag == .Pointer and17899 if (chosen_ty_tag == .Pointer and
17717 chosen_ty.ptrSize() == .One and17900 chosen_ty.ptrSize() == .One and
...@@ -17962,6 +18145,24 @@ fn resolvePeerTypes(...@@ -17962,6 +18145,24 @@ fn resolvePeerTypes(
17962 return Type.ptr(sema.arena, target, info.data);18145 return Type.ptr(sema.arena, target, info.data);
17963 }18146 }
1796418147
18148 if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag()) {
18149 .ErrorSet => return ty,
18150
18151 .ErrorUnion => {
18152 const payload_ty = chosen_ty.errorUnionPayload();
18153 return try Module.errorUnionType(sema.arena, ty, payload_ty);
18154 },
18155
18156 .ComptimeInt, .ComptimeFloat => return sema.fail(block, src, "unable to make error union out of number literal", .{}),
18157
18158 .Null => return sema.fail(block, src, "unable to make error union out of null literal", .{}),
18159
18160 else => {
18161 // Create error union of our error set and the chosen type
18162 return try Module.errorUnionType(sema.arena, ty, chosen_ty);
18163 },
18164 };
18165
17965 return chosen_ty;18166 return chosen_ty;
17966}18167}
1796718168
src/type.zig+17
...@@ -4216,6 +4216,23 @@ pub const Type = extern union {...@@ -4216,6 +4216,23 @@ pub const Type = extern union {
4216 };4216 };
4217 }4217 }
42184218
4219 /// Merge ty with ty2.
4220 /// Asserts that ty and ty2 are both error sets and are resolved.
4221 pub fn errorSetMerge(ty: Type, arena: Allocator, ty2: Type) !Type {
4222 const lhs_names = ty.errorSetNames();
4223 const rhs_names = ty2.errorSetNames();
4224 var names = Module.ErrorSet.NameMap{};
4225 try names.ensureUnusedCapacity(arena, @intCast(u32, lhs_names.len + rhs_names.len));
4226 for (lhs_names) |name| {
4227 names.putAssumeCapacityNoClobber(name, {});
4228 }
4229 for (rhs_names) |name| {
4230 names.putAssumeCapacity(name, {});
4231 }
4232
4233 return try Tag.error_set_merged.create(arena, names);
4234 }
4235
4219 pub fn enumFields(ty: Type) Module.EnumFull.NameMap {4236 pub fn enumFields(ty: Type) Module.EnumFull.NameMap {
4220 return switch (ty.tag()) {4237 return switch (ty.tag()) {
4221 .enum_full, .enum_nonexhaustive => ty.cast(Payload.EnumFull).?.data.fields,4238 .enum_full, .enum_nonexhaustive => ty.cast(Payload.EnumFull).?.data.fields,
test/behavior/error.zig-2
...@@ -264,8 +264,6 @@ fn testErrToIntWithOnePossibleValue(...@@ -264,8 +264,6 @@ fn testErrToIntWithOnePossibleValue(
264}264}
265265
266test "error union peer type resolution" {266test "error union peer type resolution" {
267 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
268
269 try testErrorUnionPeerTypeResolution(1);267 try testErrorUnionPeerTypeResolution(1);
270}268}
271269