Skip to content

Commit 79db393

Browse files
authored
Merge pull request #24297 from ziglang/optional-empty-error-set
Sema: correct OPV for optional empty error set
2 parents e837765 + d9742ac commit 79db393

File tree

4 files changed

+80
-20
lines changed

4 files changed

+80
-20
lines changed

src/Air.zig

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,20 @@ pub const Inst = struct {
11411141
pub fn toType(ref: Ref) Type {
11421142
return .fromInterned(ref.toInterned().?);
11431143
}
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+
}
11441158
};
11451159

11461160
/// 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 {
17541768
}
17551769

17561770
pub fn internedToRef(ip_index: InternPool.Index) Inst.Ref {
1757-
return switch (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-
};
1771+
return .fromIntern(ip_index);
17641772
}
17651773

17661774
/// Returns `null` if runtime-known.

src/Sema.zig

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8060,7 +8060,7 @@ fn analyzeCall(
80608060
};
80618061

80628062
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(.{
80648064
.tag = call_tag,
80658065
.data = .{ .pl_op = .{
80668066
.operand = runtime_func,
@@ -8072,7 +8072,7 @@ fn analyzeCall(
80728072
sema.appendRefsAssumeCapacity(runtime_args);
80738073

80748074
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);
80768076
}
80778077

80788078
if (call_tag == .call_always_tail) {
@@ -8082,10 +8082,10 @@ fn analyzeCall(
80828082
.pointer => func_or_ptr_ty.childType(zcu),
80838083
else => unreachable,
80848084
};
8085-
return sema.handleTailCall(block, call_src, runtime_func_ty, result);
8085+
return sema.handleTailCall(block, call_src, runtime_func_ty, maybe_opv);
80868086
}
80878087

8088-
if (resolved_ret_ty.toIntern() == .noreturn_type) {
8088+
if (ip.isNoReturn(resolved_ret_ty.toIntern())) {
80898089
const want_check = c: {
80908090
if (!block.wantSafety()) break :c false;
80918091
if (func_val != null) break :c false;
@@ -8099,6 +8099,11 @@ fn analyzeCall(
80998099
return .unreachable_value;
81008100
}
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+
81028107
return result;
81038108
}
81048109

@@ -8335,7 +8340,7 @@ fn analyzeCall(
83358340
break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope);
83368341
};
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: {
83398344
const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern());
83408345
break :r Air.internedToRef(val_resolved);
83418346
} else r: {
@@ -8347,7 +8352,7 @@ fn analyzeCall(
83478352
};
83488353

83498354
if (block.isComptime()) {
8350-
const result_val = (try sema.resolveValue(result)).?;
8355+
const result_val = (try sema.resolveValue(maybe_opv)).?;
83518356
if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) {
83528357
_ = try pt.intern(.{ .memoized_call = .{
83538358
.func = func_val.?.toIntern(),
@@ -8359,10 +8364,10 @@ fn analyzeCall(
83598364
}
83608365

83618366
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);
83638368
}
83648369

8365-
return result;
8370+
return maybe_opv;
83668371
}
83678372

83688373
fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
@@ -9065,10 +9070,14 @@ fn zirOptionalPayload(
90659070
};
90669071

90679072
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", .{});
9073+
if (val.optionalValue(zcu)) |payload| return Air.internedToRef(payload.toIntern());
9074+
if (block.isComptime()) return sema.fail(block, src, "unable to unwrap null", .{});
9075+
if (safety_check and block.wantSafety()) {
9076+
try sema.safetyPanic(block, src, .unwrap_null);
9077+
} else {
9078+
_ = try block.addNoOp(.unreach);
9079+
}
9080+
return .unreachable_value;
90729081
}
90739082

90749083
try sema.requireRuntimeBlock(block, src, null);
@@ -36443,7 +36452,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
3644336452
.type_int_unsigned, // u0 handled above
3644436453
.type_pointer,
3644536454
.type_slice,
36446-
.type_optional, // ?noreturn handled above
3644736455
.type_anyframe,
3644836456
.type_error_union,
3644936457
.type_anyerror_union,
@@ -36655,6 +36663,15 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
3665536663

3665636664
else => unreachable,
3665736665
},
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+
},
3665836675
},
3665936676
};
3666036677
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export fn example() void {
2+
comptime foo() catch |err| switch (err) {};
3+
}
4+
var x: ?error{} = null;
5+
fn 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
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const std = @import("std");
2+
3+
pub 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+
12+
pub fn main() !void {
13+
foo() catch |err| switch (err) {};
14+
return error.TestFailed;
15+
}
16+
var x: ?error{} = null;
17+
fn foo() !void {
18+
return x.?;
19+
}
20+
// run
21+
// backend=stage2,llvm
22+
// target=native

0 commit comments

Comments
 (0)