authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-29 16:39:26-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-29 17:20:23-07:00
log7999374b21795a7b8fac7af0b2e326d39bd1e839
tree14a3344acff87876a43897211912115f592b771d
parente8377659560497beb36ec1f981b024f74292c1b9

Sema: correct OPV for optional empty error set

prevents crashes in backends; improves codegen; provides more comptime-ness.

3 files changed, 52 insertions(+), 5 deletions(-)

src/Sema.zig+17-5
......@@ -9065,10 +9065,14 @@ fn zirOptionalPayload(
90659065 };
90669066
90679067 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
9068 return if (val.optionalValue(zcu)) |payload|
9069 Air.internedToRef(payload.toIntern())
9070 else
9071 sema.fail(block, src, "unable to unwrap null", .{});
9068 if (val.optionalValue(zcu)) |payload| return Air.internedToRef(payload.toIntern());
9069 if (block.isComptime()) return sema.fail(block, src, "unable to unwrap null", .{});
9070 if (safety_check and block.wantSafety()) {
9071 try sema.safetyPanic(block, src, .unwrap_null);
9072 } else {
9073 _ = try block.addNoOp(.unreach);
9074 }
9075 return .unreachable_value;
90729076 }
90739077
90749078 try sema.requireRuntimeBlock(block, src, null);
......@@ -36443,7 +36447,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
3644336447 .type_int_unsigned, // u0 handled above
3644436448 .type_pointer,
3644536449 .type_slice,
36446 .type_optional, // ?noreturn handled above
3644736450 .type_anyframe,
3644836451 .type_error_union,
3644936452 .type_anyerror_union,
......@@ -36655,6 +36658,15 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
3665536658
3665636659 else => unreachable,
3665736660 },
36661
36662 .type_optional => {
36663 const payload_ip = ip.indexToKey(ty.toIntern()).opt_type;
36664 // Although ?noreturn is handled above, the element type
36665 // can be effectively noreturn for example via an empty
36666 // enum or error set.
36667 if (ip.isNoReturn(payload_ip)) return try pt.nullValue(ty);
36668 return null;
36669 },
3665836670 },
3665936671 };
3666036672}
test/cases/compile_errors/optional_empty_error_set.zig created+13
......@@ -0,0 +1,13 @@
1export fn example() void {
2 comptime foo() catch |err| switch (err) {};
3}
4var x: ?error{} = null;
5fn foo() !void {
6 return x.?;
7}
8// error
9// backend=stage2
10// target=native
11//
12// :6:13: error: unable to unwrap null
13// :2:17: note: called at comptime here
test/cases/safety/optional_empty_error_set.zig created+22
......@@ -0,0 +1,22 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, ra: ?usize) noreturn {
4 _ = stack_trace;
5 _ = ra;
6 if (std.mem.eql(u8, message, "attempt to use null value")) {
7 std.process.exit(0);
8 }
9 std.process.exit(1);
10}
11
12pub fn main() !void {
13 foo() catch |err| switch (err) {};
14 return error.TestFailed;
15}
16var x: ?error{} = null;
17fn foo() !void {
18 return x.?;
19}
20// run
21// backend=stage2,llvm
22// target=native