Commit 7a39a038db

Andrew Kelley <andrew@ziglang.org>
2020-08-14 22:08:41
stage2: proper semantic analysis of improper returning of implicit void
1 parent 5f7c719
Changed files (4)
src-self-hosted/Module.zig
@@ -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);
src-self-hosted/zir_sema.zig
@@ -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);
 }
 
test/stage2/cbe.zig
@@ -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();
         \\}
         \\
     );
test/stage2/compare_output.zig
@@ -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 {