authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-30 15:25:35+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-06-30 15:25:35+02:00
log79db39307bea14c9d43b365535e704d2a86f3b24
treee8e28732fab6a70ca261dbb13654be44a202881e
parente8377659560497beb36ec1f981b024f74292c1b9
parentd9742acc232a2af4f98e0fbb9f749737a126e6d1
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #24297 from ziglang/optional-empty-error-set

Sema: correct OPV for optional empty error set

4 files changed, 80 insertions(+), 20 deletions(-)

src/Air.zig+15-7
...@@ -1141,6 +1141,20 @@ pub const Inst = struct {...@@ -1141,6 +1141,20 @@ pub const Inst = struct {
1141 pub fn toType(ref: Ref) Type {1141 pub fn toType(ref: Ref) Type {
1142 return .fromInterned(ref.toInterned().?);1142 return .fromInterned(ref.toInterned().?);
1143 }1143 }
1144
1145 pub fn fromIntern(ip_index: InternPool.Index) Ref {
1146 return switch (ip_index) {
1147 .none => .none,
1148 else => {
1149 assert(@intFromEnum(ip_index) >> 31 == 0);
1150 return @enumFromInt(@as(u31, @intCast(@intFromEnum(ip_index))));
1151 },
1152 };
1153 }
1154
1155 pub fn fromValue(v: Value) Ref {
1156 return .fromIntern(v.toIntern());
1157 }
1144 };1158 };
11451159
1146 /// All instructions have an 8-byte payload, which is contained within1160 /// All instructions have an 8-byte payload, which is contained within
...@@ -1754,13 +1768,7 @@ pub fn deinit(air: *Air, gpa: std.mem.Allocator) void {...@@ -1754,13 +1768,7 @@ pub fn deinit(air: *Air, gpa: std.mem.Allocator) void {
1754}1768}
17551769
1756pub fn internedToRef(ip_index: InternPool.Index) Inst.Ref {1770pub fn internedToRef(ip_index: InternPool.Index) Inst.Ref {
1757 return switch (ip_index) {1771 return .fromIntern(ip_index);
1758 .none => .none,
1759 else => {
1760 assert(@intFromEnum(ip_index) >> 31 == 0);
1761 return @enumFromInt(@as(u31, @intCast(@intFromEnum(ip_index))));
1762 },
1763 };
1764}1772}
17651773
1766/// Returns `null` if runtime-known.1774/// Returns `null` if runtime-known.
src/Sema.zig+30-13
...@@ -8060,7 +8060,7 @@ fn analyzeCall(...@@ -8060,7 +8060,7 @@ fn analyzeCall(
8060 };8060 };
80618061
8062 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + runtime_args.len);8062 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + runtime_args.len);
8063 const result = try block.addInst(.{8063 const maybe_opv = try block.addInst(.{
8064 .tag = call_tag,8064 .tag = call_tag,
8065 .data = .{ .pl_op = .{8065 .data = .{ .pl_op = .{
8066 .operand = runtime_func,8066 .operand = runtime_func,
...@@ -8072,7 +8072,7 @@ fn analyzeCall(...@@ -8072,7 +8072,7 @@ fn analyzeCall(
8072 sema.appendRefsAssumeCapacity(runtime_args);8072 sema.appendRefsAssumeCapacity(runtime_args);
80738073
8074 if (ensure_result_used) {8074 if (ensure_result_used) {
8075 try sema.ensureResultUsed(block, sema.typeOf(result), call_src);8075 try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src);
8076 }8076 }
80778077
8078 if (call_tag == .call_always_tail) {8078 if (call_tag == .call_always_tail) {
...@@ -8082,10 +8082,10 @@ fn analyzeCall(...@@ -8082,10 +8082,10 @@ fn analyzeCall(
8082 .pointer => func_or_ptr_ty.childType(zcu),8082 .pointer => func_or_ptr_ty.childType(zcu),
8083 else => unreachable,8083 else => unreachable,
8084 };8084 };
8085 return sema.handleTailCall(block, call_src, runtime_func_ty, result);8085 return sema.handleTailCall(block, call_src, runtime_func_ty, maybe_opv);
8086 }8086 }
80878087
8088 if (resolved_ret_ty.toIntern() == .noreturn_type) {8088 if (ip.isNoReturn(resolved_ret_ty.toIntern())) {
8089 const want_check = c: {8089 const want_check = c: {
8090 if (!block.wantSafety()) break :c false;8090 if (!block.wantSafety()) break :c false;
8091 if (func_val != null) break :c false;8091 if (func_val != null) break :c false;
...@@ -8099,6 +8099,11 @@ fn analyzeCall(...@@ -8099,6 +8099,11 @@ fn analyzeCall(
8099 return .unreachable_value;8099 return .unreachable_value;
8100 }8100 }
81018101
8102 const result: Air.Inst.Ref = if (try sema.typeHasOnePossibleValue(sema.typeOf(maybe_opv))) |opv|
8103 .fromValue(opv)
8104 else
8105 maybe_opv;
8106
8102 return result;8107 return result;
8103 }8108 }
81048109
...@@ -8335,7 +8340,7 @@ fn analyzeCall(...@@ -8335,7 +8340,7 @@ fn analyzeCall(
8335 break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope);8340 break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope);
8336 };8341 };
83378342
8338 const result: Air.Inst.Ref = if (try sema.resolveValue(result_raw)) |result_val| r: {8343 const maybe_opv: Air.Inst.Ref = if (try sema.resolveValue(result_raw)) |result_val| r: {
8339 const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern());8344 const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern());
8340 break :r Air.internedToRef(val_resolved);8345 break :r Air.internedToRef(val_resolved);
8341 } else r: {8346 } else r: {
...@@ -8347,7 +8352,7 @@ fn analyzeCall(...@@ -8347,7 +8352,7 @@ fn analyzeCall(
8347 };8352 };
83488353
8349 if (block.isComptime()) {8354 if (block.isComptime()) {
8350 const result_val = (try sema.resolveValue(result)).?;8355 const result_val = (try sema.resolveValue(maybe_opv)).?;
8351 if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) {8356 if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) {
8352 _ = try pt.intern(.{ .memoized_call = .{8357 _ = try pt.intern(.{ .memoized_call = .{
8353 .func = func_val.?.toIntern(),8358 .func = func_val.?.toIntern(),
...@@ -8359,10 +8364,10 @@ fn analyzeCall(...@@ -8359,10 +8364,10 @@ fn analyzeCall(
8359 }8364 }
83608365
8361 if (ensure_result_used) {8366 if (ensure_result_used) {
8362 try sema.ensureResultUsed(block, sema.typeOf(result), call_src);8367 try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src);
8363 }8368 }
83648369
8365 return result;8370 return maybe_opv;
8366}8371}
83678372
8368fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {8373fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
...@@ -9065,10 +9070,14 @@ fn zirOptionalPayload(...@@ -9065,10 +9070,14 @@ fn zirOptionalPayload(
9065 };9070 };
90669071
9067 if (try sema.resolveDefinedValue(block, src, operand)) |val| {9072 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
9068 return if (val.optionalValue(zcu)) |payload|9073 if (val.optionalValue(zcu)) |payload| return Air.internedToRef(payload.toIntern());
9069 Air.internedToRef(payload.toIntern())9074 if (block.isComptime()) return sema.fail(block, src, "unable to unwrap null", .{});
9070 else9075 if (safety_check and block.wantSafety()) {
9071 sema.fail(block, src, "unable to unwrap null", .{});9076 try sema.safetyPanic(block, src, .unwrap_null);
9077 } else {
9078 _ = try block.addNoOp(.unreach);
9079 }
9080 return .unreachable_value;
9072 }9081 }
90739082
9074 try sema.requireRuntimeBlock(block, src, null);9083 try sema.requireRuntimeBlock(block, src, null);
...@@ -36443,7 +36452,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {...@@ -36443,7 +36452,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
36443 .type_int_unsigned, // u0 handled above36452 .type_int_unsigned, // u0 handled above
36444 .type_pointer,36453 .type_pointer,
36445 .type_slice,36454 .type_slice,
36446 .type_optional, // ?noreturn handled above
36447 .type_anyframe,36455 .type_anyframe,
36448 .type_error_union,36456 .type_error_union,
36449 .type_anyerror_union,36457 .type_anyerror_union,
...@@ -36655,6 +36663,15 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {...@@ -36655,6 +36663,15 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
3665536663
36656 else => unreachable,36664 else => unreachable,
36657 },36665 },
36666
36667 .type_optional => {
36668 const payload_ip = ip.indexToKey(ty.toIntern()).opt_type;
36669 // Although ?noreturn is handled above, the element type
36670 // can be effectively noreturn for example via an empty
36671 // enum or error set.
36672 if (ip.isNoReturn(payload_ip)) return try pt.nullValue(ty);
36673 return null;
36674 },
36658 },36675 },
36659 };36676 };
36660}36677}
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