Commit db0f372da8

Veikka Tuominen <git@vexu.eu>
2022-08-17 12:10:58
Sema: make optional noreturn behave correctly
1 parent a12abc6
Changed files (3)
src
test
behavior
cases
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);
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);
+}
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