Commit 19924ca289

mlugg <mlugg@mlugg.co.uk>
2024-09-15 13:58:39
Sema: give `try` operand `error{}` result type in non-errorable functions
Resolves: #21414
1 parent 4d81e8e
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -4487,7 +4487,7 @@ fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: boo
         break :ty operand_ty.childType(zcu);
     } else operand_ty;
 
-    const err_set_ty = err_set: {
+    const err_set_ty: Type = err_set: {
         // There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals
         // until we hit an error union or set.
         var cur_ty = sema.fn_ret_ty;
@@ -4496,16 +4496,12 @@ fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: boo
                 .error_set => break :err_set cur_ty,
                 .error_union => break :err_set cur_ty.errorUnionSet(zcu),
                 .optional => cur_ty = cur_ty.optionalChild(zcu),
-                else => return sema.failWithOwnedErrorMsg(block, msg: {
-                    const msg = try sema.errMsg(src, "expected '{}', found error set", .{sema.fn_ret_ty.fmt(pt)});
-                    errdefer msg.destroy(sema.gpa);
-                    const ret_ty_src: LazySrcLoc = .{
-                        .base_node_inst = sema.getOwnerFuncDeclInst(),
-                        .offset = .{ .node_offset_fn_type_ret_ty = 0 },
-                    };
-                    try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{});
-                    break :msg msg;
-                }),
+                else => {
+                    // This function cannot return an error.
+                    // `try` is still valid if the error case is impossible, i.e. no error is returned.
+                    // So, the result type has an error set of `error{}`.
+                    break :err_set .fromInterned(try zcu.intern_pool.getErrorSetType(zcu.gpa, pt.tid, &.{}));
+                },
             }
         }
     };
test/behavior/try.zig
@@ -86,3 +86,40 @@ test "try forwards result location" {
     try expect((S.foo(false) catch return error.TestUnexpectedResult) == 123);
     try std.testing.expectError(error.Foo, S.foo(true));
 }
+
+test "'return try' of empty error set in function returning non-error" {
+    if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
+
+    const S = struct {
+        fn succeed0() error{}!u32 {
+            return 123;
+        }
+        fn succeed1() !u32 {
+            return 456;
+        }
+        fn tryNoError0() u32 {
+            return try succeed0();
+        }
+        fn tryNoError1() u32 {
+            return try succeed1();
+        }
+        fn tryNoError2() u32 {
+            const e: error{}!u32 = 789;
+            return try e;
+        }
+        fn doTheTest() !void {
+            const res0 = tryNoError0();
+            const res1 = tryNoError1();
+            const res2 = tryNoError2();
+            try expect(res0 == 123);
+            try expect(res1 == 456);
+            try expect(res2 == 789);
+        }
+    };
+    try S.doTheTest();
+    try comptime S.doTheTest();
+}