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

Resolves: #21414
This commit is contained in:
mlugg 2024-09-15 12:58:39 +01:00 committed by Matthew Lugg
parent 4d81e8ee91
commit 19924ca289
2 changed files with 44 additions and 11 deletions

View File

@ -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, &.{}));
},
}
}
};

View File

@ -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();
}