authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-15 12:58:39+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-15 16:51:26+01:00
log19924ca2890964b411362c423dd9f4b10596a18f
tree0aa9fd860a4518b51d9595c75245797dcde95da2
parent4d81e8ee915c3e012131cf90ed87cc8c6a01a934

Sema: give `try` operand `error{}` result type in non-errorable functions

Resolves: #21414

2 files changed, 44 insertions(+), 11 deletions(-)

src/Sema.zig+7-11
......@@ -4487,7 +4487,7 @@ fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: boo
44874487 break :ty operand_ty.childType(zcu);
44884488 } else operand_ty;
44894489
4490 const err_set_ty = err_set: {
4490 const err_set_ty: Type = err_set: {
44914491 // There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals
44924492 // until we hit an error union or set.
44934493 var cur_ty = sema.fn_ret_ty;
......@@ -4496,16 +4496,12 @@ fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: boo
44964496 .error_set => break :err_set cur_ty,
44974497 .error_union => break :err_set cur_ty.errorUnionSet(zcu),
44984498 .optional => cur_ty = cur_ty.optionalChild(zcu),
4499 else => return sema.failWithOwnedErrorMsg(block, msg: {
4500 const msg = try sema.errMsg(src, "expected '{}', found error set", .{sema.fn_ret_ty.fmt(pt)});
4501 errdefer msg.destroy(sema.gpa);
4502 const ret_ty_src: LazySrcLoc = .{
4503 .base_node_inst = sema.getOwnerFuncDeclInst(),
4504 .offset = .{ .node_offset_fn_type_ret_ty = 0 },
4505 };
4506 try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{});
4507 break :msg msg;
4508 }),
4499 else => {
4500 // This function cannot return an error.
4501 // `try` is still valid if the error case is impossible, i.e. no error is returned.
4502 // So, the result type has an error set of `error{}`.
4503 break :err_set .fromInterned(try zcu.intern_pool.getErrorSetType(zcu.gpa, pt.tid, &.{}));
4504 },
45094505 }
45104506 }
45114507 };
test/behavior/try.zig+37
......@@ -86,3 +86,40 @@ test "try forwards result location" {
8686 try expect((S.foo(false) catch return error.TestUnexpectedResult) == 123);
8787 try std.testing.expectError(error.Foo, S.foo(true));
8888}
89
90test "'return try' of empty error set in function returning non-error" {
91 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
92 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
93 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
94 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
95 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
96
97 const S = struct {
98 fn succeed0() error{}!u32 {
99 return 123;
100 }
101 fn succeed1() !u32 {
102 return 456;
103 }
104 fn tryNoError0() u32 {
105 return try succeed0();
106 }
107 fn tryNoError1() u32 {
108 return try succeed1();
109 }
110 fn tryNoError2() u32 {
111 const e: error{}!u32 = 789;
112 return try e;
113 }
114 fn doTheTest() !void {
115 const res0 = tryNoError0();
116 const res1 = tryNoError1();
117 const res2 = tryNoError2();
118 try expect(res0 == 123);
119 try expect(res1 == 456);
120 try expect(res2 == 789);
121 }
122 };
123 try S.doTheTest();
124 try comptime S.doTheTest();
125}