From 7ec783876a565662223268a70ba984e0a132b94a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 11 Jan 2018 23:04:08 -0500
Subject: functions which can return errors have secret stack trace param
See #651
---
src/ir.cpp | 10 ----------
1 file changed, 10 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index f236910250..340e4fbeda 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -8230,16 +8230,6 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out)
return ir_resolve_bool(ira, value, out);
}
-static ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
- Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name));
- resolve_top_level_decl(codegen, tld, false, nullptr);
- assert(tld->id == TldIdVar);
- TldVar *tld_var = (TldVar *)tld;
- ConstExprValue *var_value = tld_var->var->value;
- assert(var_value != nullptr);
- return var_value;
-}
-
static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) {
if (type_is_invalid(value->value.type))
return false;
--
cgit v1.2.3
From 32ea6f54e5f05c4173828c4f4c8ab9965a929120 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 12 Jan 2018 02:12:11 -0500
Subject: *WIP* proof of concept error return traces
---
build.zig | 12 ++---
src/all_types.hpp | 2 +
src/analyze.cpp | 6 ++-
src/analyze.hpp | 1 +
src/codegen.cpp | 106 +++++++++++++++++++++++++++++++-----
src/ir.cpp | 12 +++++
std/build.zig | 6 +--
std/debug/index.zig | 110 ++++++++++++++++++++++++++++++++++++--
std/special/build_runner.zig | 4 +-
std/special/builtin.zig | 2 +-
std/special/compiler_rt/index.zig | 2 +-
std/special/panic.zig | 8 ++-
12 files changed, 238 insertions(+), 33 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/build.zig b/build.zig
index 7c1563c706..1fa984dcff 100644
--- a/build.zig
+++ b/build.zig
@@ -10,7 +10,7 @@ const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
const io = std.io;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
const mode = b.standardReleaseOptions();
var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
@@ -36,7 +36,7 @@ pub fn build(b: &Builder) {
const test_step = b.step("test", "Run all the tests");
// find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library
- const build_info = b.exec([][]const u8{b.zig_exe, "BUILD_INFO"});
+ const build_info = try b.exec([][]const u8{b.zig_exe, "BUILD_INFO"});
var index: usize = 0;
const cmake_binary_dir = nextValue(&index, build_info);
const cxx_compiler = nextValue(&index, build_info);
@@ -68,7 +68,7 @@ pub fn build(b: &Builder) {
dependOnLib(exe, llvm);
if (exe.target.getOs() == builtin.Os.linux) {
- const libstdcxx_path_padded = b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});
+ const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});
const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next();
exe.addObjectFile(libstdcxx_path);
@@ -155,9 +155,9 @@ const LibraryDep = struct {
};
fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep {
- const libs_output = b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
- const includes_output = b.exec([][]const u8{llvm_config_exe, "--includedir"});
- const libdir_output = b.exec([][]const u8{llvm_config_exe, "--libdir"});
+ const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
+ const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"});
+ const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"});
var result = LibraryDep {
.libs = ArrayList([]const u8).init(b.allocator),
diff --git a/src/all_types.hpp b/src/all_types.hpp
index caae13d20d..6476c08625 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1205,6 +1205,7 @@ struct FnTableEntry {
uint32_t alignstack_value;
ZigList export_list;
+ bool calls_errorable_function;
};
uint32_t fn_table_entry_hash(FnTableEntry*);
@@ -1530,6 +1531,7 @@ struct CodeGen {
FnTableEntry *panic_fn;
LLVMValueRef cur_ret_ptr;
LLVMValueRef cur_fn_val;
+ LLVMValueRef cur_err_ret_trace_val;
bool c_want_stdint;
bool c_want_stdbool;
AstNode *root_export_decl;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 2e51e5151c..61d86b5eb2 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -869,7 +869,7 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) {
zig_unreachable();
}
-static TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) {
+TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) {
if (g->stack_trace_type == nullptr) {
ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace");
assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType);
@@ -1191,6 +1191,9 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
}
TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
+ if (type_is_invalid(type_entry)) {
+ return g->builtin_types.entry_invalid;
+ }
if (fn_type_id.cc != CallingConventionUnspecified) {
type_ensure_zero_bits_known(g, type_entry);
if (!type_has_bits(type_entry)) {
@@ -2586,6 +2589,7 @@ static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntr
}
static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
+ return; // TODO
AstNode *proto_node = panic_fn->proto_node;
assert(proto_node->type == NodeTypeFnProto);
TypeTableEntry *fn_type = panic_fn->type_entry;
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 050630bc07..3992cefdfc 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -187,6 +187,7 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G
ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);
+TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g);
#endif
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 4de0fcec51..97cab9a69f 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -22,6 +22,8 @@
#include
#include
+static const size_t stack_trace_ptr_count = 31;
+
static void init_darwin_native(CodeGen *g) {
char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET");
char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET");
@@ -867,16 +869,24 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) {
return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0));
}
-static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) {
+static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg) {
assert(g->panic_fn != nullptr);
LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);
LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);
- ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, "");
+ if (stack_trace_arg == nullptr) {
+ TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
+ stack_trace_arg = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
+ }
+ LLVMValueRef args[] = {
+ msg_arg,
+ stack_trace_arg,
+ };
+ ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, "");
LLVMBuildUnreachable(g->builder);
}
static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) {
- gen_panic(g, get_panic_msg_ptr_val(g, msg_id));
+ gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr);
}
static LLVMValueRef get_memcpy_fn_val(CodeGen *g) {
@@ -956,7 +966,11 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2);
Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false);
- LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), &g->err_tag_type->type_ref, 1, false);
+ LLVMTypeRef arg_types[] = {
+ g->err_tag_type->type_ref,
+ g->ptr_to_stack_trace_type->type_ref,
+ };
+ LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
addLLVMFnAttr(fn_val, "noreturn");
addLLVMFnAttr(fn_val, "cold");
@@ -1008,7 +1022,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, "");
gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type);
- gen_panic(g, global_slice);
+ gen_panic(g, global_slice, LLVMGetParam(fn_val, 1));
LLVMPositionBuilderAtEnd(g->builder, prev_block);
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
@@ -1019,7 +1033,16 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
- ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified),
+ LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val;
+ if (err_ret_trace_val == nullptr) {
+ TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
+ err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
+ }
+ LLVMValueRef args[] = {
+ err_val,
+ err_ret_trace_val,
+ };
+ ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified),
ZigLLVM_FnInlineAuto, "");
LLVMBuildUnreachable(g->builder);
}
@@ -1299,6 +1322,50 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) {
LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
TypeTableEntry *return_type = return_instruction->value->value.type;
+
+ bool is_err_return = false;
+ if (return_type->id == TypeTableEntryIdErrorUnion) {
+ if (return_instruction->value->value.special == ConstValSpecialStatic) {
+ is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr;
+ } else if (return_instruction->value->value.special == ConstValSpecialRuntime) {
+ is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
+ // TODO: emit a branch to check if the return value is an error
+ }
+ } else if (return_type->id == TypeTableEntryIdPureError) {
+ is_err_return = true;
+ }
+ if (is_err_return) {
+ LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
+
+ // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer();
+ // stack_trace.index += 1;
+
+ LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError");
+
+ LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block);
+ size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
+ LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, "");
+ size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
+ LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, "");
+
+ // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer();
+ LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, "");
+ LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), "");
+ LLVMValueRef address_indices[] = {
+ LLVMConstNull(usize_type_ref),
+ modded_val,
+ };
+ LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, addresses_field_ptr, address_indices, 2, "");
+ LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, block_address, usize_type_ref, "");
+ gen_store_untyped(g, address_value, address_slot, 0, false);
+
+ // stack_trace.index += 1;
+ LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), "");
+ gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false);
+
+ LLVMBuildBr(g->builder, return_block);
+ LLVMPositionBuilderAtEnd(g->builder, return_block);
+ }
if (handle_is_ptr(return_type)) {
if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {
assert(g->cur_ret_ptr);
@@ -2353,7 +2420,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
}
}
if (last_arg_err_ret_stack) {
- gen_param_values[gen_param_index] = LLVMGetUndef(g->ptr_to_stack_trace_type->type_ref);
+ gen_param_values[gen_param_index] = g->cur_err_ret_trace_val;
gen_param_index += 1;
}
@@ -3482,7 +3549,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec
}
static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) {
- gen_panic(g, ir_llvm_value(g, instruction->msg));
+ gen_panic(g, ir_llvm_value(g, instruction->msg), nullptr);
return nullptr;
}
@@ -4501,7 +4568,8 @@ static void do_code_gen(CodeGen *g) {
LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);
g->cur_fn = fn_table_entry;
g->cur_fn_val = fn;
- if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) {
+ TypeTableEntry *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
+ if (handle_is_ptr(return_type)) {
g->cur_ret_ptr = LLVMGetParam(fn, 0);
} else {
g->cur_ret_ptr = nullptr;
@@ -4510,6 +4578,18 @@ static void do_code_gen(CodeGen *g) {
build_all_basic_blocks(g, fn_table_entry);
clear_debug_source_node(g);
+ if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) {
+ g->cur_err_ret_trace_val = LLVMGetParam(fn, LLVMCountParamTypes(fn_table_entry->type_entry->data.fn.raw_type_ref) - 1);
+ } else if (fn_table_entry->calls_errorable_function) {
+ g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
+ size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
+ LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, "");
+ TypeTableEntry *usize = g->builtin_types.entry_usize;
+ gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
+ } else {
+ g->cur_err_ret_trace_val = nullptr;
+ }
+
// allocate temporary stack data
for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) {
IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
@@ -5096,12 +5176,11 @@ static void define_builtin_compile_vars(CodeGen *g) {
os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
Buf *contents = buf_alloc();
- buf_append_str(contents,
+ buf_appendf(contents,
"pub const StackTrace = struct {\n"
" index: usize,\n"
- " instruction_addresses: [31]usize,\n"
- "};\n\n"
- );
+ " instruction_addresses: [%" ZIG_PRI_usize "]usize,\n"
+ "};\n\n", stack_trace_ptr_count);
const char *cur_os = nullptr;
{
@@ -5266,6 +5345,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->compile_var_import = add_source_file(g, g->compile_var_package, abs_full_path, contents);
+ scan_import(g, g->compile_var_import);
}
static void init(CodeGen *g) {
diff --git a/src/ir.cpp b/src/ir.cpp
index 340e4fbeda..5dd608083f 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -10043,9 +10043,21 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
ir_add_alloca(ira, new_call_instruction, return_type);
+ if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) {
+ parent_fn_entry->calls_errorable_function = true;
+ }
+
return ir_finish_anal(ira, return_type);
}
+ FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
+ assert(fn_type_id->return_type != nullptr);
+ assert(parent_fn_entry != nullptr);
+ if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) {
+ parent_fn_entry->calls_errorable_function = true;
+ }
+
+
IrInstruction **casted_args = allocate(call_param_count);
size_t next_arg_index = 0;
if (first_arg_ptr) {
diff --git a/std/build.zig b/std/build.zig
index cd6b3811ea..6984715466 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -721,11 +721,9 @@ pub const Builder = struct {
return error.FileNotFound;
}
- pub fn exec(self: &Builder, argv: []const []const u8) -> []u8 {
+ pub fn exec(self: &Builder, argv: []const []const u8) -> %[]u8 {
const max_output_size = 100 * 1024;
- const result = os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size) catch |err| {
- std.debug.panic("Unable to spawn {}: {}", argv[0], @errorName(err));
- };
+ const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size);
switch (result.term) {
os.ChildProcess.Term.Exited => |code| {
if (code != 0) {
diff --git a/std/debug/index.zig b/std/debug/index.zig
index 464974b7de..fa23040794 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -37,10 +37,16 @@ fn getStderrStream() -> %&io.OutStream {
}
}
+/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
+pub fn dumpCurrentStackTrace() {
+ const stderr = getStderrStream() catch return;
+ writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return;
+}
+
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
-pub fn dumpStackTrace() {
+pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) {
const stderr = getStderrStream() catch return;
- writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return;
+ writeStackTrace(stack_trace, stderr, global_allocator, stderr_file.isTty()) catch return;
}
/// This function invokes undefined behavior when `ok` is `false`.
@@ -88,7 +94,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
const stderr = getStderrStream() catch os.abort();
stderr.print(format ++ "\n", args) catch os.abort();
- writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort();
+ writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort();
os.abort();
}
@@ -101,7 +107,103 @@ const RESET = "\x1b[0m";
error PathNotFound;
error InvalidDebugInfo;
-pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool,
+pub fn writeStackTrace(st_addrs: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool) -> %void {
+ switch (builtin.object_format) {
+ builtin.ObjectFormat.elf => {
+ var stack_trace = ElfStackTrace {
+ .self_exe_file = undefined,
+ .elf = undefined,
+ .debug_info = undefined,
+ .debug_abbrev = undefined,
+ .debug_str = undefined,
+ .debug_line = undefined,
+ .debug_ranges = null,
+ .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
+ .compile_unit_list = ArrayList(CompileUnit).init(allocator),
+ };
+ const st = &stack_trace;
+ st.self_exe_file = try os.openSelfExe();
+ defer st.self_exe_file.close();
+
+ try st.elf.openFile(allocator, &st.self_exe_file);
+ defer st.elf.close();
+
+ st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
+ st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
+ st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo;
+ st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
+ st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
+ try scanAllCompileUnits(st);
+
+ var ignored_count: usize = 0;
+
+ var frame_index: usize = undefined;
+ var frames_left: usize = undefined;
+ if (st_addrs.index < st_addrs.instruction_addresses.len) {
+ frame_index = 0;
+ frames_left = st_addrs.index;
+ } else {
+ frame_index = (st_addrs.index + 1) % st_addrs.instruction_addresses.len;
+ frames_left = st_addrs.instruction_addresses.len;
+ }
+
+ while (frames_left != 0) : ({frames_left -= 1; frame_index = (frame_index + 1) % st_addrs.instruction_addresses.len;}) {
+ const return_address = st_addrs.instruction_addresses[frame_index];
+
+ // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
+ // at compile time. I'll call it issue #313
+ const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
+
+ const compile_unit = findCompileUnit(st, return_address) catch {
+ try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
+ return_address);
+ continue;
+ };
+ const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name);
+ if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| {
+ defer line_info.deinit();
+ try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
+ DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
+ line_info.file_name, line_info.line, line_info.column,
+ return_address, compile_unit_name);
+ if (printLineFromFile(st.allocator(), out_stream, line_info)) {
+ if (line_info.column == 0) {
+ try out_stream.write("\n");
+ } else {
+ {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
+ try out_stream.writeByte(' ');
+ }}
+ try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
+ }
+ } else |err| switch (err) {
+ error.EndOfFile, error.PathNotFound => {},
+ else => return err,
+ }
+ } else |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => {
+ try out_stream.print(ptr_hex ++ " in ??? ({})\n",
+ return_address, compile_unit_name);
+ },
+ else => return err,
+ }
+ }
+ },
+ builtin.ObjectFormat.coff => {
+ try out_stream.write("(stack trace unavailable for COFF object format)\n");
+ },
+ builtin.ObjectFormat.macho => {
+ try out_stream.write("(stack trace unavailable for Mach-O object format)\n");
+ },
+ builtin.ObjectFormat.wasm => {
+ try out_stream.write("(stack trace unavailable for WASM object format)\n");
+ },
+ builtin.ObjectFormat.unknown => {
+ try out_stream.write("(stack trace unavailable for unknown object format)\n");
+ },
+ }
+}
+
+pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool,
ignore_frame_count: usize) -> %void
{
switch (builtin.object_format) {
diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig
index 24d9c756e7..b293a1e64d 100644
--- a/std/special/build_runner.zig
+++ b/std/special/build_runner.zig
@@ -112,7 +112,7 @@ pub fn main() -> %void {
}
builder.setInstallPrefix(prefix);
- root.build(&builder);
+ root.build(&builder) catch unreachable;
if (builder.validateUserInputDidItFail())
return usageAndErr(&builder, true, try stderr_stream);
@@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
// run the build script to collect the options
if (!already_ran_build) {
builder.setInstallPrefix(null);
- root.build(builder);
+ root.build(builder) catch unreachable;
}
// This usage text has to be synchronized with src/main.cpp
diff --git a/std/special/builtin.zig b/std/special/builtin.zig
index e6c09863ca..dd77ba9c75 100644
--- a/std/special/builtin.zig
+++ b/std/special/builtin.zig
@@ -5,7 +5,7 @@ const builtin = @import("builtin");
// Avoid dragging in the debug safety mechanisms into this .o file,
// unless we're trying to test this file.
-pub coldcc fn panic(msg: []const u8) -> noreturn {
+pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
if (builtin.is_test) {
@import("std").debug.panic("{}", msg);
} else {
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
index 5034a6fd90..96f34e56ff 100644
--- a/std/special/compiler_rt/index.zig
+++ b/std/special/compiler_rt/index.zig
@@ -74,7 +74,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
// Avoid dragging in the debug safety mechanisms into this .o file,
// unless we're trying to test this file.
-pub coldcc fn panic(msg: []const u8) -> noreturn {
+pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
if (is_test) {
@import("std").debug.panic("{}", msg);
} else {
diff --git a/std/special/panic.zig b/std/special/panic.zig
index 03c2586739..690c4afa1c 100644
--- a/std/special/panic.zig
+++ b/std/special/panic.zig
@@ -4,14 +4,20 @@
// have to be added in the compiler.
const builtin = @import("builtin");
+const std = @import("std");
-pub coldcc fn panic(msg: []const u8) -> noreturn {
+pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
switch (builtin.os) {
// TODO: fix panic in zen.
builtin.Os.freestanding, builtin.Os.zen => {
while (true) {}
},
else => {
+ if (error_return_trace) |trace| {
+ std.debug.warn("{}\n", msg);
+ std.debug.dumpStackTrace(trace);
+ @import("std").debug.panic("");
+ }
@import("std").debug.panic("{}", msg);
},
}
--
cgit v1.2.3
From 7b57454cc11371b71097967656e19f0a1736d733 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 15 Jan 2018 00:01:02 -0500
Subject: clean up error return tracing
* error return tracing is disabled in release-fast mode
* add @errorReturnTrace
* zig build API changes build return type from `void` to `%void`
* allow `void`, `noreturn`, and `u8` from main. closes #535
---
doc/langref.html.in | 8 ++++
example/mix_o_files/build.zig | 2 +-
example/shared_library/build.zig | 2 +-
src/all_types.hpp | 7 ++++
src/analyze.cpp | 12 ++----
src/codegen.cpp | 78 ++++++++++++++++++++++--------------
src/ir.cpp | 35 ++++++++++++++++
src/ir_print.cpp | 7 ++++
std/build.zig | 4 +-
std/debug/index.zig | 9 +++--
std/io.zig | 2 +-
std/os/index.zig | 11 +----
std/special/bootstrap.zig | 45 ++++++++++++++++-----
std/special/build_runner.zig | 8 ++--
std/special/test_runner.zig | 9 +----
test/compile_errors.zig | 18 ++++-----
test/debug_safety.zig | 40 +++++++++---------
test/standalone/issue_339/build.zig | 2 +-
test/standalone/issue_339/test.zig | 3 +-
test/standalone/pkg_import/build.zig | 2 +-
test/standalone/use_alias/build.zig | 2 +-
test/tests.zig | 4 +-
22 files changed, 198 insertions(+), 112 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 2476790105..94bc780959 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -142,6 +142,7 @@
@TagType
@EnumTagType
@errorName
+ @errorReturnTrace
@fence
@fieldParentPtr
@frameAddress
@@ -4412,6 +4413,13 @@ test.zig:6:2: error: found compile log statement
or all calls have a compile-time known value for err, then no
error name table will be generated.
+ @errorReturnTrace
+ @errorReturnTrace() -> ?&builtin.StackTrace
+
+ If the binary is built with error return tracing, and this function is invoked in a
+ function that calls a function with an error or error union return type, returns a
+ stack trace object. Otherwise returns `null`.
+
@fence
@fence(order: AtomicOrder)
diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig
index 391af9924a..4bba69b091 100644
--- a/example/mix_o_files/build.zig
+++ b/example/mix_o_files/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
const obj = b.addObject("base64", "base64.zig");
const exe = b.addCExecutable("test");
diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig
index 9b7f3793c6..147b54401c 100644
--- a/example/shared_library/build.zig
+++ b/example/shared_library/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
const exe = b.addCExecutable("test");
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 7a3f1a9b79..b401097647 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1274,6 +1274,7 @@ enum BuiltinFnId {
BuiltinFnIdSetAlignStack,
BuiltinFnIdArgType,
BuiltinFnIdExport,
+ BuiltinFnIdErrorReturnTrace,
};
struct BuiltinFnEntry {
@@ -1499,6 +1500,7 @@ struct CodeGen {
Buf triple_str;
BuildMode build_mode;
bool is_test_build;
+ bool have_err_ret_tracing;
uint32_t target_os_index;
uint32_t target_arch_index;
uint32_t target_environ_index;
@@ -1902,6 +1904,7 @@ enum IrInstructionId {
IrInstructionIdSetAlignStack,
IrInstructionIdArgType,
IrInstructionIdExport,
+ IrInstructionIdErrorReturnTrace,
};
struct IrInstruction {
@@ -2723,6 +2726,10 @@ struct IrInstructionExport {
IrInstruction *target;
};
+struct IrInstructionErrorReturnTrace {
+ IrInstruction base;
+};
+
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index f7c0f832a2..8028ace289 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -925,8 +925,9 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
if (!skip_debug_info) {
bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&
handle_is_ptr(fn_type_id->return_type);
- bool prefix_arg_error_return_trace = fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
- fn_type_id->return_type->id == TypeTableEntryIdPureError;
+ bool prefix_arg_error_return_trace = g->have_err_ret_tracing &&
+ (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
+ fn_type_id->return_type->id == TypeTableEntryIdPureError);
// +1 for maybe making the first argument the return value
// +1 for maybe last argument the error return trace
LLVMTypeRef *gen_param_types = allocate(2 + fn_type_id->param_count);
@@ -2711,13 +2712,6 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
{
if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
g->main_fn = fn_table_entry;
- TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
- TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
- if (actual_return_type != err_void) {
- add_node_error(g, fn_proto->return_type,
- buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
- buf_ptr(&actual_return_type->name)));
- }
} else if ((import->package == g->panic_package || g->have_pub_panic) &&
buf_eql_str(&fn_table_entry->symbol_name, "panic"))
{
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 96c0879615..5113aa7275 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -404,7 +404,10 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
zig_unreachable();
}
-static uint32_t get_err_ret_trace_arg_index(FnTableEntry *fn_table_entry) {
+static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_entry) {
+ if (!g->have_err_ret_tracing) {
+ return UINT32_MAX;
+ }
TypeTableEntry *fn_type = fn_table_entry->type_entry;
TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) {
@@ -572,7 +575,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
}
}
- uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(fn_table_entry);
+ uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
if (err_ret_trace_arg_index != UINT32_MAX) {
addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
}
@@ -1415,31 +1418,33 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
TypeTableEntry *return_type = return_instruction->value->value.type;
- bool is_err_return = false;
- if (return_type->id == TypeTableEntryIdErrorUnion) {
- if (return_instruction->value->value.special == ConstValSpecialStatic) {
- is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr;
- } else if (return_instruction->value->value.special == ConstValSpecialRuntime) {
- is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
- // TODO: emit a branch to check if the return value is an error
+ if (g->have_err_ret_tracing) {
+ bool is_err_return = false;
+ if (return_type->id == TypeTableEntryIdErrorUnion) {
+ if (return_instruction->value->value.special == ConstValSpecialStatic) {
+ is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr;
+ } else if (return_instruction->value->value.special == ConstValSpecialRuntime) {
+ is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
+ // TODO: emit a branch to check if the return value is an error
+ }
+ } else if (return_type->id == TypeTableEntryIdPureError) {
+ is_err_return = true;
+ }
+ if (is_err_return) {
+ LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError");
+ LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block);
+
+ LLVMValueRef return_err_fn = get_return_err_fn(g);
+ LLVMValueRef args[] = {
+ g->cur_err_ret_trace_val,
+ block_address,
+ };
+ LLVMBuildBr(g->builder, return_block);
+ LLVMPositionBuilderAtEnd(g->builder, return_block);
+ LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 2,
+ get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
+ LLVMSetTailCall(call_instruction, true);
}
- } else if (return_type->id == TypeTableEntryIdPureError) {
- is_err_return = true;
- }
- if (is_err_return) {
- LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError");
- LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block);
-
- LLVMValueRef return_err_fn = get_return_err_fn(g);
- LLVMValueRef args[] = {
- g->cur_err_ret_trace_val,
- block_address,
- };
- LLVMBuildBr(g->builder, return_block);
- LLVMPositionBuilderAtEnd(g->builder, return_block);
- LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 2,
- get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
- LLVMSetTailCall(call_instruction, true);
}
if (handle_is_ptr(return_type)) {
if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {
@@ -2475,7 +2480,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
TypeTableEntry *src_return_type = fn_type_id->return_type;
bool ret_has_bits = type_has_bits(src_return_type);
bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
- bool prefix_arg_err_ret_stack = src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError;
+ bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError);
size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0);
bool is_var_args = fn_type_id->is_var_args;
LLVMValueRef *gen_param_values = allocate(actual_param_count);
@@ -3031,6 +3036,16 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
return target_val;
}
+static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable,
+ IrInstructionErrorReturnTrace *instruction)
+{
+ TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
+ if (g->cur_err_ret_trace_val == nullptr) {
+ return LLVMConstNull(ptr_to_stack_trace_type->type_ref);
+ }
+ return g->cur_err_ret_trace_val;
+}
+
static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
switch (atomic_order) {
case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;
@@ -3804,6 +3819,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction);
case IrInstructionIdAlignCast:
return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction);
+ case IrInstructionIdErrorReturnTrace:
+ return ir_render_error_return_trace(g, executable, (IrInstructionErrorReturnTrace *)instruction);
}
zig_unreachable();
}
@@ -4653,10 +4670,10 @@ static void do_code_gen(CodeGen *g) {
build_all_basic_blocks(g, fn_table_entry);
clear_debug_source_node(g);
- uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(fn_table_entry);
+ uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
if (err_ret_trace_arg_index != UINT32_MAX) {
g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index);
- } else if (fn_table_entry->calls_errorable_function) {
+ } else if (g->have_err_ret_tracing && fn_table_entry->calls_errorable_function) {
// TODO call graph analysis to find out what this number needs to be for every function
static const size_t stack_trace_ptr_count = 30;
@@ -5251,6 +5268,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);
create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2);
create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
+ create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
}
static const char *bool_to_str(bool b) {
@@ -5553,6 +5571,8 @@ static void init(CodeGen *g) {
}
}
+ g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease;
+
define_builtin_fns(g);
define_builtin_compile_vars(g);
}
diff --git a/src/ir.cpp b/src/ir.cpp
index 5dd608083f..d6844f32ba 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -572,6 +572,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) {
return IrInstructionIdArgType;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) {
+ return IrInstructionIdErrorReturnTrace;
+}
+
template
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate(1);
@@ -2305,6 +2309,12 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s
return &instruction->base;
}
+static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) {
+ IrInstructionErrorReturnTrace *instruction = ir_build_instruction(irb, scope, source_node);
+
+ return &instruction->base;
+}
+
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -3731,6 +3741,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);
}
+ case BuiltinFnIdErrorReturnTrace:
+ {
+ return ir_build_error_return_trace(irb, scope, node);
+ }
}
zig_unreachable();
}
@@ -9568,6 +9582,24 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
return ira->codegen->builtin_types.entry_void;
}
+static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
+ IrInstructionErrorReturnTrace *instruction)
+{
+ FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
+ TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
+ TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
+ if (fn_entry == nullptr || !fn_entry->calls_errorable_function || !ira->codegen->have_err_ret_tracing) {
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+ out_val->data.x_maybe = nullptr;
+ return nullable_type;
+ }
+
+ IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
+ instruction->base.source_node);
+ ir_link_new_instruction(new_instruction, &instruction->base);
+ return nullable_type;
+}
+
static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
{
@@ -15324,6 +15356,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction);
case IrInstructionIdExport:
return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);
+ case IrInstructionIdErrorReturnTrace:
+ return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction);
}
zig_unreachable();
}
@@ -15507,6 +15541,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdOpaqueType:
case IrInstructionIdArgType:
case IrInstructionIdTagType:
+ case IrInstructionIdErrorReturnTrace:
return false;
case IrInstructionIdAsm:
{
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index f5aba2a45d..930d22f21a 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -996,6 +996,10 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
}
}
+static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {
+ fprintf(irp->f, "@errorReturnTrace()");
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
@@ -1308,6 +1312,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdExport:
ir_print_export(irp, (IrInstructionExport *)instruction);
break;
+ case IrInstructionIdErrorReturnTrace:
+ ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/std/build.zig b/std/build.zig
index 6984715466..5d79b00c4f 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -247,11 +247,11 @@ pub const Builder = struct {
defer wanted_steps.deinit();
if (step_names.len == 0) {
- wanted_steps.append(&self.default_step) catch unreachable;
+ try wanted_steps.append(&self.default_step);
} else {
for (step_names) |step_name| {
const s = try self.getTopLevelStepByName(step_name);
- wanted_steps.append(s) catch unreachable;
+ try wanted_steps.append(s);
}
}
diff --git a/std/debug/index.zig b/std/debug/index.zig
index f7f6ffa323..5dc7b0dbfa 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -56,7 +56,7 @@ pub fn dumpCurrentStackTrace() {
}
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
-pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) {
+pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) {
const stderr = getStderrStream() catch return;
const debug_info = openSelfDebugInfo(global_allocator) catch |err| {
stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
@@ -127,7 +127,7 @@ const RESET = "\x1b[0m";
error PathNotFound;
error InvalidDebugInfo;
-pub fn writeStackTrace(stack_trace: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
+pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
debug_info: &ElfStackTrace, tty_color: bool) -> %void
{
var frame_index: usize = undefined;
@@ -167,6 +167,9 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
}
fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void {
+ if (builtin.os == builtin.Os.windows) {
+ return error.UnsupportedDebugInfo;
+ }
// TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
// at compile time. I'll call it issue #313
const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
@@ -177,7 +180,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a
return;
};
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
- if (getLineNumberInfo(debug_info, compile_unit, usize(address) - 1)) |line_info| {
+ if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
defer line_info.deinit();
try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
diff --git a/std/io.zig b/std/io.zig
index c6fd5502a2..605553b0ea 100644
--- a/std/io.zig
+++ b/std/io.zig
@@ -224,7 +224,7 @@ pub const File = struct {
};
}
},
- else => @compileError("unsupported OS"),
+ else => @compileError("unsupported OS: " ++ @tagName(builtin.os)),
}
}
diff --git a/std/os/index.zig b/std/os/index.zig
index e338359522..fd8eb84ab4 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -148,7 +148,7 @@ pub coldcc fn abort() -> noreturn {
}
/// Exits the program cleanly with the specified status code.
-pub coldcc fn exit(status: i32) -> noreturn {
+pub coldcc fn exit(status: u8) -> noreturn {
if (builtin.link_libc) {
c.exit(status);
}
@@ -157,14 +157,7 @@ pub coldcc fn exit(status: i32) -> noreturn {
posix.exit(status);
},
Os.windows => {
- // Map a possibly negative status code to a non-negative status for the systems default
- // integer width.
- const p_status = if (@sizeOf(c_uint) < @sizeOf(u32))
- @truncate(c_uint, @bitCast(u32, status))
- else
- c_uint(@bitCast(u32, status));
-
- windows.ExitProcess(p_status);
+ windows.ExitProcess(status);
},
else => @compileError("Unsupported OS"),
}
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index 55d28a2d0e..77bb7316a9 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -21,8 +21,7 @@ comptime {
}
extern fn zenMain() -> noreturn {
- root.main() catch std.os.posix.exit(1);
- std.os.posix.exit(0);
+ std.os.posix.exit(callMain());
}
nakedcc fn _start() -> noreturn {
@@ -43,29 +42,55 @@ nakedcc fn _start() -> noreturn {
extern fn WinMainCRTStartup() -> noreturn {
@setAlignStack(16);
- root.main() catch std.os.windows.ExitProcess(1);
- std.os.windows.ExitProcess(0);
+ std.os.windows.ExitProcess(callMain());
}
fn posixCallMainAndExit() -> noreturn {
const argc = *argc_ptr;
const argv = @ptrCast(&&u8, &argc_ptr[1]);
const envp = @ptrCast(&?&u8, &argv[argc + 1]);
- callMain(argc, argv, envp) catch std.os.posix.exit(1);
- std.os.posix.exit(0);
+ std.os.posix.exit(callMainWithArgs(argc, argv, envp));
}
-fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {
+fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) -> u8 {
std.os.ArgIteratorPosix.raw = argv[0..argc];
var env_count: usize = 0;
while (envp[env_count] != null) : (env_count += 1) {}
std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];
- return root.main();
+ return callMain();
}
extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {
- callMain(usize(c_argc), c_argv, c_envp) catch return 1;
- return 0;
+ return callMainWithArgs(usize(c_argc), c_argv, c_envp);
+}
+
+fn callMain() -> u8 {
+ switch (@typeId(@typeOf(root.main).ReturnType)) {
+ builtin.TypeId.NoReturn => {
+ root.main();
+ },
+ builtin.TypeId.Void => {
+ root.main();
+ return 0;
+ },
+ builtin.TypeId.Int => {
+ if (@typeOf(root.main).ReturnType.bit_count != 8) {
+ @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
+ }
+ return root.main();
+ },
+ builtin.TypeId.ErrorUnion => {
+ root.main() catch |err| {
+ std.debug.warn("error: {}\n", @errorName(err));
+ if (@errorReturnTrace()) |trace| {
+ std.debug.dumpStackTrace(trace);
+ }
+ return 1;
+ };
+ return 0;
+ },
+ else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"),
+ }
}
diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig
index b293a1e64d..4722aff07a 100644
--- a/std/special/build_runner.zig
+++ b/std/special/build_runner.zig
@@ -14,7 +14,7 @@ pub fn main() -> %void {
var arg_it = os.args();
// TODO use a more general purpose allocator here
- var inc_allocator = std.heap.IncrementingAllocator.init(40 * 1024 * 1024) catch unreachable;
+ var inc_allocator = try std.heap.IncrementingAllocator.init(40 * 1024 * 1024);
defer inc_allocator.deinit();
const allocator = &inc_allocator.allocator;
@@ -107,12 +107,12 @@ pub fn main() -> %void {
return usageAndErr(&builder, false, try stderr_stream);
}
} else {
- targets.append(arg) catch unreachable;
+ try targets.append(arg);
}
}
builder.setInstallPrefix(prefix);
- root.build(&builder) catch unreachable;
+ try root.build(&builder);
if (builder.validateUserInputDidItFail())
return usageAndErr(&builder, true, try stderr_stream);
@@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
// run the build script to collect the options
if (!already_ran_build) {
builder.setInstallPrefix(null);
- root.build(builder) catch unreachable;
+ try root.build(builder);
}
// This usage text has to be synchronized with src/main.cpp
diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig
index 027251c52c..c5c0bad40c 100644
--- a/std/special/test_runner.zig
+++ b/std/special/test_runner.zig
@@ -8,14 +8,7 @@ pub fn main() -> %void {
for (test_fn_list) |test_fn, i| {
warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
- if (builtin.is_test) {
- test_fn.func() catch unreachable;
- } else {
- test_fn.func() catch |err| {
- warn("{}\n", err);
- return err;
- };
- }
+ try test_fn.func();
warn("OK\n");
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 005fe55e41..6b2ea545ed 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,15 @@
const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompileErrorContext) {
+ cases.add("wrong return type for main",
+ \\pub fn main() -> f32 { }
+ , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
+
+ cases.add("double ?? on main return value",
+ \\pub fn main() -> ??void {
+ \\}
+ , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
+
cases.add("bad identifier in function with struct defined inside function which references local const",
\\export fn entry() {
\\ const BlockKind = u32;
@@ -1059,15 +1068,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
,
".tmp_source.zig:2:5: error: expected type 'void', found 'error'");
- cases.add("wrong return type for main",
- \\pub fn main() { }
- , ".tmp_source.zig:1:15: error: expected return type of main to be '%void', instead is 'void'");
-
- cases.add("double ?? on main return value",
- \\pub fn main() -> ??void {
- \\}
- , ".tmp_source.zig:1:18: error: expected return type of main to be '%void', instead is '??void'");
-
cases.add("invalid pointer for var type",
\\extern fn ext() -> usize;
\\var bytes: [ext()]u8 = undefined;
diff --git a/test/debug_safety.zig b/test/debug_safety.zig
index fde5b061ee..b32ffb34f0 100644
--- a/test/debug_safety.zig
+++ b/test/debug_safety.zig
@@ -2,7 +2,7 @@ const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompareOutputContext) {
cases.addDebugSafety("calling panic",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\pub fn main() -> %void {
@@ -11,7 +11,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("out of bounds slice access",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\pub fn main() -> %void {
@@ -25,7 +25,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer addition overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -39,7 +39,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer subtraction overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -53,7 +53,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer multiplication overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -67,7 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer negation overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -81,7 +81,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed integer division overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -95,7 +95,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed shift left overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -109,7 +109,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("unsigned shift left overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -123,7 +123,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed shift right overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -137,7 +137,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("unsigned shift right overflow",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -151,7 +151,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer division by zero",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -164,7 +164,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("exact division failure",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -178,7 +178,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("cast []u8 to bigger slice of wrong size",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -192,7 +192,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("value does not fit in shortening cast",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -206,7 +206,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed integer not fitting in cast to unsigned integer",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@@ -220,7 +220,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("unwrap error",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) {
\\ @import("std").os.exit(126); // good
\\ }
@@ -236,7 +236,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("cast integer to error and no code matches",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\pub fn main() -> %void {
@@ -248,7 +248,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("@alignCast misaligned",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Wrong;
@@ -265,7 +265,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("bad union field access",
- \\pub fn panic(message: []const u8) -> noreturn {
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\
diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig
index 50fa10c593..b7b288f7a1 100644
--- a/test/standalone/issue_339/build.zig
+++ b/test/standalone/issue_339/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
const obj = b.addObject("test", "test.zig");
const test_step = b.step("test", "Test the program");
diff --git a/test/standalone/issue_339/test.zig b/test/standalone/issue_339/test.zig
index e0d6c3f3e5..dd97ffa9b1 100644
--- a/test/standalone/issue_339/test.zig
+++ b/test/standalone/issue_339/test.zig
@@ -1,4 +1,5 @@
-pub fn panic(msg: []const u8) -> noreturn { @breakpoint(); while (true) {} }
+const StackTrace = @import("builtin").StackTrace;
+pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) -> noreturn { @breakpoint(); while (true) {} }
fn bar() -> %void {}
diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig
index 044787dd4d..f40011d834 100644
--- a/test/standalone/pkg_import/build.zig
+++ b/test/standalone/pkg_import/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
const exe = b.addExecutable("test", "test.zig");
exe.addPackagePath("my_pkg", "pkg.zig");
diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig
index 03398f1a41..beca6bc304 100644
--- a/test/standalone/use_alias/build.zig
+++ b/test/standalone/use_alias/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) {
+pub fn build(b: &Builder) -> %void {
b.addCIncludePath(".");
const main = b.addTest("main.zig");
diff --git a/test/tests.zig b/test/tests.zig
index 20e1e94459..5fd995f034 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -50,6 +50,7 @@ const test_targets = []TestTarget {
};
error TestFailed;
+error CompilationIncorrectlySucceeded;
const max_stdout_size = 1 * 1024 * 1024; // 1 MB
@@ -607,8 +608,7 @@ pub const CompileErrorContext = struct {
switch (term) {
Term.Exited => |code| {
if (code == 0) {
- warn("Compilation incorrectly succeeded\n");
- return error.TestFailed;
+ return error.CompilationIncorrectlySucceeded;
}
},
else => {
--
cgit v1.2.3