Skip to content

Commit

Permalink
add runtime safety for noreturn function returning
Browse files Browse the repository at this point in the history
Closes #15221
  • Loading branch information
Vexu committed May 16, 2023
1 parent 2286c19 commit b91d6ff
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 4 deletions.
1 change: 1 addition & 0 deletions lib/std/builtin.zig
Expand Up @@ -1006,6 +1006,7 @@ pub const panic_messages = struct {
pub const for_len_mismatch = "for loop over objects with non-equal lengths";
pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";
pub const memcpy_alias = "@memcpy arguments alias";
pub const noreturn_returned = "'noreturn' function returned";
};

pub noinline fn returnError(st: *StackTrace) void {
Expand Down
34 changes: 31 additions & 3 deletions src/Sema.zig
Expand Up @@ -7055,15 +7055,38 @@ fn analyzeCall(
} },
});
sema.appendRefsAssumeCapacity(args);

if (call_tag == .call_always_tail) {
if (ensure_result_used) {
try sema.ensureResultUsed(block, sema.typeOf(func_inst), call_src);
}
return sema.handleTailCall(block, call_src, func_ty, func_inst);
} else if (block.wantSafety() and func_ty_info.return_type.isNoReturn()) {
// Function pointers and extern functions aren't guaranteed to
// actually be noreturn so we add a safety check for them.
check: {
var func_val = (try sema.resolveMaybeUndefVal(func)) orelse break :check;
switch (func_val.tag()) {
.function, .decl_ref => {
_ = try block.addNoOp(.unreach);
return Air.Inst.Ref.unreachable_value;
},
else => break :check,
}
}

try sema.safetyPanic(block, .noreturn_returned);
return Air.Inst.Ref.unreachable_value;
} else if (func_ty_info.return_type.isNoReturn()) {
_ = try block.addNoOp(.unreach);
return Air.Inst.Ref.unreachable_value;
}
break :res func_inst;
};

if (ensure_result_used) {
try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
}
if (call_tag == .call_always_tail) {
return sema.handleTailCall(block, call_src, func_ty, result);
}
return result;
}

Expand Down Expand Up @@ -7556,6 +7579,10 @@ fn instantiateGenericCall(
if (call_tag == .call_always_tail) {
return sema.handleTailCall(block, call_src, func_ty, result);
}
if (new_fn_info.return_type.isNoReturn()) {
_ = try block.addNoOp(.unreach);
return Air.Inst.Ref.unreachable_value;
}
return result;
}

Expand Down Expand Up @@ -23441,6 +23468,7 @@ pub const PanicId = enum {
for_len_mismatch,
memcpy_len_mismatch,
memcpy_alias,
noreturn_returned,
};

fn addSafetyCheck(
Expand Down
1 change: 0 additions & 1 deletion src/codegen/llvm.zig
Expand Up @@ -5030,7 +5030,6 @@ pub const FuncGen = struct {
}

if (return_type.isNoReturn() and attr != .AlwaysTail) {
_ = self.builder.buildUnreachable();
return null;
}

Expand Down
23 changes: 23 additions & 0 deletions test/cases/safety/noreturn returned.zig
@@ -0,0 +1,23 @@
const std = @import("std");

pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = stack_trace;
if (std.mem.eql(u8, message, "'noreturn' function returned")) {
std.process.exit(0);
}
std.process.exit(1);
}
const T = struct {
export fn bar() void {
// ...
}
};

extern fn bar() noreturn;
pub fn main() void {
_ = T.bar;
bar();
}
// run
// backend=llvm
// target=native

0 comments on commit b91d6ff

Please sign in to comment.