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 {
10061006 pub const for_len_mismatch = "for loop over objects with non-equal lengths";
10071007 pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";
10081008 pub const memcpy_alias = "@memcpy arguments alias";
1009 pub const noreturn_returned = "'noreturn' function returned";
10091010};
10101011
10111012pub noinline fn returnError(st: *StackTrace) void {
src/Sema.zig+31-3
......@@ -7055,15 +7055,38 @@ fn analyzeCall(
70557055 } },
70567056 });
70577057 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 }
70587084 break :res func_inst;
70597085 };
70607086
70617087 if (ensure_result_used) {
70627088 try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
70637089 }
7064 if (call_tag == .call_always_tail) {
7065 return sema.handleTailCall(block, call_src, func_ty, result);
7066 }
70677090 return result;
70687091}
70697092
......@@ -7556,6 +7579,10 @@ fn instantiateGenericCall(
75567579 if (call_tag == .call_always_tail) {
75577580 return sema.handleTailCall(block, call_src, func_ty, result);
75587581 }
7582 if (new_fn_info.return_type.isNoReturn()) {
7583 _ = try block.addNoOp(.unreach);
7584 return Air.Inst.Ref.unreachable_value;
7585 }
75597586 return result;
75607587}
75617588
......@@ -23441,6 +23468,7 @@ pub const PanicId = enum {
2344123468 for_len_mismatch,
2344223469 memcpy_len_mismatch,
2344323470 memcpy_alias,
23471 noreturn_returned,
2344423472};
2344523473
2344623474fn addSafetyCheck(
src/codegen/llvm.zig-1
......@@ -5030,7 +5030,6 @@ pub const FuncGen = struct {
50305030 }
50315031
50325032 if (return_type.isNoReturn() and attr != .AlwaysTail) {
5033 _ = self.builder.buildUnreachable();
50345033 return null;
50355034 }
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