Commit 38aae2cb7c

Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
2022-02-28 03:04:26
stage2: peer resolve error sets and unions, add more tests
1 parent bfada7c
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -17635,20 +17635,8 @@ fn resolvePeerTypes(
         if (candidate_ty.eql(chosen_ty))
             continue;
 
-        // If the candidate can coerce into our chosen type, we're done.
-        // If the chosen type can coerce into the candidate, use that.
-        if ((try sema.coerceInMemoryAllowed(block, chosen_ty, candidate_ty, false, target, src, src)) == .ok) {
-            continue;
-        }
-        if ((try sema.coerceInMemoryAllowed(block, candidate_ty, chosen_ty, false, target, src, src)) == .ok) {
-            chosen = candidate;
-            chosen_i = candidate_i + 1;
-            continue;
-        }
-
         const candidate_ty_tag = candidate_ty.zigTypeTag();
         const chosen_ty_tag = chosen_ty.zigTypeTag();
-
         switch (candidate_ty_tag) {
             .NoReturn, .Undefined => continue,
 
@@ -17780,6 +17768,51 @@ fn resolvePeerTypes(
                     chosen_i = candidate_i + 1;
                     continue;
                 }
+
+                // At this point, we must resolve any inferred error sets
+                if (candidate_ty.castTag(.error_set_inferred)) |inferred| {
+                    try sema.resolveInferredErrorSet(inferred.data);
+                }
+
+                // If anything is anyerror, we use anyerror always
+                if (candidate_ty.isAnyError()) {
+                    err_set_ty = candidate_ty;
+                    continue;
+                }
+                if (err_set_ty) |ty|
+                    if (ty.isAnyError()) continue;
+
+                if (err_set_ty == null) {
+                    // Error unions are lazy, we're forced to resolve now.
+                    // Otherwise, our candidate type cause we've never seen
+                    // error sets up to this point
+                    if (chosen_ty_tag == .ErrorUnion) {
+                        err_set_ty = chosen_ty.errorUnionSet();
+
+                        if (err_set_ty.?.castTag(.error_set_inferred)) |inferred| {
+                            try sema.resolveInferredErrorSet(inferred.data);
+                        }
+
+                        if (err_set_ty.?.isAnyError()) continue;
+                    } else {
+                        err_set_ty = candidate_ty;
+                        continue;
+                    }
+                }
+
+                // If previous is superset, keep the previous
+                var prev_is_superset = true;
+                for (candidate_ty.errorSetNames()) |name| {
+                    if (!err_set_ty.?.errorSetHasField(name)) {
+                        prev_is_superset = false;
+                        break;
+                    }
+                }
+                if (prev_is_superset) continue; // use previous
+
+                // Merge
+                err_set_ty = try err_set_ty.?.errorSetMerge(sema.arena, candidate_ty);
+                continue;
             },
             .ErrorUnion => {
                 if (chosen_ty_tag == .ErrorSet) {
@@ -17803,14 +17836,14 @@ fn resolvePeerTypes(
                     // If candidate is a superset of the error type, then use it.
                     var cand_is_superset = true;
                     for (err_set_ty.?.errorSetNames()) |name| {
-                        if (!candidate_ty.errorSetHasField(name)) {
+                        if (!eu_set_ty.errorSetHasField(name)) {
                             cand_is_superset = false;
                             break;
                         }
                     }
                     if (cand_is_superset) {
                         // Swap to candidate
-                        err_set_ty = candidate_ty;
+                        err_set_ty = eu_set_ty;
                         chosen = candidate;
                         chosen_i = candidate_i + 1;
                         continue;
@@ -18085,6 +18118,17 @@ fn resolvePeerTypes(
             else => {},
         }
 
+        // If the candidate can coerce into our chosen type, we're done.
+        // If the chosen type can coerce into the candidate, use that.
+        if ((try sema.coerceInMemoryAllowed(block, chosen_ty, candidate_ty, false, target, src, src)) == .ok) {
+            continue;
+        }
+        if ((try sema.coerceInMemoryAllowed(block, candidate_ty, chosen_ty, false, target, src, src)) == .ok) {
+            chosen = candidate;
+            chosen_i = candidate_i + 1;
+            continue;
+        }
+
         // At this point, we hit a compile error. We need to recover
         // the source locations.
         const chosen_src = candidate_srcs.resolve(
test/behavior/cast.zig
@@ -615,6 +615,96 @@ test "peer type resolution: unreachable, error set, unreachable" {
     try expect(transformed_err == error.SystemResources);
 }
 
+test "peer cast: error set any anyerror" {
+    const a: error{ One, Two } = undefined;
+    const b: anyerror = undefined;
+    try expect(@TypeOf(a, b) == anyerror);
+    try expect(@TypeOf(b, a) == anyerror);
+}
+
+test "peer type resolution: error set supersets" {
+    const a: error{ One, Two } = undefined;
+    const b: error{One} = undefined;
+
+    // A superset of B
+    {
+        const ty = @TypeOf(a, b);
+        const error_set_info = @typeInfo(ty);
+        try expect(error_set_info == .ErrorSet);
+        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"));
+    }
+
+    // B superset of A
+    {
+        const ty = @TypeOf(b, a);
+        const error_set_info = @typeInfo(ty);
+        try expect(error_set_info == .ErrorSet);
+        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 type resolution: disjoint error sets" {
+    const a: error{ One, Two } = undefined;
+    const b: error{Three} = undefined;
+
+    // note: order of error set members doesn't member, may want to sort
+
+    {
+        const ty = @TypeOf(a, b);
+        const error_set_info = @typeInfo(ty);
+        try expect(error_set_info == .ErrorSet);
+        try expect(error_set_info.ErrorSet.?.len == 3);
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two"));
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Three"));
+    }
+
+    {
+        const ty = @TypeOf(b, a);
+        const error_set_info = @typeInfo(ty);
+        try expect(error_set_info == .ErrorSet);
+        try expect(error_set_info.ErrorSet.?.len == 3);
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "Three"));
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "One"));
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two"));
+    }
+}
+
+test "peer type resolution: error union and error set" {
+    const a: error{Three} = 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);
+
+        const error_set_info = @typeInfo(info.ErrorUnion.error_set);
+        try expect(error_set_info.ErrorSet.?.len == 3);
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "Three"));
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "One"));
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two"));
+    }
+
+    {
+        const ty = @TypeOf(b, a);
+        const info = @typeInfo(ty);
+        try expect(info == .ErrorUnion);
+
+        const error_set_info = @typeInfo(info.ErrorUnion.error_set);
+        try expect(error_set_info.ErrorSet.?.len == 3);
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One"));
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two"));
+        try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Three"));
+    }
+}
+
 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