Commit b4ce855788

Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
2022-02-28 04:00:16
stage2: error union and non-error set/union peer cast resolution
1 parent 38aae2c
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -17814,8 +17814,8 @@ fn resolvePeerTypes(
                 err_set_ty = try err_set_ty.?.errorSetMerge(sema.arena, candidate_ty);
                 continue;
             },
-            .ErrorUnion => {
-                if (chosen_ty_tag == .ErrorSet) {
+            .ErrorUnion => switch (chosen_ty_tag) {
+                .ErrorSet => {
                     if (err_set_ty.?.isAnyError()) {
                         chosen = candidate;
                         chosen_i = candidate_i + 1;
@@ -17854,9 +17854,9 @@ fn resolvePeerTypes(
                     chosen = candidate;
                     chosen_i = candidate_i + 1;
                     continue;
-                }
+                },
 
-                if (chosen_ty_tag == .ErrorUnion) {
+                .ErrorUnion => {
                     const chosen_payload_ty = chosen_ty.errorUnionPayload();
                     const candidate_payload_ty = candidate_ty.errorUnionPayload();
 
@@ -17926,30 +17926,57 @@ fn resolvePeerTypes(
                         err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_ty);
                         continue;
                     }
-                }
+                },
 
-                const payload_ty = candidate_ty.errorUnionPayload();
-                if (chosen_ty_tag == .Pointer and
-                    chosen_ty.ptrSize() == .One and
-                    chosen_ty.childType().zigTypeTag() == .Array and
-                    payload_ty.isSlice())
-                {
-                    const chosen_child_ty = chosen_ty.childType();
-                    const chosen_elem_ty = chosen_child_ty.elemType2();
-                    const candidate_elem_ty = payload_ty.elemType2();
-                    if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) {
+                .Pointer => {
+                    const payload_ty = candidate_ty.errorUnionPayload();
+                    if (chosen_ty.ptrSize() == .One and
+                        chosen_ty.childType().zigTypeTag() == .Array and
+                        payload_ty.isSlice())
+                    {
+                        const chosen_child_ty = chosen_ty.childType();
+                        const chosen_elem_ty = chosen_child_ty.elemType2();
+                        const candidate_elem_ty = payload_ty.elemType2();
+                        if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) {
+                            chosen = candidate;
+                            chosen_i = candidate_i + 1;
+
+                            convert_to_slice = false; // it already is a slice
+
+                            // If the prev pointer is const then we need to const
+                            if (chosen_child_ty.isConstPtr())
+                                make_the_slice_const = true;
+
+                            continue;
+                        }
+                    }
+                },
+
+                else => {
+                    // Chosen coercing into payload type
+                    // Then merge error sets (if any)
+                    const payload_ty = candidate_ty.errorUnionPayload();
+                    if ((try sema.coerceInMemoryAllowed(block, payload_ty, chosen_ty, false, target, src, src)) == .ok) {
                         chosen = candidate;
                         chosen_i = candidate_i + 1;
 
-                        convert_to_slice = false; // it already is a slice
+                        if (err_set_ty) |ty| {
+                            const cand_set_ty = candidate_ty.errorUnionSet();
+                            if (cand_set_ty.castTag(.error_set_inferred)) |inferred| {
+                                try sema.resolveInferredErrorSet(inferred.data);
+                            }
+                            if (cand_set_ty.isAnyError()) {
+                                err_set_ty = cand_set_ty;
+                                continue;
+                            }
+                            if (ty.isAnyError()) continue;
 
-                        // If the prev pointer is const then we need to const
-                        if (chosen_child_ty.isConstPtr())
-                            make_the_slice_const = true;
+                            err_set_ty = try err_set_ty.?.errorSetMerge(sema.arena, cand_set_ty);
+                        }
 
                         continue;
                     }
-                }
+                },
             },
             .Pointer => {
                 if (candidate_ty.ptrSize() == .C) {
@@ -18115,6 +18142,12 @@ fn resolvePeerTypes(
                     continue;
                 }
             },
+            .ErrorUnion => {
+                const payload_ty = chosen_ty.errorUnionPayload();
+                if ((try sema.coerceInMemoryAllowed(block, payload_ty, candidate_ty, false, target, src, src)) == .ok) {
+                    continue;
+                }
+            },
             else => {},
         }
 
test/behavior/cast.zig
@@ -705,6 +705,37 @@ test "peer type resolution: error union and error set" {
     }
 }
 
+test "peer type resolution: error union after non-error" {
+    const a: u32 = undefined;
+    const b: error{ One, Two }!u32 = undefined;
+
+    // note: order of error set members doesn't member, may want to sort
+
+    {
+        const ty = @TypeOf(a, b);
+        const info = @typeInfo(ty);
+        try expect(info == .ErrorUnion);
+        try expect(info.ErrorUnion.payload == u32);
+
+        const error_set_info = @typeInfo(info.ErrorUnion.error_set);
+        try expect(error_set_info.ErrorSet.?.len == 2);
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two"));
+    }
+
+    {
+        const ty = @TypeOf(b, a);
+        const info = @typeInfo(ty);
+        try expect(info == .ErrorUnion);
+        try expect(info.ErrorUnion.payload == u32);
+
+        const error_set_info = @typeInfo(info.ErrorUnion.error_set);
+        try expect(error_set_info.ErrorSet.?.len == 2);
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two"));
+    }
+}
+
 test "peer cast *[0]T to E![]const T" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO