authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-17 13:10:58+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-17 20:10:18+03:00
logdb0f372da8b25f4a911cd8e0b7f8e5cdfc64f940
tree1258577ea70114ad891b917d25eecc0cf958d19b
parenta12abc6d6c8b89a09befdcbd9019247ccc3bd641

Sema: make optional noreturn behave correctly


3 files changed, 62 insertions(+), 2 deletions(-)

src/Sema.zig+13-2
...@@ -6684,8 +6684,13 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro...@@ -6684,8 +6684,13 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
6684 defer tracy.end();6684 defer tracy.end();
66856685
6686 const inst_data = sema.code.instructions.items(.data)[inst].un_node;6686 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
6687 const src = inst_data.src();6687 const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
6688 const child_type = try sema.resolveType(block, src, inst_data.operand);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 }
6689 const opt_type = try Type.optional(sema.arena, child_type);6694 const opt_type = try Type.optional(sema.arena, child_type);
66906695
6691 return sema.addType(opt_type);6696 return sema.addType(opt_type);
...@@ -25714,6 +25719,12 @@ fn analyzeIsNull(...@@ -25714,6 +25719,12 @@ fn analyzeIsNull(
25714 return Air.Inst.Ref.bool_false;25719 return Air.Inst.Ref.bool_false;
25715 }25720 }
25716 }25721 }
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 }
25717 try sema.requireRuntimeBlock(block, src, null);25728 try sema.requireRuntimeBlock(block, src, null);
25718 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;25729 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
25719 return block.addUnOp(air_tag, operand);25730 return block.addUnOp(air_tag, operand);
test/behavior/optional.zig+36
...@@ -369,3 +369,39 @@ test "optional pointer to zero bit error union payload" {...@@ -369,3 +369,39 @@ test "optional pointer to zero bit error union payload" {
369 some.foo();369 some.foo();
370 } else |_| {}370 } else |_| {}
371}371}
372
373const 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
390test "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
401test "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}
test/cases/compile_errors/invalid_optional_payload_type.zig created+13
...@@ -0,0 +1,13 @@
1comptime {
2 _ = ?anyopaque;
3}
4comptime {
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