Sema: make optional noreturn behave correctly

This commit is contained in:
Veikka Tuominen 2022-08-17 13:10:58 +03:00
parent a12abc6d6c
commit db0f372da8
3 changed files with 62 additions and 2 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -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