Skip to content

Commit db0f372

Browse files
committed
Sema: make optional noreturn behave correctly
1 parent a12abc6 commit db0f372

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

src/Sema.zig

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6684,8 +6684,13 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
66846684
defer tracy.end();
66856685

66866686
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
6687-
const src = inst_data.src();
6688-
const child_type = try sema.resolveType(block, src, inst_data.operand);
6687+
const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
6688+
const child_type = try sema.resolveType(block, operand_src, inst_data.operand);
6689+
if (child_type.zigTypeTag() == .Opaque) {
6690+
return sema.fail(block, operand_src, "opaque type '{}' cannot be optional", .{child_type.fmt(sema.mod)});
6691+
} else if (child_type.zigTypeTag() == .Null) {
6692+
return sema.fail(block, operand_src, "type '{}' cannot be optional", .{child_type.fmt(sema.mod)});
6693+
}
66896694
const opt_type = try Type.optional(sema.arena, child_type);
66906695

66916696
return sema.addType(opt_type);
@@ -25714,6 +25719,12 @@ fn analyzeIsNull(
2571425719
return Air.Inst.Ref.bool_false;
2571525720
}
2571625721
}
25722+
25723+
const operand_ty = sema.typeOf(operand);
25724+
var buf: Type.Payload.ElemType = undefined;
25725+
if (operand_ty.zigTypeTag() == .Optional and operand_ty.optionalChild(&buf).zigTypeTag() == .NoReturn) {
25726+
return Air.Inst.Ref.bool_true;
25727+
}
2571725728
try sema.requireRuntimeBlock(block, src, null);
2571825729
const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
2571925730
return block.addUnOp(air_tag, operand);

test/behavior/optional.zig

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,3 +369,39 @@ test "optional pointer to zero bit error union payload" {
369369
some.foo();
370370
} else |_| {}
371371
}
372+
373+
const NoReturn = struct {
374+
var a: u32 = undefined;
375+
fn someData() bool {
376+
a -= 1;
377+
return a == 0;
378+
}
379+
fn loop() ?noreturn {
380+
while (true) {
381+
if (someData()) return null;
382+
}
383+
}
384+
fn testOrelse() u32 {
385+
loop() orelse return 123;
386+
@compileError("bad");
387+
}
388+
};
389+
390+
test "optional of noreturn used with if" {
391+
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
392+
393+
NoReturn.a = 64;
394+
if (NoReturn.loop()) |_| {
395+
@compileError("bad");
396+
} else {
397+
try expect(true);
398+
}
399+
}
400+
401+
test "optional of noreturn used with orelse" {
402+
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
403+
404+
NoReturn.a = 64;
405+
const val = NoReturn.testOrelse();
406+
try expect(val == 123);
407+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
comptime {
2+
_ = ?anyopaque;
3+
}
4+
comptime {
5+
_ = ?@TypeOf(null);
6+
}
7+
8+
// error
9+
// backend=stage2
10+
// target=native
11+
//
12+
// :2:10: error: opaque type 'anyopaque' cannot be optional
13+
// :5:10: error: type '@TypeOf(null)' cannot be optional

0 commit comments

Comments
 (0)