authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-04-10 18:48:23+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-05-16 11:33:38+03:00
logb91d6ff9e8d3b224d8cfa4ce7f206264fe814f6f
treee6c0ec5a4201e64c323c72c9968b4ee3817699ff
parent2286c19c20897727825a6fd8dd63de1ada6724ee

add runtime safety for noreturn function returning

Closes #15221

4 files changed, 55 insertions(+), 4 deletions(-)

lib/std/builtin.zig+1
...@@ -1006,6 +1006,7 @@ pub const panic_messages = struct {...@@ -1006,6 +1006,7 @@ pub const panic_messages = struct {
1006 pub const for_len_mismatch = "for loop over objects with non-equal lengths";1006 pub const for_len_mismatch = "for loop over objects with non-equal lengths";
1007 pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";1007 pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";
1008 pub const memcpy_alias = "@memcpy arguments alias";1008 pub const memcpy_alias = "@memcpy arguments alias";
1009 pub const noreturn_returned = "'noreturn' function returned";
1009};1010};
10101011
1011pub noinline fn returnError(st: *StackTrace) void {1012pub noinline fn returnError(st: *StackTrace) void {
src/Sema.zig+31-3
...@@ -7055,15 +7055,38 @@ fn analyzeCall(...@@ -7055,15 +7055,38 @@ fn analyzeCall(
7055 } },7055 } },
7056 });7056 });
7057 sema.appendRefsAssumeCapacity(args);7057 sema.appendRefsAssumeCapacity(args);
7058
7059 if (call_tag == .call_always_tail) {
7060 if (ensure_result_used) {
7061 try sema.ensureResultUsed(block, sema.typeOf(func_inst), call_src);
7062 }
7063 return sema.handleTailCall(block, call_src, func_ty, func_inst);
7064 } else if (block.wantSafety() and func_ty_info.return_type.isNoReturn()) {
7065 // Function pointers and extern functions aren't guaranteed to
7066 // actually be noreturn so we add a safety check for them.
7067 check: {
7068 var func_val = (try sema.resolveMaybeUndefVal(func)) orelse break :check;
7069 switch (func_val.tag()) {
7070 .function, .decl_ref => {
7071 _ = try block.addNoOp(.unreach);
7072 return Air.Inst.Ref.unreachable_value;
7073 },
7074 else => break :check,
7075 }
7076 }
7077
7078 try sema.safetyPanic(block, .noreturn_returned);
7079 return Air.Inst.Ref.unreachable_value;
7080 } else if (func_ty_info.return_type.isNoReturn()) {
7081 _ = try block.addNoOp(.unreach);
7082 return Air.Inst.Ref.unreachable_value;
7083 }
7058 break :res func_inst;7084 break :res func_inst;
7059 };7085 };
70607086
7061 if (ensure_result_used) {7087 if (ensure_result_used) {
7062 try sema.ensureResultUsed(block, sema.typeOf(result), call_src);7088 try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
7063 }7089 }
7064 if (call_tag == .call_always_tail) {
7065 return sema.handleTailCall(block, call_src, func_ty, result);
7066 }
7067 return result;7090 return result;
7068}7091}
70697092
...@@ -7556,6 +7579,10 @@ fn instantiateGenericCall(...@@ -7556,6 +7579,10 @@ fn instantiateGenericCall(
7556 if (call_tag == .call_always_tail) {7579 if (call_tag == .call_always_tail) {
7557 return sema.handleTailCall(block, call_src, func_ty, result);7580 return sema.handleTailCall(block, call_src, func_ty, result);
7558 }7581 }
7582 if (new_fn_info.return_type.isNoReturn()) {
7583 _ = try block.addNoOp(.unreach);
7584 return Air.Inst.Ref.unreachable_value;
7585 }
7559 return result;7586 return result;
7560}7587}
75617588
...@@ -23441,6 +23468,7 @@ pub const PanicId = enum {...@@ -23441,6 +23468,7 @@ pub const PanicId = enum {
23441 for_len_mismatch,23468 for_len_mismatch,
23442 memcpy_len_mismatch,23469 memcpy_len_mismatch,
23443 memcpy_alias,23470 memcpy_alias,
23471 noreturn_returned,
23444};23472};
2344523473
23446fn addSafetyCheck(23474fn addSafetyCheck(
src/codegen/llvm.zig-1
...@@ -5030,7 +5030,6 @@ pub const FuncGen = struct {...@@ -5030,7 +5030,6 @@ pub const FuncGen = struct {
5030 }5030 }
50315031
5032 if (return_type.isNoReturn() and attr != .AlwaysTail) {5032 if (return_type.isNoReturn() and attr != .AlwaysTail) {
5033 _ = self.builder.buildUnreachable();
5034 return null;5033 return null;
5035 }5034 }
50365035
test/cases/safety/noreturn returned.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "'noreturn' function returned")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10const T = struct {
11 export fn bar() void {
12 // ...
13 }
14};
15
16extern fn bar() noreturn;
17pub fn main() void {
18 _ = T.bar;
19 bar();
20}
21// run
22// backend=llvm
23// target=native