Commit 5aa9628de3

Veikka Tuominen <git@vexu.eu>
2023-05-12 13:59:26
Sema: handle recursive inferred errors better in analyzeIsNonErrComptimeOnly
Closes #15669
1 parent 0958d5d
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -29153,8 +29153,6 @@ fn analyzeIsNonErrComptimeOnly(
             if (ies.errors.count() != 0) break :blk;
             if (maybe_operand_val == null) {
                 // Try to avoid resolving inferred error set if possible.
-                if (ies.errors.count() != 0) break :blk;
-                if (ies.is_anyerror) break :blk;
                 for (ies.inferred_error_sets.keys()) |other_ies| {
                     if (ies == other_ies) continue;
                     try sema.resolveInferredErrorSet(block, src, other_ies);
@@ -29166,11 +29164,10 @@ fn analyzeIsNonErrComptimeOnly(
 
                     if (other_ies.errors.count() != 0) break :blk;
                 }
-                if (ies.func == sema.owner_func) {
-                    // We're checking the inferred errorset of the current function and none of
-                    // its child inferred error sets contained any errors meaning that any value
-                    // so far with this type can't contain errors either.
-                    return Air.Inst.Ref.bool_true;
+                if (!ies.is_resolved and ies.func.state == .in_progress) {
+                    // Calling resolveInferredErrorSet would immediately fail
+                    // so we'll have to rely on runtime checks.
+                    return Air.Inst.Ref.none;
                 }
                 try sema.resolveInferredErrorSet(block, src, ies);
                 if (ies.is_anyerror) break :blk;
test/behavior/error.zig
@@ -678,22 +678,6 @@ test "error union payload is properly aligned" {
     if (blk.a != 1) unreachable;
 }
 
-test "ret_ptr doesn't cause own inferred error set to be resolved" {
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
-
-    const S = struct {
-        fn foo() !void {}
-
-        fn doTheTest() !void {
-            errdefer @compileError("bad");
-
-            return try @This().foo();
-        }
-    };
-    try S.doTheTest();
-}
-
 test "simple else prong allowed even when all errors handled" {
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -889,3 +873,28 @@ test "optional error set return type" {
     try expect(null == S.foo(true));
     try expect(E.A == S.foo(false).?);
 }
+
+test "try used in recursive function with inferred error set" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+    const Value = union(enum) {
+        values: []const @This(),
+        b,
+
+        fn x(value: @This()) !void {
+            switch (value.values[0]) {
+                .values => return try x(value.values[0]),
+                .b => return error.a,
+            }
+        }
+    };
+    const a = Value{
+        .values = &[1]Value{
+            .{
+                .values = &[1]Value{.{ .b = {} }},
+            },
+        },
+    };
+    try expectError(error.a, Value.x(a));
+}