Commit fd1eade4ca

LemonBoy <thatlemon@gmail.com>
2020-02-26 23:17:44
ir: Allow empty inferred error sets
Closes #4564
1 parent 0a88352
Changed files (3)
src
test
stage1
behavior
src/analyze.cpp
@@ -4771,38 +4771,41 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) {
         if (return_err_set_type->data.error_set.infer_fn != nullptr &&
             return_err_set_type->data.error_set.incomplete)
         {
-            ZigType *inferred_err_set_type;
+            // The inferred error set type is null if the function doesn't
+            // return any error
+            ZigType *inferred_err_set_type = nullptr;
+
             if (fn->src_implicit_return_type->id == ZigTypeIdErrorSet) {
                 inferred_err_set_type = fn->src_implicit_return_type;
             } else if (fn->src_implicit_return_type->id == ZigTypeIdErrorUnion) {
                 inferred_err_set_type = fn->src_implicit_return_type->data.error_union.err_set_type;
-            } else {
-                add_node_error(g, return_type_node,
-                        buf_sprintf("function with inferred error set must return at least one possible error"));
-                fn->anal_state = FnAnalStateInvalid;
-                return;
             }
 
-            if (inferred_err_set_type->data.error_set.infer_fn != nullptr &&
-                inferred_err_set_type->data.error_set.incomplete)
-            {
-                if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
-                    fn->anal_state = FnAnalStateInvalid;
-                    return;
+            if (inferred_err_set_type != nullptr) {
+                if (inferred_err_set_type->data.error_set.infer_fn != nullptr &&
+                    inferred_err_set_type->data.error_set.incomplete)
+                {
+                    if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
+                        fn->anal_state = FnAnalStateInvalid;
+                        return;
+                    }
                 }
-            }
 
-            return_err_set_type->data.error_set.incomplete = false;
-            if (type_is_global_error_set(inferred_err_set_type)) {
-                return_err_set_type->data.error_set.err_count = UINT32_MAX;
-            } else {
-                return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count;
-                if (inferred_err_set_type->data.error_set.err_count > 0) {
-                    return_err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(inferred_err_set_type->data.error_set.err_count);
-                    for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) {
-                        return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i];
+                return_err_set_type->data.error_set.incomplete = false;
+                if (type_is_global_error_set(inferred_err_set_type)) {
+                    return_err_set_type->data.error_set.err_count = UINT32_MAX;
+                } else {
+                    return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count;
+                    if (inferred_err_set_type->data.error_set.err_count > 0) {
+                        return_err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(inferred_err_set_type->data.error_set.err_count);
+                        for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) {
+                            return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i];
+                        }
                     }
                 }
+            } else {
+                return_err_set_type->data.error_set.incomplete = false;
+                return_err_set_type->data.error_set.err_count = 0;
             }
         }
     }
test/stage1/behavior/fn.zig
@@ -1,4 +1,7 @@
-const expect = @import("std").testing.expect;
+const std = @import("std");
+const testing = std.testing;
+const expect = testing.expect;
+const expectEqual = testing.expectEqual;
 
 test "params" {
     expect(testParamsAdd(22, 11) == 33);
@@ -272,3 +275,12 @@ test "ability to give comptime types and non comptime types to same parameter" {
     S.doTheTest();
     comptime S.doTheTest();
 }
+
+test "function with inferred error set but returning no error" {
+    const S = struct {
+        fn foo() !void {}
+    };
+
+    const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?;
+    expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len);
+}
test/compile_errors.zig
@@ -2734,16 +2734,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         "tmp.zig:5:5: error: else prong required when switching on type 'anyerror'",
     });
 
-    cases.add("inferred error set with no returned error",
-        \\export fn entry() void {
-        \\    foo() catch unreachable;
-        \\}
-        \\fn foo() !void {
-        \\}
-    , &[_][]const u8{
-        "tmp.zig:4:11: error: function with inferred error set must return at least one possible error",
-    });
-
     cases.add("error not handled in switch",
         \\export fn entry() void {
         \\    foo(452) catch |err| switch (err) {