From 19924ca2890964b411362c423dd9f4b10596a18f Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 15 Sep 2024 12:58:39 +0100 Subject: [PATCH] Sema: give `try` operand `error{}` result type in non-errorable functions Resolves: #21414 --- src/Sema.zig | 18 +++++++----------- test/behavior/try.zig | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 61501fa455..433429f92a 100644 --- a/src/Sema.zig +++ b/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, &.{})); + }, } } }; diff --git a/test/behavior/try.zig b/test/behavior/try.zig index f17133fabe..3e66582aaa 100644 --- a/test/behavior/try.zig +++ b/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(); +}