stage2: proper semantic analysis of improper returning of implicit void

This commit is contained in:
Andrew Kelley 2020-08-14 13:08:41 -07:00
parent 5f7c7191ab
commit 7a39a038db
4 changed files with 41 additions and 8 deletions

View File

@ -1345,8 +1345,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
_ = try astgen.blockExpr(self, params_scope, .none, body_block);
if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or
!gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))
if (gen_scope.instructions.items.len == 0 or
!gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
{
const src = tree.token_locs[body_block.rbrace].start;
_ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);

View File

@ -112,8 +112,17 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
}
pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
for (body.instructions) |src_inst| {
src_inst.analyzed_inst = try analyzeInst(mod, scope, src_inst);
for (body.instructions) |src_inst, i| {
const analyzed_inst = try analyzeInst(mod, scope, src_inst);
src_inst.analyzed_inst = analyzed_inst;
if (analyzed_inst.ty.zigTypeTag() == .NoReturn) {
for (body.instructions[i..]) |unreachable_inst| {
if (unreachable_inst.castTag(.dbg_stmt)) |dbg_stmt| {
return mod.fail(scope, dbg_stmt.base.src, "unreachable code", .{});
}
}
break;
}
}
}
@ -1216,6 +1225,15 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!
fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
const b = try mod.requireRuntimeBlock(scope, inst.base.src);
if (b.func) |func| {
// Need to emit a compile error if returning void is not allowed.
const void_inst = try mod.constVoid(scope, inst.base.src);
const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);
if (casted_void.ty.zigTypeTag() != .Void) {
return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);
}
}
return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
}

View File

@ -10,13 +10,19 @@ const linux_x64 = std.zig.CrossTarget{
pub fn addCases(ctx: *TestContext) !void {
ctx.c("empty start function", linux_x64,
\\export fn _start() noreturn {}
\\export fn _start() noreturn {
\\ unreachable;
\\}
,
\\zig_noreturn void _start(void) {}
\\zig_noreturn void _start(void) {
\\ zig_unreachable();
\\}
\\
);
ctx.c("less empty start function", linux_x64,
\\fn main() noreturn {}
\\fn main() noreturn {
\\ unreachable;
\\}
\\
\\export fn _start() noreturn {
\\ main();
@ -28,7 +34,9 @@ pub fn addCases(ctx: *TestContext) !void {
\\ main();
\\}
\\
\\zig_noreturn void main(void) {}
\\zig_noreturn void main(void) {
\\ zig_unreachable();
\\}
\\
);
// TODO: implement return values
@ -40,6 +48,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (0)
\\ );
\\ unreachable;
\\}
\\
\\export fn _start() noreturn {
@ -62,6 +71,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ register size_t rax_constant __asm__("rax") = 231;
\\ register size_t rdi_constant __asm__("rdi") = 0;
\\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
\\ zig_unreachable();
\\}
\\
);

View File

@ -26,6 +26,11 @@ pub fn addCases(ctx: *TestContext) !void {
case.addError("", &[_][]const u8{":1:1: error: no entry point found"});
case.addError(
\\export fn _start() noreturn {
\\}
, &[_][]const u8{":2:1: error: expected noreturn, found void"});
// Regular old hello world
case.addCompareOutput(
\\export fn _start() noreturn {