From db0f372da8b25f4a911cd8e0b7f8e5cdfc64f940 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 17 Aug 2022 13:10:58 +0300 Subject: [PATCH] Sema: make optional noreturn behave correctly --- src/Sema.zig | 15 ++++++-- test/behavior/optional.zig | 36 +++++++++++++++++++ .../invalid_optional_payload_type.zig | 13 +++++++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 test/cases/compile_errors/invalid_optional_payload_type.zig diff --git a/src/Sema.zig b/src/Sema.zig index d7d6994bcd..ceeb8af23c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6684,8 +6684,13 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); - const child_type = try sema.resolveType(block, src, inst_data.operand); + const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; + const child_type = try sema.resolveType(block, operand_src, inst_data.operand); + if (child_type.zigTypeTag() == .Opaque) { + return sema.fail(block, operand_src, "opaque type '{}' cannot be optional", .{child_type.fmt(sema.mod)}); + } else if (child_type.zigTypeTag() == .Null) { + return sema.fail(block, operand_src, "type '{}' cannot be optional", .{child_type.fmt(sema.mod)}); + } const opt_type = try Type.optional(sema.arena, child_type); return sema.addType(opt_type); @@ -25714,6 +25719,12 @@ fn analyzeIsNull( return Air.Inst.Ref.bool_false; } } + + const operand_ty = sema.typeOf(operand); + var buf: Type.Payload.ElemType = undefined; + if (operand_ty.zigTypeTag() == .Optional and operand_ty.optionalChild(&buf).zigTypeTag() == .NoReturn) { + return Air.Inst.Ref.bool_true; + } try sema.requireRuntimeBlock(block, src, null); const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; return block.addUnOp(air_tag, operand); diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 4e5eb5061c..c13a3b7e4f 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -369,3 +369,39 @@ test "optional pointer to zero bit error union payload" { some.foo(); } else |_| {} } + +const NoReturn = struct { + var a: u32 = undefined; + fn someData() bool { + a -= 1; + return a == 0; + } + fn loop() ?noreturn { + while (true) { + if (someData()) return null; + } + } + fn testOrelse() u32 { + loop() orelse return 123; + @compileError("bad"); + } +}; + +test "optional of noreturn used with if" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + NoReturn.a = 64; + if (NoReturn.loop()) |_| { + @compileError("bad"); + } else { + try expect(true); + } +} + +test "optional of noreturn used with orelse" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + NoReturn.a = 64; + const val = NoReturn.testOrelse(); + try expect(val == 123); +} diff --git a/test/cases/compile_errors/invalid_optional_payload_type.zig b/test/cases/compile_errors/invalid_optional_payload_type.zig new file mode 100644 index 0000000000..0058cd5e36 --- /dev/null +++ b/test/cases/compile_errors/invalid_optional_payload_type.zig @@ -0,0 +1,13 @@ +comptime { + _ = ?anyopaque; +} +comptime { + _ = ?@TypeOf(null); +} + +// error +// backend=stage2 +// target=native +// +// :2:10: error: opaque type 'anyopaque' cannot be optional +// :5:10: error: type '@TypeOf(null)' cannot be optional