authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-01-15 16:28:30-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-01-15 16:28:30-05:00
log5a4968484b1ff761766180204151e77a701cff68
treec98f5386609d3fed67da3a27be0ac9b1ea3402bb
parent793f031c4ca7fdd230ef262895acf3e454be49dd
parent6ec9933fd80013104982debab9fbff1463582f19

Merge branch 'wip-err-ret-trace'


27 files changed, 605 insertions(+), 181 deletions(-)

build.zig+6-6
...@@ -10,7 +10,7 @@ const ArrayList = std.ArrayList;...@@ -10,7 +10,7 @@ const ArrayList = std.ArrayList;
10const Buffer = std.Buffer;10const Buffer = std.Buffer;
11const io = std.io;11const io = std.io;
1212
13pub fn build(b: &Builder) {13pub fn build(b: &Builder) -> %void {
14 const mode = b.standardReleaseOptions();14 const mode = b.standardReleaseOptions();
1515
16 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");16 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
...@@ -36,7 +36,7 @@ pub fn build(b: &Builder) {...@@ -36,7 +36,7 @@ pub fn build(b: &Builder) {
36 const test_step = b.step("test", "Run all the tests");36 const test_step = b.step("test", "Run all the tests");
3737
38 // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library38 // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library
39 const build_info = b.exec([][]const u8{b.zig_exe, "BUILD_INFO"});39 const build_info = try b.exec([][]const u8{b.zig_exe, "BUILD_INFO"});
40 var index: usize = 0;40 var index: usize = 0;
41 const cmake_binary_dir = nextValue(&index, build_info);41 const cmake_binary_dir = nextValue(&index, build_info);
42 const cxx_compiler = nextValue(&index, build_info);42 const cxx_compiler = nextValue(&index, build_info);
...@@ -68,7 +68,7 @@ pub fn build(b: &Builder) {...@@ -68,7 +68,7 @@ pub fn build(b: &Builder) {
68 dependOnLib(exe, llvm);68 dependOnLib(exe, llvm);
6969
70 if (exe.target.getOs() == builtin.Os.linux) {70 if (exe.target.getOs() == builtin.Os.linux) {
71 const libstdcxx_path_padded = b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});71 const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});
72 const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next();72 const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next();
73 exe.addObjectFile(libstdcxx_path);73 exe.addObjectFile(libstdcxx_path);
7474
...@@ -155,9 +155,9 @@ const LibraryDep = struct {...@@ -155,9 +155,9 @@ const LibraryDep = struct {
155};155};
156156
157fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep {157fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep {
158 const libs_output = b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});158 const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
159 const includes_output = b.exec([][]const u8{llvm_config_exe, "--includedir"});159 const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"});
160 const libdir_output = b.exec([][]const u8{llvm_config_exe, "--libdir"});160 const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"});
161161
162 var result = LibraryDep {162 var result = LibraryDep {
163 .libs = ArrayList([]const u8).init(b.allocator),163 .libs = ArrayList([]const u8).init(b.allocator),
doc/langref.html.in+8
...@@ -142,6 +142,7 @@...@@ -142,6 +142,7 @@
142 <li><a href="#builtin-TagType">@TagType</a></li>142 <li><a href="#builtin-TagType">@TagType</a></li>
143 <li><a href="#builtin-EnumTagType">@EnumTagType</a></li>143 <li><a href="#builtin-EnumTagType">@EnumTagType</a></li>
144 <li><a href="#builtin-errorName">@errorName</a></li>144 <li><a href="#builtin-errorName">@errorName</a></li>
145 <li><a href="#builtin-errorReturnTrace">@errorReturnTrace</a></li>
145 <li><a href="#builtin-fence">@fence</a></li>146 <li><a href="#builtin-fence">@fence</a></li>
146 <li><a href="#builtin-fieldParentPtr">@fieldParentPtr</a></li>147 <li><a href="#builtin-fieldParentPtr">@fieldParentPtr</a></li>
147 <li><a href="#builtin-frameAddress">@frameAddress</a></li>148 <li><a href="#builtin-frameAddress">@frameAddress</a></li>
...@@ -4412,6 +4413,13 @@ test.zig:6:2: error: found compile log statement...@@ -4412,6 +4413,13 @@ test.zig:6:2: error: found compile log statement
4412 or all calls have a compile-time known value for <code>err</code>, then no4413 or all calls have a compile-time known value for <code>err</code>, then no
4413 error name table will be generated.4414 error name table will be generated.
4414 </p>4415 </p>
4416 <h3 id="builtin-errorReturnTrace">@errorReturnTrace</h3>
4417 <pre><code class="zig">@errorReturnTrace() -&gt; ?&amp;builtin.StackTrace</code></pre>
4418 <p>
4419 If the binary is built with error return tracing, and this function is invoked in a
4420 function that calls a function with an error or error union return type, returns a
4421 stack trace object. Otherwise returns `null`.
4422 </p>
4415 <h3 id="builtin-fence">@fence</h3>4423 <h3 id="builtin-fence">@fence</h3>
4416 <pre><code class="zig">@fence(order: AtomicOrder)</code></pre>4424 <pre><code class="zig">@fence(order: AtomicOrder)</code></pre>
4417 <p>4425 <p>
example/mix_o_files/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) {3pub fn build(b: &Builder) -> %void {
4 const obj = b.addObject("base64", "base64.zig");4 const obj = b.addObject("base64", "base64.zig");
55
6 const exe = b.addCExecutable("test");6 const exe = b.addCExecutable("test");
example/shared_library/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) {3pub fn build(b: &Builder) -> %void {
4 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));4 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
55
6 const exe = b.addCExecutable("test");6 const exe = b.addCExecutable("test");
src/all_types.hpp+13
...@@ -1205,6 +1205,7 @@ struct FnTableEntry {...@@ -1205,6 +1205,7 @@ struct FnTableEntry {
1205 uint32_t alignstack_value;1205 uint32_t alignstack_value;
12061206
1207 ZigList<FnExport> export_list;1207 ZigList<FnExport> export_list;
1208 bool calls_errorable_function;
1208};1209};
12091210
1210uint32_t fn_table_entry_hash(FnTableEntry*);1211uint32_t fn_table_entry_hash(FnTableEntry*);
...@@ -1273,6 +1274,7 @@ enum BuiltinFnId {...@@ -1273,6 +1274,7 @@ enum BuiltinFnId {
1273 BuiltinFnIdSetAlignStack,1274 BuiltinFnIdSetAlignStack,
1274 BuiltinFnIdArgType,1275 BuiltinFnIdArgType,
1275 BuiltinFnIdExport,1276 BuiltinFnIdExport,
1277 BuiltinFnIdErrorReturnTrace,
1276};1278};
12771279
1278struct BuiltinFnEntry {1280struct BuiltinFnEntry {
...@@ -1498,6 +1500,7 @@ struct CodeGen {...@@ -1498,6 +1500,7 @@ struct CodeGen {
1498 Buf triple_str;1500 Buf triple_str;
1499 BuildMode build_mode;1501 BuildMode build_mode;
1500 bool is_test_build;1502 bool is_test_build;
1503 bool have_err_ret_tracing;
1501 uint32_t target_os_index;1504 uint32_t target_os_index;
1502 uint32_t target_arch_index;1505 uint32_t target_arch_index;
1503 uint32_t target_environ_index;1506 uint32_t target_environ_index;
...@@ -1530,6 +1533,7 @@ struct CodeGen {...@@ -1530,6 +1533,7 @@ struct CodeGen {
1530 FnTableEntry *panic_fn;1533 FnTableEntry *panic_fn;
1531 LLVMValueRef cur_ret_ptr;1534 LLVMValueRef cur_ret_ptr;
1532 LLVMValueRef cur_fn_val;1535 LLVMValueRef cur_fn_val;
1536 LLVMValueRef cur_err_ret_trace_val;
1533 bool c_want_stdint;1537 bool c_want_stdint;
1534 bool c_want_stdbool;1538 bool c_want_stdbool;
1535 AstNode *root_export_decl;1539 AstNode *root_export_decl;
...@@ -1572,6 +1576,8 @@ struct CodeGen {...@@ -1572,6 +1576,8 @@ struct CodeGen {
1572 size_t largest_err_name_len;1576 size_t largest_err_name_len;
1573 LLVMValueRef safety_crash_err_fn;1577 LLVMValueRef safety_crash_err_fn;
15741578
1579 LLVMValueRef return_err_fn;
1580
1575 IrInstruction *invalid_instruction;1581 IrInstruction *invalid_instruction;
1576 ConstExprValue const_void_val;1582 ConstExprValue const_void_val;
15771583
...@@ -1595,6 +1601,8 @@ struct CodeGen {...@@ -1595,6 +1601,8 @@ struct CodeGen {
1595 ZigList<AstNode *> tld_ref_source_node_stack;1601 ZigList<AstNode *> tld_ref_source_node_stack;
15961602
1597 TypeTableEntry *align_amt_type;1603 TypeTableEntry *align_amt_type;
1604 TypeTableEntry *stack_trace_type;
1605 TypeTableEntry *ptr_to_stack_trace_type;
1598};1606};
15991607
1600enum VarLinkage {1608enum VarLinkage {
...@@ -1896,6 +1904,7 @@ enum IrInstructionId {...@@ -1896,6 +1904,7 @@ enum IrInstructionId {
1896 IrInstructionIdSetAlignStack,1904 IrInstructionIdSetAlignStack,
1897 IrInstructionIdArgType,1905 IrInstructionIdArgType,
1898 IrInstructionIdExport,1906 IrInstructionIdExport,
1907 IrInstructionIdErrorReturnTrace,
1899};1908};
19001909
1901struct IrInstruction {1910struct IrInstruction {
...@@ -2717,6 +2726,10 @@ struct IrInstructionExport {...@@ -2717,6 +2726,10 @@ struct IrInstructionExport {
2717 IrInstruction *target;2726 IrInstruction *target;
2718};2727};
27192728
2729struct IrInstructionErrorReturnTrace {
2730 IrInstruction base;
2731};
2732
2720static const size_t slice_ptr_index = 0;2733static const size_t slice_ptr_index = 0;
2721static const size_t slice_len_index = 1;2734static const size_t slice_len_index = 1;
27222735
src/analyze.cpp+47-12
...@@ -869,6 +869,16 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) {...@@ -869,6 +869,16 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) {
869 zig_unreachable();869 zig_unreachable();
870}870}
871871
872TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) {
873 if (g->stack_trace_type == nullptr) {
874 ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace");
875 assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType);
876 g->stack_trace_type = stack_trace_type_val->data.x_type;
877 g->ptr_to_stack_trace_type = get_pointer_to_type(g, g->stack_trace_type, false);
878 }
879 return g->ptr_to_stack_trace_type;
880}
881
872TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {882TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
873 auto table_entry = g->fn_type_table.maybe_get(fn_type_id);883 auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
874 if (table_entry) {884 if (table_entry) {
...@@ -915,10 +925,16 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {...@@ -915,10 +925,16 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
915 if (!skip_debug_info) {925 if (!skip_debug_info) {
916 bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&926 bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&
917 handle_is_ptr(fn_type_id->return_type);927 handle_is_ptr(fn_type_id->return_type);
928 bool prefix_arg_error_return_trace = g->have_err_ret_tracing &&
929 (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
930 fn_type_id->return_type->id == TypeTableEntryIdPureError);
918 // +1 for maybe making the first argument the return value931 // +1 for maybe making the first argument the return value
919 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count);932 // +1 for maybe last argument the error return trace
920 // +1 because 0 is the return type and +1 for maybe making first arg ret val933 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(2 + fn_type_id->param_count);
921 ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(2 + fn_type_id->param_count);934 // +1 because 0 is the return type and
935 // +1 for maybe making first arg ret val and
936 // +1 for maybe last argument the error return trace
937 ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(3 + fn_type_id->param_count);
922 param_di_types[0] = fn_type_id->return_type->di_type;938 param_di_types[0] = fn_type_id->return_type->di_type;
923 size_t gen_param_index = 0;939 size_t gen_param_index = 0;
924 TypeTableEntry *gen_return_type;940 TypeTableEntry *gen_return_type;
...@@ -936,6 +952,14 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {...@@ -936,6 +952,14 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
936 }952 }
937 fn_type->data.fn.gen_return_type = gen_return_type;953 fn_type->data.fn.gen_return_type = gen_return_type;
938954
955 if (prefix_arg_error_return_trace) {
956 TypeTableEntry *gen_type = get_ptr_to_stack_trace_type(g);
957 gen_param_types[gen_param_index] = gen_type->type_ref;
958 gen_param_index += 1;
959 // after the gen_param_index += 1 because 0 is the return type
960 param_di_types[gen_param_index] = gen_type->di_type;
961 }
962
939 fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count);963 fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count);
940 for (size_t i = 0; i < fn_type_id->param_count; i += 1) {964 for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
941 FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i];965 FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i];
...@@ -1168,6 +1192,9 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1168,6 +1192,9 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1168 }1192 }
11691193
1170 TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);1194 TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
1195 if (type_is_invalid(type_entry)) {
1196 return g->builtin_types.entry_invalid;
1197 }
1171 if (fn_type_id.cc != CallingConventionUnspecified) {1198 if (fn_type_id.cc != CallingConventionUnspecified) {
1172 type_ensure_zero_bits_known(g, type_entry);1199 type_ensure_zero_bits_known(g, type_entry);
1173 if (!type_has_bits(type_entry)) {1200 if (!type_has_bits(type_entry)) {
...@@ -2558,7 +2585,7 @@ static bool scope_is_root_decls(Scope *scope) {...@@ -2558,7 +2585,7 @@ static bool scope_is_root_decls(Scope *scope) {
25582585
2559static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) {2586static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) {
2560 add_node_error(g, proto_node,2587 add_node_error(g, proto_node,
2561 buf_sprintf("expected 'fn([]const u8) -> unreachable', found '%s'",2588 buf_sprintf("expected 'fn([]const u8, ?&builtin.StackTrace) -> unreachable', found '%s'",
2562 buf_ptr(&fn_type->name)));2589 buf_ptr(&fn_type->name)));
2563}2590}
25642591
...@@ -2567,7 +2594,7 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {...@@ -2567,7 +2594,7 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
2567 assert(proto_node->type == NodeTypeFnProto);2594 assert(proto_node->type == NodeTypeFnProto);
2568 TypeTableEntry *fn_type = panic_fn->type_entry;2595 TypeTableEntry *fn_type = panic_fn->type_entry;
2569 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;2596 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
2570 if (fn_type_id->param_count != 1) {2597 if (fn_type_id->param_count != 2) {
2571 return wrong_panic_prototype(g, proto_node, fn_type);2598 return wrong_panic_prototype(g, proto_node, fn_type);
2572 }2599 }
2573 TypeTableEntry *const_u8_ptr = get_pointer_to_type(g, g->builtin_types.entry_u8, true);2600 TypeTableEntry *const_u8_ptr = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
...@@ -2576,6 +2603,11 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {...@@ -2576,6 +2603,11 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
2576 return wrong_panic_prototype(g, proto_node, fn_type);2603 return wrong_panic_prototype(g, proto_node, fn_type);
2577 }2604 }
25782605
2606 TypeTableEntry *nullable_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g));
2607 if (fn_type_id->param_info[1].type != nullable_ptr_to_stack_trace_type) {
2608 return wrong_panic_prototype(g, proto_node, fn_type);
2609 }
2610
2579 TypeTableEntry *actual_return_type = fn_type_id->return_type;2611 TypeTableEntry *actual_return_type = fn_type_id->return_type;
2580 if (actual_return_type != g->builtin_types.entry_unreachable) {2612 if (actual_return_type != g->builtin_types.entry_unreachable) {
2581 return wrong_panic_prototype(g, proto_node, fn_type);2613 return wrong_panic_prototype(g, proto_node, fn_type);
...@@ -2680,13 +2712,6 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {...@@ -2680,13 +2712,6 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
2680 {2712 {
2681 if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {2713 if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
2682 g->main_fn = fn_table_entry;2714 g->main_fn = fn_table_entry;
2683 TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
2684 TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
2685 if (actual_return_type != err_void) {
2686 add_node_error(g, fn_proto->return_type,
2687 buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
2688 buf_ptr(&actual_return_type->name)));
2689 }
2690 } else if ((import->package == g->panic_package || g->have_pub_panic) &&2715 } else if ((import->package == g->panic_package || g->have_pub_panic) &&
2691 buf_eql_str(&fn_table_entry->symbol_name, "panic"))2716 buf_eql_str(&fn_table_entry->symbol_name, "panic"))
2692 {2717 {
...@@ -5527,3 +5552,13 @@ bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) {...@@ -5527,3 +5552,13 @@ bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) {
5527 return a == b;5552 return a == b;
5528}5553}
55295554
5555ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
5556 Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name));
5557 resolve_top_level_decl(codegen, tld, false, nullptr);
5558 assert(tld->id == TldIdVar);
5559 TldVar *tld_var = (TldVar *)tld;
5560 ConstExprValue *var_value = tld_var->var->value;
5561 assert(var_value != nullptr);
5562 return var_value;
5563}
5564
src/analyze.hpp+5
...@@ -185,4 +185,9 @@ PackageTableEntry *new_anonymous_package(void);...@@ -185,4 +185,9 @@ PackageTableEntry *new_anonymous_package(void);
185Buf *const_value_to_buffer(ConstExprValue *const_val);185Buf *const_value_to_buffer(ConstExprValue *const_val);
186void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc);186void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc);
187187
188
189ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);
190TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g);
191
192
188#endif193#endif
src/codegen.cpp+231-16
...@@ -404,6 +404,19 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {...@@ -404,6 +404,19 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
404 zig_unreachable();404 zig_unreachable();
405}405}
406406
407static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_entry) {
408 if (!g->have_err_ret_tracing) {
409 return UINT32_MAX;
410 }
411 TypeTableEntry *fn_type = fn_table_entry->type_entry;
412 TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
413 if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) {
414 return UINT32_MAX;
415 }
416 bool first_arg_ret = type_has_bits(return_type) && handle_is_ptr(return_type);
417 return first_arg_ret ? 1 : 0;
418}
419
407static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {420static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
408 if (fn_table_entry->llvm_value)421 if (fn_table_entry->llvm_value)
409 return fn_table_entry->llvm_value;422 return fn_table_entry->llvm_value;
...@@ -483,7 +496,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {...@@ -483,7 +496,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
483 LLVMSetUnnamedAddr(fn_table_entry->llvm_value, true);496 LLVMSetUnnamedAddr(fn_table_entry->llvm_value, true);
484 }497 }
485498
486 if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) {499 TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
500 if (return_type->id == TypeTableEntryIdUnreachable) {
487 addLLVMFnAttr(fn_table_entry->llvm_value, "noreturn");501 addLLVMFnAttr(fn_table_entry->llvm_value, "noreturn");
488 }502 }
489503
...@@ -520,13 +534,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {...@@ -520,13 +534,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
520 // use the ABI alignment, which is fine.534 // use the ABI alignment, which is fine.
521 }535 }
522536
523 if (!type_has_bits(fn_type->data.fn.fn_type_id.return_type)) {537 if (!type_has_bits(return_type)) {
524 // nothing to do538 // nothing to do
525 } else if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdPointer ||539 } else if (return_type->id == TypeTableEntryIdPointer || return_type->id == TypeTableEntryIdFn) {
526 fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdFn)
527 {
528 addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");540 addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
529 } else if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type) &&541 } else if (handle_is_ptr(return_type) &&
530 calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc))542 calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc))
531 {543 {
532 addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");544 addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
...@@ -563,6 +575,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {...@@ -563,6 +575,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
563 }575 }
564 }576 }
565577
578 uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
579 if (err_ret_trace_arg_index != UINT32_MAX) {
580 addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
581 }
582
566 return fn_table_entry->llvm_value;583 return fn_table_entry->llvm_value;
567}584}
568585
...@@ -864,16 +881,25 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) {...@@ -864,16 +881,25 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) {
864 return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0));881 return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0));
865}882}
866883
867static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) {884static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg) {
868 assert(g->panic_fn != nullptr);885 assert(g->panic_fn != nullptr);
869 LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);886 LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);
870 LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);887 LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);
871 ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, "");888 if (stack_trace_arg == nullptr) {
889 TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
890 stack_trace_arg = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
891 }
892 LLVMValueRef args[] = {
893 msg_arg,
894 stack_trace_arg,
895 };
896 LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, "");
897 LLVMSetTailCall(call_instruction, true);
872 LLVMBuildUnreachable(g->builder);898 LLVMBuildUnreachable(g->builder);
873}899}
874900
875static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) {901static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) {
876 gen_panic(g, get_panic_msg_ptr_val(g, msg_id));902 gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr);
877}903}
878904
879static LLVMValueRef get_memcpy_fn_val(CodeGen *g) {905static LLVMValueRef get_memcpy_fn_val(CodeGen *g) {
...@@ -895,6 +921,87 @@ static LLVMValueRef get_memcpy_fn_val(CodeGen *g) {...@@ -895,6 +921,87 @@ static LLVMValueRef get_memcpy_fn_val(CodeGen *g) {
895 return g->memcpy_fn_val;921 return g->memcpy_fn_val;
896}922}
897923
924static LLVMValueRef get_return_err_fn(CodeGen *g) {
925 if (g->return_err_fn != nullptr)
926 return g->return_err_fn;
927
928 assert(g->err_tag_type != nullptr);
929
930 LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
931
932 LLVMTypeRef arg_types[] = {
933 // error return trace pointer
934 get_ptr_to_stack_trace_type(g)->type_ref,
935 // return address
936 ptr_u8,
937 };
938 LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
939
940 Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_return_error"), false);
941 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
942 addLLVMFnAttr(fn_val, "cold");
943 LLVMSetLinkage(fn_val, LLVMInternalLinkage);
944 LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
945 addLLVMFnAttr(fn_val, "nounwind");
946 add_uwtable_attr(g, fn_val);
947 addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
948 addLLVMArgAttr(fn_val, (unsigned)1, "nonnull");
949 if (g->build_mode == BuildModeDebug) {
950 ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
951 ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
952 }
953
954 LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
955 LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
956 LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
957 LLVMPositionBuilderAtEnd(g->builder, entry_block);
958 ZigLLVMClearCurrentDebugLocation(g->builder);
959
960 LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
961
962 // stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address;
963
964 LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0);
965 size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
966 LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, "");
967 size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
968 LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, "");
969
970 TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
971 size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
972 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
973 size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
974 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
975
976 LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, "");
977 LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, "");
978 LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, len_value, "");
979 LLVMValueRef address_indices[] = {
980 modded_val,
981 };
982
983 LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, "");
984 LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, "");
985
986 LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, LLVMGetParam(fn_val, 1), usize_type_ref, "");
987
988 LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, return_address, usize_type_ref, "");
989 gen_store_untyped(g, address_value, address_slot, 0, false);
990
991 // stack_trace.index += 1;
992 LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), "");
993 gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false);
994
995 // return;
996 LLVMBuildRetVoid(g->builder);
997
998 LLVMPositionBuilderAtEnd(g->builder, prev_block);
999 LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
1000
1001 g->return_err_fn = fn_val;
1002 return fn_val;
1003}
1004
898static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {1005static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
899 if (g->safety_crash_err_fn != nullptr)1006 if (g->safety_crash_err_fn != nullptr)
900 return g->safety_crash_err_fn;1007 return g->safety_crash_err_fn;
...@@ -953,7 +1060,11 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {...@@ -953,7 +1060,11 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
953 LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2);1060 LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2);
9541061
955 Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false);1062 Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false);
956 LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), &g->err_tag_type->type_ref, 1, false);1063 LLVMTypeRef arg_types[] = {
1064 g->ptr_to_stack_trace_type->type_ref,
1065 g->err_tag_type->type_ref,
1066 };
1067 LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
957 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);1068 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
958 addLLVMFnAttr(fn_val, "noreturn");1069 addLLVMFnAttr(fn_val, "noreturn");
959 addLLVMFnAttr(fn_val, "cold");1070 addLLVMFnAttr(fn_val, "cold");
...@@ -975,7 +1086,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {...@@ -975,7 +1086,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
975 LLVMPositionBuilderAtEnd(g->builder, entry_block);1086 LLVMPositionBuilderAtEnd(g->builder, entry_block);
976 ZigLLVMClearCurrentDebugLocation(g->builder);1087 ZigLLVMClearCurrentDebugLocation(g->builder);
9771088
978 LLVMValueRef err_val = LLVMGetParam(fn_val, 0);1089 LLVMValueRef err_val = LLVMGetParam(fn_val, 1);
9791090
980 LLVMValueRef err_table_indices[] = {1091 LLVMValueRef err_table_indices[] = {
981 LLVMConstNull(g->builtin_types.entry_usize->type_ref),1092 LLVMConstNull(g->builtin_types.entry_usize->type_ref),
...@@ -1005,7 +1116,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {...@@ -1005,7 +1116,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
1005 LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, "");1116 LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, "");
1006 gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type);1117 gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type);
10071118
1008 gen_panic(g, global_slice);1119 gen_panic(g, global_slice, LLVMGetParam(fn_val, 0));
10091120
1010 LLVMPositionBuilderAtEnd(g->builder, prev_block);1121 LLVMPositionBuilderAtEnd(g->builder, prev_block);
1011 LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);1122 LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
...@@ -1016,8 +1127,18 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {...@@ -1016,8 +1127,18 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
10161127
1017static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {1128static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
1018 LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);1129 LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
1019 ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified),1130 LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val;
1131 if (err_ret_trace_val == nullptr) {
1132 TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
1133 err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
1134 }
1135 LLVMValueRef args[] = {
1136 err_ret_trace_val,
1137 err_val,
1138 };
1139 LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified),
1020 ZigLLVM_FnInlineAuto, "");1140 ZigLLVM_FnInlineAuto, "");
1141 LLVMSetTailCall(call_instruction, true);
1021 LLVMBuildUnreachable(g->builder);1142 LLVMBuildUnreachable(g->builder);
1022}1143}
10231144
...@@ -1296,6 +1417,35 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {...@@ -1296,6 +1417,35 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
1296static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) {1417static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) {
1297 LLVMValueRef value = ir_llvm_value(g, return_instruction->value);1418 LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
1298 TypeTableEntry *return_type = return_instruction->value->value.type;1419 TypeTableEntry *return_type = return_instruction->value->value.type;
1420
1421 if (g->have_err_ret_tracing) {
1422 bool is_err_return = false;
1423 if (return_type->id == TypeTableEntryIdErrorUnion) {
1424 if (return_instruction->value->value.special == ConstValSpecialStatic) {
1425 is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr;
1426 } else if (return_instruction->value->value.special == ConstValSpecialRuntime) {
1427 is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
1428 // TODO: emit a branch to check if the return value is an error
1429 }
1430 } else if (return_type->id == TypeTableEntryIdPureError) {
1431 is_err_return = true;
1432 }
1433 if (is_err_return) {
1434 LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError");
1435 LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block);
1436
1437 LLVMValueRef return_err_fn = get_return_err_fn(g);
1438 LLVMValueRef args[] = {
1439 g->cur_err_ret_trace_val,
1440 block_address,
1441 };
1442 LLVMBuildBr(g->builder, return_block);
1443 LLVMPositionBuilderAtEnd(g->builder, return_block);
1444 LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 2,
1445 get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
1446 LLVMSetTailCall(call_instruction, true);
1447 }
1448 }
1299 if (handle_is_ptr(return_type)) {1449 if (handle_is_ptr(return_type)) {
1300 if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {1450 if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {
1301 assert(g->cur_ret_ptr);1451 assert(g->cur_ret_ptr);
...@@ -2330,7 +2480,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -2330,7 +2480,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
2330 TypeTableEntry *src_return_type = fn_type_id->return_type;2480 TypeTableEntry *src_return_type = fn_type_id->return_type;
2331 bool ret_has_bits = type_has_bits(src_return_type);2481 bool ret_has_bits = type_has_bits(src_return_type);
2332 bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);2482 bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
2333 size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0);2483 bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError);
2484 size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0);
2334 bool is_var_args = fn_type_id->is_var_args;2485 bool is_var_args = fn_type_id->is_var_args;
2335 LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);2486 LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
2336 size_t gen_param_index = 0;2487 size_t gen_param_index = 0;
...@@ -2338,6 +2489,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -2338,6 +2489,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
2338 gen_param_values[gen_param_index] = instruction->tmp_ptr;2489 gen_param_values[gen_param_index] = instruction->tmp_ptr;
2339 gen_param_index += 1;2490 gen_param_index += 1;
2340 }2491 }
2492 if (prefix_arg_err_ret_stack) {
2493 gen_param_values[gen_param_index] = g->cur_err_ret_trace_val;
2494 gen_param_index += 1;
2495 }
2341 for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) {2496 for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) {
2342 IrInstruction *param_instruction = instruction->args[call_i];2497 IrInstruction *param_instruction = instruction->args[call_i];
2343 TypeTableEntry *param_type = param_instruction->value.type;2498 TypeTableEntry *param_type = param_instruction->value.type;
...@@ -2881,6 +3036,16 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I...@@ -2881,6 +3036,16 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
2881 return target_val;3036 return target_val;
2882}3037}
28833038
3039static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable,
3040 IrInstructionErrorReturnTrace *instruction)
3041{
3042 TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
3043 if (g->cur_err_ret_trace_val == nullptr) {
3044 return LLVMConstNull(ptr_to_stack_trace_type->type_ref);
3045 }
3046 return g->cur_err_ret_trace_val;
3047}
3048
2884static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {3049static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
2885 switch (atomic_order) {3050 switch (atomic_order) {
2886 case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;3051 case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;
...@@ -3474,7 +3639,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec...@@ -3474,7 +3639,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec
3474}3639}
34753640
3476static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) {3641static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) {
3477 gen_panic(g, ir_llvm_value(g, instruction->msg));3642 gen_panic(g, ir_llvm_value(g, instruction->msg), nullptr);
3478 return nullptr;3643 return nullptr;
3479}3644}
34803645
...@@ -3654,6 +3819,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -3654,6 +3819,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
3654 return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction);3819 return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction);
3655 case IrInstructionIdAlignCast:3820 case IrInstructionIdAlignCast:
3656 return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction);3821 return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction);
3822 case IrInstructionIdErrorReturnTrace:
3823 return ir_render_error_return_trace(g, executable, (IrInstructionErrorReturnTrace *)instruction);
3657 }3824 }
3658 zig_unreachable();3825 zig_unreachable();
3659}3826}
...@@ -4493,7 +4660,8 @@ static void do_code_gen(CodeGen *g) {...@@ -4493,7 +4660,8 @@ static void do_code_gen(CodeGen *g) {
4493 LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);4660 LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);
4494 g->cur_fn = fn_table_entry;4661 g->cur_fn = fn_table_entry;
4495 g->cur_fn_val = fn;4662 g->cur_fn_val = fn;
4496 if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) {4663 TypeTableEntry *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
4664 if (handle_is_ptr(return_type)) {
4497 g->cur_ret_ptr = LLVMGetParam(fn, 0);4665 g->cur_ret_ptr = LLVMGetParam(fn, 0);
4498 } else {4666 } else {
4499 g->cur_ret_ptr = nullptr;4667 g->cur_ret_ptr = nullptr;
...@@ -4502,6 +4670,42 @@ static void do_code_gen(CodeGen *g) {...@@ -4502,6 +4670,42 @@ static void do_code_gen(CodeGen *g) {
4502 build_all_basic_blocks(g, fn_table_entry);4670 build_all_basic_blocks(g, fn_table_entry);
4503 clear_debug_source_node(g);4671 clear_debug_source_node(g);
45044672
4673 uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
4674 if (err_ret_trace_arg_index != UINT32_MAX) {
4675 g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index);
4676 } else if (g->have_err_ret_tracing && fn_table_entry->calls_errorable_function) {
4677 // TODO call graph analysis to find out what this number needs to be for every function
4678 static const size_t stack_trace_ptr_count = 30;
4679
4680 TypeTableEntry *usize = g->builtin_types.entry_usize;
4681 TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count);
4682 LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses",
4683 get_abi_alignment(g, array_type));
4684 g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
4685 size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
4686 LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, "");
4687 gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
4688
4689 size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
4690 LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, "");
4691
4692 TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
4693 size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
4694 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
4695 LLVMValueRef zero = LLVMConstNull(usize->type_ref);
4696 LLVMValueRef indices[] = {zero, zero};
4697 LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val,
4698 indices, 2, "");
4699 gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr,
4700 get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false));
4701
4702 size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
4703 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
4704 gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false));
4705 } else {
4706 g->cur_err_ret_trace_val = nullptr;
4707 }
4708
4505 // allocate temporary stack data4709 // allocate temporary stack data
4506 for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) {4710 for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) {
4507 IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);4711 IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
...@@ -5064,6 +5268,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -5064,6 +5268,7 @@ static void define_builtin_fns(CodeGen *g) {
5064 create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);5268 create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);
5065 create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2);5269 create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2);
5066 create_builtin_fn(g, BuiltinFnIdExport, "export", 3);5270 create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
5271 create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
5067}5272}
50685273
5069static const char *bool_to_str(bool b) {5274static const char *bool_to_str(bool b) {
...@@ -5088,6 +5293,12 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -5088,6 +5293,12 @@ static void define_builtin_compile_vars(CodeGen *g) {
5088 os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);5293 os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
5089 Buf *contents = buf_alloc();5294 Buf *contents = buf_alloc();
50905295
5296 buf_append_str(contents,
5297 "pub const StackTrace = struct {\n"
5298 " index: usize,\n"
5299 " instruction_addresses: []usize,\n"
5300 "};\n\n");
5301
5091 const char *cur_os = nullptr;5302 const char *cur_os = nullptr;
5092 {5303 {
5093 buf_appendf(contents, "pub const Os = enum {\n");5304 buf_appendf(contents, "pub const Os = enum {\n");
...@@ -5233,6 +5444,7 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -5233,6 +5444,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
5233 buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt);5444 buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt);
5234 buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));5445 buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
5235 buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));5446 buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));
5447 buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing));
52365448
5237 buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");5449 buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");
52385450
...@@ -5251,6 +5463,7 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -5251,6 +5463,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
5251 g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);5463 g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
5252 g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);5464 g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
5253 g->compile_var_import = add_source_file(g, g->compile_var_package, abs_full_path, contents);5465 g->compile_var_import = add_source_file(g, g->compile_var_package, abs_full_path, contents);
5466 scan_import(g, g->compile_var_import);
5254}5467}
52555468
5256static void init(CodeGen *g) {5469static void init(CodeGen *g) {
...@@ -5359,6 +5572,8 @@ static void init(CodeGen *g) {...@@ -5359,6 +5572,8 @@ static void init(CodeGen *g) {
5359 }5572 }
5360 }5573 }
53615574
5575 g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease;
5576
5362 define_builtin_fns(g);5577 define_builtin_fns(g);
5363 define_builtin_compile_vars(g);5578 define_builtin_compile_vars(g);
5364}5579}
src/ir.cpp+47-10
...@@ -572,6 +572,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) {...@@ -572,6 +572,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) {
572 return IrInstructionIdArgType;572 return IrInstructionIdArgType;
573}573}
574574
575static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) {
576 return IrInstructionIdErrorReturnTrace;
577}
578
575template<typename T>579template<typename T>
576static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {580static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
577 T *special_instruction = allocate<T>(1);581 T *special_instruction = allocate<T>(1);
...@@ -2305,6 +2309,12 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s...@@ -2305,6 +2309,12 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s
2305 return &instruction->base;2309 return &instruction->base;
2306}2310}
23072311
2312static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) {
2313 IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node);
2314
2315 return &instruction->base;
2316}
2317
2308static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {2318static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
2309 results[ReturnKindUnconditional] = 0;2319 results[ReturnKindUnconditional] = 0;
2310 results[ReturnKindError] = 0;2320 results[ReturnKindError] = 0;
...@@ -3731,6 +3741,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3731,6 +3741,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
37313741
3732 return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);3742 return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);
3733 }3743 }
3744 case BuiltinFnIdErrorReturnTrace:
3745 {
3746 return ir_build_error_return_trace(irb, scope, node);
3747 }
3734 }3748 }
3735 zig_unreachable();3749 zig_unreachable();
3736}3750}
...@@ -8230,16 +8244,6 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out)...@@ -8230,16 +8244,6 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out)
8230 return ir_resolve_bool(ira, value, out);8244 return ir_resolve_bool(ira, value, out);
8231}8245}
82328246
8233static ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
8234 Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name));
8235 resolve_top_level_decl(codegen, tld, false, nullptr);
8236 assert(tld->id == TldIdVar);
8237 TldVar *tld_var = (TldVar *)tld;
8238 ConstExprValue *var_value = tld_var->var->value;
8239 assert(var_value != nullptr);
8240 return var_value;
8241}
8242
8243static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) {8247static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) {
8244 if (type_is_invalid(value->value.type))8248 if (type_is_invalid(value->value.type))
8245 return false;8249 return false;
...@@ -9578,6 +9582,24 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi...@@ -9578,6 +9582,24 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
9578 return ira->codegen->builtin_types.entry_void;9582 return ira->codegen->builtin_types.entry_void;
9579}9583}
95809584
9585static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
9586 IrInstructionErrorReturnTrace *instruction)
9587{
9588 FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
9589 TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
9590 TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
9591 if (fn_entry == nullptr || !fn_entry->calls_errorable_function || !ira->codegen->have_err_ret_tracing) {
9592 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
9593 out_val->data.x_maybe = nullptr;
9594 return nullable_type;
9595 }
9596
9597 IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
9598 instruction->base.source_node);
9599 ir_link_new_instruction(new_instruction, &instruction->base);
9600 return nullable_type;
9601}
9602
9581static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,9603static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
9582 IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)9604 IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
9583{9605{
...@@ -10053,9 +10075,21 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -10053,9 +10075,21 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
10053 TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;10075 TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
10054 ir_add_alloca(ira, new_call_instruction, return_type);10076 ir_add_alloca(ira, new_call_instruction, return_type);
1005510077
10078 if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) {
10079 parent_fn_entry->calls_errorable_function = true;
10080 }
10081
10056 return ir_finish_anal(ira, return_type);10082 return ir_finish_anal(ira, return_type);
10057 }10083 }
1005810084
10085 FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
10086 assert(fn_type_id->return_type != nullptr);
10087 assert(parent_fn_entry != nullptr);
10088 if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) {
10089 parent_fn_entry->calls_errorable_function = true;
10090 }
10091
10092
10059 IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count);10093 IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count);
10060 size_t next_arg_index = 0;10094 size_t next_arg_index = 0;
10061 if (first_arg_ptr) {10095 if (first_arg_ptr) {
...@@ -15322,6 +15356,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -15322,6 +15356,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
15322 return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction);15356 return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction);
15323 case IrInstructionIdExport:15357 case IrInstructionIdExport:
15324 return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);15358 return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);
15359 case IrInstructionIdErrorReturnTrace:
15360 return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction);
15325 }15361 }
15326 zig_unreachable();15362 zig_unreachable();
15327}15363}
...@@ -15505,6 +15541,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -15505,6 +15541,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
15505 case IrInstructionIdOpaqueType:15541 case IrInstructionIdOpaqueType:
15506 case IrInstructionIdArgType:15542 case IrInstructionIdArgType:
15507 case IrInstructionIdTagType:15543 case IrInstructionIdTagType:
15544 case IrInstructionIdErrorReturnTrace:
15508 return false;15545 return false;
15509 case IrInstructionIdAsm:15546 case IrInstructionIdAsm:
15510 {15547 {
src/ir_print.cpp+7
...@@ -996,6 +996,10 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {...@@ -996,6 +996,10 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
996 }996 }
997}997}
998998
999static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {
1000 fprintf(irp->f, "@errorReturnTrace()");
1001}
1002
9991003
1000static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {1004static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1001 ir_print_prefix(irp, instruction);1005 ir_print_prefix(irp, instruction);
...@@ -1308,6 +1312,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1308,6 +1312,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1308 case IrInstructionIdExport:1312 case IrInstructionIdExport:
1309 ir_print_export(irp, (IrInstructionExport *)instruction);1313 ir_print_export(irp, (IrInstructionExport *)instruction);
1310 break;1314 break;
1315 case IrInstructionIdErrorReturnTrace:
1316 ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction);
1317 break;
1311 }1318 }
1312 fprintf(irp->f, "\n");1319 fprintf(irp->f, "\n");
1313}1320}
std/build.zig+4-6
...@@ -247,11 +247,11 @@ pub const Builder = struct {...@@ -247,11 +247,11 @@ pub const Builder = struct {
247 defer wanted_steps.deinit();247 defer wanted_steps.deinit();
248248
249 if (step_names.len == 0) {249 if (step_names.len == 0) {
250 wanted_steps.append(&self.default_step) catch unreachable;250 try wanted_steps.append(&self.default_step);
251 } else {251 } else {
252 for (step_names) |step_name| {252 for (step_names) |step_name| {
253 const s = try self.getTopLevelStepByName(step_name);253 const s = try self.getTopLevelStepByName(step_name);
254 wanted_steps.append(s) catch unreachable;254 try wanted_steps.append(s);
255 }255 }
256 }256 }
257257
...@@ -721,11 +721,9 @@ pub const Builder = struct {...@@ -721,11 +721,9 @@ pub const Builder = struct {
721 return error.FileNotFound;721 return error.FileNotFound;
722 }722 }
723723
724 pub fn exec(self: &Builder, argv: []const []const u8) -> []u8 {724 pub fn exec(self: &Builder, argv: []const []const u8) -> %[]u8 {
725 const max_output_size = 100 * 1024;725 const max_output_size = 100 * 1024;
726 const result = os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size) catch |err| {726 const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size);
727 std.debug.panic("Unable to spawn {}: {}", argv[0], @errorName(err));
728 };
729 switch (result.term) {727 switch (result.term) {
730 os.ChildProcess.Term.Exited => |code| {728 os.ChildProcess.Term.Exited => |code| {
731 if (code != 0) {729 if (code != 0) {
std/debug/index.zig+149-63
...@@ -13,6 +13,10 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;...@@ -13,6 +13,10 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
13error MissingDebugInfo;13error MissingDebugInfo;
14error InvalidDebugInfo;14error InvalidDebugInfo;
15error UnsupportedDebugInfo;15error UnsupportedDebugInfo;
16error UnknownObjectFormat;
17error TodoSupportCoffDebugInfo;
18error TodoSupportMachoDebugInfo;
19error TodoSupportCOFFDebugInfo;
1620
1721
18/// Tries to write to stderr, unbuffered, and ignores any error returned.22/// Tries to write to stderr, unbuffered, and ignores any error returned.
...@@ -37,10 +41,43 @@ fn getStderrStream() -> %&io.OutStream {...@@ -37,10 +41,43 @@ fn getStderrStream() -> %&io.OutStream {
37 }41 }
38}42}
3943
44var self_debug_info: ?&ElfStackTrace = null;
45pub fn getSelfDebugInfo() -> %&ElfStackTrace {
46 if (self_debug_info) |info| {
47 return info;
48 } else {
49 const info = try openSelfDebugInfo(global_allocator);
50 self_debug_info = info;
51 return info;
52 }
53}
54
55/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
56pub fn dumpCurrentStackTrace() {
57 const stderr = getStderrStream() catch return;
58 const debug_info = getSelfDebugInfo() catch |err| {
59 stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
60 return;
61 };
62 defer debug_info.close();
63 writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), 1) catch |err| {
64 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
65 return;
66 };
67}
68
40/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.69/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
41pub fn dumpStackTrace() {70pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) {
42 const stderr = getStderrStream() catch return;71 const stderr = getStderrStream() catch return;
43 writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return;72 const debug_info = getSelfDebugInfo() catch |err| {
73 stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
74 return;
75 };
76 defer debug_info.close();
77 writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {
78 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
79 return;
80 };
44}81}
4582
46/// This function invokes undefined behavior when `ok` is `false`.83/// This function invokes undefined behavior when `ok` is `false`.
...@@ -88,7 +125,21 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {...@@ -88,7 +125,21 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
88125
89 const stderr = getStderrStream() catch os.abort();126 const stderr = getStderrStream() catch os.abort();
90 stderr.print(format ++ "\n", args) catch os.abort();127 stderr.print(format ++ "\n", args) catch os.abort();
91 writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort();128 dumpCurrentStackTrace();
129
130 os.abort();
131}
132
133pub fn panicWithTrace(trace: &const builtin.StackTrace, comptime format: []const u8, args: ...) -> noreturn {
134 if (panicking) {
135 os.abort();
136 } else {
137 panicking = true;
138 }
139 const stderr = getStderrStream() catch os.abort();
140 stderr.print(format ++ "\n", args) catch os.abort();
141 dumpStackTrace(trace);
142 dumpCurrentStackTrace();
92143
93 os.abort();144 os.abort();
94}145}
...@@ -101,12 +152,91 @@ const RESET = "\x1b[0m";...@@ -101,12 +152,91 @@ const RESET = "\x1b[0m";
101error PathNotFound;152error PathNotFound;
102error InvalidDebugInfo;153error InvalidDebugInfo;
103154
104pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool,155pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
105 ignore_frame_count: usize) -> %void156 debug_info: &ElfStackTrace, tty_color: bool) -> %void
106{157{
158 var frame_index: usize = undefined;
159 var frames_left: usize = undefined;
160 if (stack_trace.index < stack_trace.instruction_addresses.len) {
161 frame_index = 0;
162 frames_left = stack_trace.index;
163 } else {
164 frame_index = (stack_trace.index + 1) % stack_trace.instruction_addresses.len;
165 frames_left = stack_trace.instruction_addresses.len;
166 }
167
168 while (frames_left != 0) : ({
169 frames_left -= 1;
170 frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
171 }) {
172 const return_address = stack_trace.instruction_addresses[frame_index];
173 try printSourceAtAddress(debug_info, out_stream, return_address);
174 }
175}
176
177pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator,
178 debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) -> %void
179{
180 var ignored_count: usize = 0;
181
182 var fp = @ptrToInt(@frameAddress());
183 while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) {
184 if (ignored_count < ignore_frame_count) {
185 ignored_count += 1;
186 continue;
187 }
188
189 const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize));
190 try printSourceAtAddress(debug_info, out_stream, return_address);
191 }
192}
193
194fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void {
195 if (builtin.os == builtin.Os.windows) {
196 return error.UnsupportedDebugInfo;
197 }
198 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
199 // at compile time. I'll call it issue #313
200 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
201
202 const compile_unit = findCompileUnit(debug_info, address) catch {
203 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
204 address);
205 return;
206 };
207 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
208 if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
209 defer line_info.deinit();
210 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
211 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
212 line_info.file_name, line_info.line, line_info.column,
213 address, compile_unit_name);
214 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
215 if (line_info.column == 0) {
216 try out_stream.write("\n");
217 } else {
218 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
219 try out_stream.writeByte(' ');
220 }}
221 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
222 }
223 } else |err| switch (err) {
224 error.EndOfFile, error.PathNotFound => {},
225 else => return err,
226 }
227 } else |err| switch (err) {
228 error.MissingDebugInfo, error.InvalidDebugInfo => {
229 try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
230 },
231 else => return err,
232 }
233}
234
235pub fn openSelfDebugInfo(allocator: &mem.Allocator) -> %&ElfStackTrace {
107 switch (builtin.object_format) {236 switch (builtin.object_format) {
108 builtin.ObjectFormat.elf => {237 builtin.ObjectFormat.elf => {
109 var stack_trace = ElfStackTrace {238 const st = try allocator.create(ElfStackTrace);
239 *st = ElfStackTrace {
110 .self_exe_file = undefined,240 .self_exe_file = undefined,
111 .elf = undefined,241 .elf = undefined,
112 .debug_info = undefined,242 .debug_info = undefined,
...@@ -117,12 +247,11 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty...@@ -117,12 +247,11 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
117 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),247 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
118 .compile_unit_list = ArrayList(CompileUnit).init(allocator),248 .compile_unit_list = ArrayList(CompileUnit).init(allocator),
119 };249 };
120 const st = &stack_trace;
121 st.self_exe_file = try os.openSelfExe();250 st.self_exe_file = try os.openSelfExe();
122 defer st.self_exe_file.close();251 %defer st.self_exe_file.close();
123252
124 try st.elf.openFile(allocator, &st.self_exe_file);253 try st.elf.openFile(allocator, &st.self_exe_file);
125 defer st.elf.close();254 %defer st.elf.close();
126255
127 st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;256 st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
128 st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;257 st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
...@@ -130,67 +259,19 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty...@@ -130,67 +259,19 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
130 st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;259 st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
131 st.debug_ranges = (try st.elf.findSection(".debug_ranges"));260 st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
132 try scanAllCompileUnits(st);261 try scanAllCompileUnits(st);
133262 return st;
134 var ignored_count: usize = 0;
135
136 var fp = @ptrToInt(@frameAddress());
137 while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) {
138 if (ignored_count < ignore_frame_count) {
139 ignored_count += 1;
140 continue;
141 }
142
143 const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize));
144
145 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
146 // at compile time. I'll call it issue #313
147 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
148
149 const compile_unit = findCompileUnit(st, return_address) catch {
150 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
151 return_address);
152 continue;
153 };
154 const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name);
155 if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| {
156 defer line_info.deinit();
157 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
158 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
159 line_info.file_name, line_info.line, line_info.column,
160 return_address, compile_unit_name);
161 if (printLineFromFile(st.allocator(), out_stream, line_info)) {
162 if (line_info.column == 0) {
163 try out_stream.write("\n");
164 } else {
165 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
166 try out_stream.writeByte(' ');
167 }}
168 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
169 }
170 } else |err| switch (err) {
171 error.EndOfFile, error.PathNotFound => {},
172 else => return err,
173 }
174 } else |err| switch (err) {
175 error.MissingDebugInfo, error.InvalidDebugInfo => {
176 try out_stream.print(ptr_hex ++ " in ??? ({})\n",
177 return_address, compile_unit_name);
178 },
179 else => return err,
180 }
181 }
182 },263 },
183 builtin.ObjectFormat.coff => {264 builtin.ObjectFormat.coff => {
184 try out_stream.write("(stack trace unavailable for COFF object format)\n");265 return error.TodoSupportCoffDebugInfo;
185 },266 },
186 builtin.ObjectFormat.macho => {267 builtin.ObjectFormat.macho => {
187 try out_stream.write("(stack trace unavailable for Mach-O object format)\n");268 return error.TodoSupportMachoDebugInfo;
188 },269 },
189 builtin.ObjectFormat.wasm => {270 builtin.ObjectFormat.wasm => {
190 try out_stream.write("(stack trace unavailable for WASM object format)\n");271 return error.TodoSupportCOFFDebugInfo;
191 },272 },
192 builtin.ObjectFormat.unknown => {273 builtin.ObjectFormat.unknown => {
193 try out_stream.write("(stack trace unavailable for unknown object format)\n");274 return error.UnknownObjectFormat;
194 },275 },
195 }276 }
196}277}
...@@ -228,7 +309,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_...@@ -228,7 +309,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
228 }309 }
229}310}
230311
231const ElfStackTrace = struct {312pub const ElfStackTrace = struct {
232 self_exe_file: io.File,313 self_exe_file: io.File,
233 elf: elf.Elf,314 elf: elf.Elf,
234 debug_info: &elf.SectionHeader,315 debug_info: &elf.SectionHeader,
...@@ -248,6 +329,11 @@ const ElfStackTrace = struct {...@@ -248,6 +329,11 @@ const ElfStackTrace = struct {
248 const in_stream = &in_file_stream.stream;329 const in_stream = &in_file_stream.stream;
249 return readStringRaw(self.allocator(), in_stream);330 return readStringRaw(self.allocator(), in_stream);
250 }331 }
332
333 pub fn close(self: &ElfStackTrace) {
334 self.self_exe_file.close();
335 self.elf.close();
336 }
251};337};
252338
253const PcRange = struct {339const PcRange = struct {
std/io.zig+1-1
...@@ -224,7 +224,7 @@ pub const File = struct {...@@ -224,7 +224,7 @@ pub const File = struct {
224 };224 };
225 }225 }
226 },226 },
227 else => @compileError("unsupported OS"),227 else => @compileError("unsupported OS: " ++ @tagName(builtin.os)),
228 }228 }
229 }229 }
230230
std/os/index.zig+2-9
...@@ -148,7 +148,7 @@ pub coldcc fn abort() -> noreturn {...@@ -148,7 +148,7 @@ pub coldcc fn abort() -> noreturn {
148}148}
149149
150/// Exits the program cleanly with the specified status code.150/// Exits the program cleanly with the specified status code.
151pub coldcc fn exit(status: i32) -> noreturn {151pub coldcc fn exit(status: u8) -> noreturn {
152 if (builtin.link_libc) {152 if (builtin.link_libc) {
153 c.exit(status);153 c.exit(status);
154 }154 }
...@@ -157,14 +157,7 @@ pub coldcc fn exit(status: i32) -> noreturn {...@@ -157,14 +157,7 @@ pub coldcc fn exit(status: i32) -> noreturn {
157 posix.exit(status);157 posix.exit(status);
158 },158 },
159 Os.windows => {159 Os.windows => {
160 // Map a possibly negative status code to a non-negative status for the systems default160 windows.ExitProcess(status);
161 // integer width.
162 const p_status = if (@sizeOf(c_uint) < @sizeOf(u32))
163 @truncate(c_uint, @bitCast(u32, status))
164 else
165 c_uint(@bitCast(u32, status));
166
167 windows.ExitProcess(p_status);
168 },161 },
169 else => @compileError("Unsupported OS"),162 else => @compileError("Unsupported OS"),
170 }163 }
std/special/bootstrap.zig+35-10
...@@ -21,8 +21,7 @@ comptime {...@@ -21,8 +21,7 @@ comptime {
21}21}
2222
23extern fn zenMain() -> noreturn {23extern fn zenMain() -> noreturn {
24 root.main() catch std.os.posix.exit(1);24 std.os.posix.exit(callMain());
25 std.os.posix.exit(0);
26}25}
2726
28nakedcc fn _start() -> noreturn {27nakedcc fn _start() -> noreturn {
...@@ -43,29 +42,55 @@ nakedcc fn _start() -> noreturn {...@@ -43,29 +42,55 @@ nakedcc fn _start() -> noreturn {
43extern fn WinMainCRTStartup() -> noreturn {42extern fn WinMainCRTStartup() -> noreturn {
44 @setAlignStack(16);43 @setAlignStack(16);
4544
46 root.main() catch std.os.windows.ExitProcess(1);45 std.os.windows.ExitProcess(callMain());
47 std.os.windows.ExitProcess(0);
48}46}
4947
50fn posixCallMainAndExit() -> noreturn {48fn posixCallMainAndExit() -> noreturn {
51 const argc = *argc_ptr;49 const argc = *argc_ptr;
52 const argv = @ptrCast(&&u8, &argc_ptr[1]);50 const argv = @ptrCast(&&u8, &argc_ptr[1]);
53 const envp = @ptrCast(&?&u8, &argv[argc + 1]);51 const envp = @ptrCast(&?&u8, &argv[argc + 1]);
54 callMain(argc, argv, envp) catch std.os.posix.exit(1);52 std.os.posix.exit(callMainWithArgs(argc, argv, envp));
55 std.os.posix.exit(0);
56}53}
5754
58fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {55fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) -> u8 {
59 std.os.ArgIteratorPosix.raw = argv[0..argc];56 std.os.ArgIteratorPosix.raw = argv[0..argc];
6057
61 var env_count: usize = 0;58 var env_count: usize = 0;
62 while (envp[env_count] != null) : (env_count += 1) {}59 while (envp[env_count] != null) : (env_count += 1) {}
63 std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];60 std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];
6461
65 return root.main();62 return callMain();
66}63}
6764
68extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {65extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {
69 callMain(usize(c_argc), c_argv, c_envp) catch return 1;66 return callMainWithArgs(usize(c_argc), c_argv, c_envp);
70 return 0;67}
68
69fn callMain() -> u8 {
70 switch (@typeId(@typeOf(root.main).ReturnType)) {
71 builtin.TypeId.NoReturn => {
72 root.main();
73 },
74 builtin.TypeId.Void => {
75 root.main();
76 return 0;
77 },
78 builtin.TypeId.Int => {
79 if (@typeOf(root.main).ReturnType.bit_count != 8) {
80 @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
81 }
82 return root.main();
83 },
84 builtin.TypeId.ErrorUnion => {
85 root.main() catch |err| {
86 std.debug.warn("error: {}\n", @errorName(err));
87 if (@errorReturnTrace()) |trace| {
88 std.debug.dumpStackTrace(trace);
89 }
90 return 1;
91 };
92 return 0;
93 },
94 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"),
95 }
71}96}
std/special/build_runner.zig+4-4
...@@ -14,7 +14,7 @@ pub fn main() -> %void {...@@ -14,7 +14,7 @@ pub fn main() -> %void {
14 var arg_it = os.args();14 var arg_it = os.args();
1515
16 // TODO use a more general purpose allocator here16 // TODO use a more general purpose allocator here
17 var inc_allocator = std.heap.IncrementingAllocator.init(40 * 1024 * 1024) catch unreachable;17 var inc_allocator = try std.heap.IncrementingAllocator.init(40 * 1024 * 1024);
18 defer inc_allocator.deinit();18 defer inc_allocator.deinit();
1919
20 const allocator = &inc_allocator.allocator;20 const allocator = &inc_allocator.allocator;
...@@ -107,12 +107,12 @@ pub fn main() -> %void {...@@ -107,12 +107,12 @@ pub fn main() -> %void {
107 return usageAndErr(&builder, false, try stderr_stream);107 return usageAndErr(&builder, false, try stderr_stream);
108 }108 }
109 } else {109 } else {
110 targets.append(arg) catch unreachable;110 try targets.append(arg);
111 }111 }
112 }112 }
113113
114 builder.setInstallPrefix(prefix);114 builder.setInstallPrefix(prefix);
115 root.build(&builder);115 try root.build(&builder);
116116
117 if (builder.validateUserInputDidItFail())117 if (builder.validateUserInputDidItFail())
118 return usageAndErr(&builder, true, try stderr_stream);118 return usageAndErr(&builder, true, try stderr_stream);
...@@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)...@@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
129 // run the build script to collect the options129 // run the build script to collect the options
130 if (!already_ran_build) {130 if (!already_ran_build) {
131 builder.setInstallPrefix(null);131 builder.setInstallPrefix(null);
132 root.build(builder);132 try root.build(builder);
133 }133 }
134134
135 // This usage text has to be synchronized with src/main.cpp135 // This usage text has to be synchronized with src/main.cpp
std/special/builtin.zig+1-1
...@@ -5,7 +5,7 @@ const builtin = @import("builtin");...@@ -5,7 +5,7 @@ const builtin = @import("builtin");
55
6// Avoid dragging in the debug safety mechanisms into this .o file,6// Avoid dragging in the debug safety mechanisms into this .o file,
7// unless we're trying to test this file.7// unless we're trying to test this file.
8pub coldcc fn panic(msg: []const u8) -> noreturn {8pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
9 if (builtin.is_test) {9 if (builtin.is_test) {
10 @import("std").debug.panic("{}", msg);10 @import("std").debug.panic("{}", msg);
11 } else {11 } else {
std/special/compiler_rt/index.zig+1-1
...@@ -74,7 +74,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;...@@ -74,7 +74,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
7474
75// Avoid dragging in the debug safety mechanisms into this .o file,75// Avoid dragging in the debug safety mechanisms into this .o file,
76// unless we're trying to test this file.76// unless we're trying to test this file.
77pub coldcc fn panic(msg: []const u8) -> noreturn {77pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
78 if (is_test) {78 if (is_test) {
79 @import("std").debug.panic("{}", msg);79 @import("std").debug.panic("{}", msg);
80 } else {80 } else {
std/special/panic.zig+5-1
...@@ -4,14 +4,18 @@...@@ -4,14 +4,18 @@
4// have to be added in the compiler.4// have to be added in the compiler.
55
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const std = @import("std");
78
8pub coldcc fn panic(msg: []const u8) -> noreturn {9pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
9 switch (builtin.os) {10 switch (builtin.os) {
10 // TODO: fix panic in zen.11 // TODO: fix panic in zen.
11 builtin.Os.freestanding, builtin.Os.zen => {12 builtin.Os.freestanding, builtin.Os.zen => {
12 while (true) {}13 while (true) {}
13 },14 },
14 else => {15 else => {
16 if (error_return_trace) |trace| {
17 @import("std").debug.panicWithTrace(trace, "{}", msg);
18 }
15 @import("std").debug.panic("{}", msg);19 @import("std").debug.panic("{}", msg);
16 },20 },
17 }21 }
std/special/test_runner.zig+1-4
...@@ -8,10 +8,7 @@ pub fn main() -> %void {...@@ -8,10 +8,7 @@ pub fn main() -> %void {
8 for (test_fn_list) |test_fn, i| {8 for (test_fn_list) |test_fn, i| {
9 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);9 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
1010
11 test_fn.func() catch |err| {11 try test_fn.func();
12 warn("{}\n", err);
13 return err;
14 };
1512
16 warn("OK\n");13 warn("OK\n");
17 }14 }
test/compile_errors.zig+9-9
...@@ -1,6 +1,15 @@...@@ -1,6 +1,15 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: &tests.CompileErrorContext) {3pub fn addCases(cases: &tests.CompileErrorContext) {
4 cases.add("wrong return type for main",
5 \\pub fn main() -> f32 { }
6 , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
7
8 cases.add("double ?? on main return value",
9 \\pub fn main() -> ??void {
10 \\}
11 , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
12
4 cases.add("bad identifier in function with struct defined inside function which references local const",13 cases.add("bad identifier in function with struct defined inside function which references local const",
5 \\export fn entry() {14 \\export fn entry() {
6 \\ const BlockKind = u32;15 \\ const BlockKind = u32;
...@@ -1059,15 +1068,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {...@@ -1059,15 +1068,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
1059 ,1068 ,
1060 ".tmp_source.zig:2:5: error: expected type 'void', found 'error'");1069 ".tmp_source.zig:2:5: error: expected type 'void', found 'error'");
10611070
1062 cases.add("wrong return type for main",
1063 \\pub fn main() { }
1064 , ".tmp_source.zig:1:15: error: expected return type of main to be '%void', instead is 'void'");
1065
1066 cases.add("double ?? on main return value",
1067 \\pub fn main() -> ??void {
1068 \\}
1069 , ".tmp_source.zig:1:18: error: expected return type of main to be '%void', instead is '??void'");
1070
1071 cases.add("invalid pointer for var type",1071 cases.add("invalid pointer for var type",
1072 \\extern fn ext() -> usize;1072 \\extern fn ext() -> usize;
1073 \\var bytes: [ext()]u8 = undefined;1073 \\var bytes: [ext()]u8 = undefined;
test/debug_safety.zig+20-20
...@@ -2,7 +2,7 @@ const tests = @import("tests.zig");...@@ -2,7 +2,7 @@ const tests = @import("tests.zig");
22
3pub fn addCases(cases: &tests.CompareOutputContext) {3pub fn addCases(cases: &tests.CompareOutputContext) {
4 cases.addDebugSafety("calling panic",4 cases.addDebugSafety("calling panic",
5 \\pub fn panic(message: []const u8) -> noreturn {5 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
6 \\ @import("std").os.exit(126);6 \\ @import("std").os.exit(126);
7 \\}7 \\}
8 \\pub fn main() -> %void {8 \\pub fn main() -> %void {
...@@ -11,7 +11,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -11,7 +11,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
11 );11 );
1212
13 cases.addDebugSafety("out of bounds slice access",13 cases.addDebugSafety("out of bounds slice access",
14 \\pub fn panic(message: []const u8) -> noreturn {14 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
15 \\ @import("std").os.exit(126);15 \\ @import("std").os.exit(126);
16 \\}16 \\}
17 \\pub fn main() -> %void {17 \\pub fn main() -> %void {
...@@ -25,7 +25,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -25,7 +25,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
25 );25 );
2626
27 cases.addDebugSafety("integer addition overflow",27 cases.addDebugSafety("integer addition overflow",
28 \\pub fn panic(message: []const u8) -> noreturn {28 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
29 \\ @import("std").os.exit(126);29 \\ @import("std").os.exit(126);
30 \\}30 \\}
31 \\error Whatever;31 \\error Whatever;
...@@ -39,7 +39,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -39,7 +39,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
39 );39 );
4040
41 cases.addDebugSafety("integer subtraction overflow",41 cases.addDebugSafety("integer subtraction overflow",
42 \\pub fn panic(message: []const u8) -> noreturn {42 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
43 \\ @import("std").os.exit(126);43 \\ @import("std").os.exit(126);
44 \\}44 \\}
45 \\error Whatever;45 \\error Whatever;
...@@ -53,7 +53,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -53,7 +53,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
53 );53 );
5454
55 cases.addDebugSafety("integer multiplication overflow",55 cases.addDebugSafety("integer multiplication overflow",
56 \\pub fn panic(message: []const u8) -> noreturn {56 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
57 \\ @import("std").os.exit(126);57 \\ @import("std").os.exit(126);
58 \\}58 \\}
59 \\error Whatever;59 \\error Whatever;
...@@ -67,7 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -67,7 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
67 );67 );
6868
69 cases.addDebugSafety("integer negation overflow",69 cases.addDebugSafety("integer negation overflow",
70 \\pub fn panic(message: []const u8) -> noreturn {70 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
71 \\ @import("std").os.exit(126);71 \\ @import("std").os.exit(126);
72 \\}72 \\}
73 \\error Whatever;73 \\error Whatever;
...@@ -81,7 +81,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -81,7 +81,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
81 );81 );
8282
83 cases.addDebugSafety("signed integer division overflow",83 cases.addDebugSafety("signed integer division overflow",
84 \\pub fn panic(message: []const u8) -> noreturn {84 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
85 \\ @import("std").os.exit(126);85 \\ @import("std").os.exit(126);
86 \\}86 \\}
87 \\error Whatever;87 \\error Whatever;
...@@ -95,7 +95,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -95,7 +95,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
95 );95 );
9696
97 cases.addDebugSafety("signed shift left overflow",97 cases.addDebugSafety("signed shift left overflow",
98 \\pub fn panic(message: []const u8) -> noreturn {98 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
99 \\ @import("std").os.exit(126);99 \\ @import("std").os.exit(126);
100 \\}100 \\}
101 \\error Whatever;101 \\error Whatever;
...@@ -109,7 +109,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -109,7 +109,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
109 );109 );
110110
111 cases.addDebugSafety("unsigned shift left overflow",111 cases.addDebugSafety("unsigned shift left overflow",
112 \\pub fn panic(message: []const u8) -> noreturn {112 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
113 \\ @import("std").os.exit(126);113 \\ @import("std").os.exit(126);
114 \\}114 \\}
115 \\error Whatever;115 \\error Whatever;
...@@ -123,7 +123,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -123,7 +123,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
123 );123 );
124124
125 cases.addDebugSafety("signed shift right overflow",125 cases.addDebugSafety("signed shift right overflow",
126 \\pub fn panic(message: []const u8) -> noreturn {126 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
127 \\ @import("std").os.exit(126);127 \\ @import("std").os.exit(126);
128 \\}128 \\}
129 \\error Whatever;129 \\error Whatever;
...@@ -137,7 +137,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -137,7 +137,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
137 );137 );
138138
139 cases.addDebugSafety("unsigned shift right overflow",139 cases.addDebugSafety("unsigned shift right overflow",
140 \\pub fn panic(message: []const u8) -> noreturn {140 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
141 \\ @import("std").os.exit(126);141 \\ @import("std").os.exit(126);
142 \\}142 \\}
143 \\error Whatever;143 \\error Whatever;
...@@ -151,7 +151,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -151,7 +151,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
151 );151 );
152152
153 cases.addDebugSafety("integer division by zero",153 cases.addDebugSafety("integer division by zero",
154 \\pub fn panic(message: []const u8) -> noreturn {154 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
155 \\ @import("std").os.exit(126);155 \\ @import("std").os.exit(126);
156 \\}156 \\}
157 \\error Whatever;157 \\error Whatever;
...@@ -164,7 +164,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -164,7 +164,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
164 );164 );
165165
166 cases.addDebugSafety("exact division failure",166 cases.addDebugSafety("exact division failure",
167 \\pub fn panic(message: []const u8) -> noreturn {167 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
168 \\ @import("std").os.exit(126);168 \\ @import("std").os.exit(126);
169 \\}169 \\}
170 \\error Whatever;170 \\error Whatever;
...@@ -178,7 +178,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -178,7 +178,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
178 );178 );
179179
180 cases.addDebugSafety("cast []u8 to bigger slice of wrong size",180 cases.addDebugSafety("cast []u8 to bigger slice of wrong size",
181 \\pub fn panic(message: []const u8) -> noreturn {181 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
182 \\ @import("std").os.exit(126);182 \\ @import("std").os.exit(126);
183 \\}183 \\}
184 \\error Whatever;184 \\error Whatever;
...@@ -192,7 +192,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -192,7 +192,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
192 );192 );
193193
194 cases.addDebugSafety("value does not fit in shortening cast",194 cases.addDebugSafety("value does not fit in shortening cast",
195 \\pub fn panic(message: []const u8) -> noreturn {195 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
196 \\ @import("std").os.exit(126);196 \\ @import("std").os.exit(126);
197 \\}197 \\}
198 \\error Whatever;198 \\error Whatever;
...@@ -206,7 +206,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -206,7 +206,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
206 );206 );
207207
208 cases.addDebugSafety("signed integer not fitting in cast to unsigned integer",208 cases.addDebugSafety("signed integer not fitting in cast to unsigned integer",
209 \\pub fn panic(message: []const u8) -> noreturn {209 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
210 \\ @import("std").os.exit(126);210 \\ @import("std").os.exit(126);
211 \\}211 \\}
212 \\error Whatever;212 \\error Whatever;
...@@ -220,7 +220,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -220,7 +220,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
220 );220 );
221221
222 cases.addDebugSafety("unwrap error",222 cases.addDebugSafety("unwrap error",
223 \\pub fn panic(message: []const u8) -> noreturn {223 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
224 \\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) {224 \\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) {
225 \\ @import("std").os.exit(126); // good225 \\ @import("std").os.exit(126); // good
226 \\ }226 \\ }
...@@ -236,7 +236,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -236,7 +236,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
236 );236 );
237237
238 cases.addDebugSafety("cast integer to error and no code matches",238 cases.addDebugSafety("cast integer to error and no code matches",
239 \\pub fn panic(message: []const u8) -> noreturn {239 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
240 \\ @import("std").os.exit(126);240 \\ @import("std").os.exit(126);
241 \\}241 \\}
242 \\pub fn main() -> %void {242 \\pub fn main() -> %void {
...@@ -248,7 +248,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -248,7 +248,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
248 );248 );
249249
250 cases.addDebugSafety("@alignCast misaligned",250 cases.addDebugSafety("@alignCast misaligned",
251 \\pub fn panic(message: []const u8) -> noreturn {251 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
252 \\ @import("std").os.exit(126);252 \\ @import("std").os.exit(126);
253 \\}253 \\}
254 \\error Wrong;254 \\error Wrong;
...@@ -265,7 +265,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -265,7 +265,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
265 );265 );
266266
267 cases.addDebugSafety("bad union field access",267 cases.addDebugSafety("bad union field access",
268 \\pub fn panic(message: []const u8) -> noreturn {268 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
269 \\ @import("std").os.exit(126);269 \\ @import("std").os.exit(126);
270 \\}270 \\}
271 \\271 \\
test/standalone/issue_339/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) {3pub fn build(b: &Builder) -> %void {
4 const obj = b.addObject("test", "test.zig");4 const obj = b.addObject("test", "test.zig");
55
6 const test_step = b.step("test", "Test the program");6 const test_step = b.step("test", "Test the program");
test/standalone/issue_339/test.zig+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1pub fn panic(msg: []const u8) -> noreturn { @breakpoint(); while (true) {} }1const StackTrace = @import("builtin").StackTrace;
2pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) -> noreturn { @breakpoint(); while (true) {} }
23
3fn bar() -> %void {}4fn bar() -> %void {}
45
test/standalone/pkg_import/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) {3pub fn build(b: &Builder) -> %void {
4 const exe = b.addExecutable("test", "test.zig");4 const exe = b.addExecutable("test", "test.zig");
5 exe.addPackagePath("my_pkg", "pkg.zig");5 exe.addPackagePath("my_pkg", "pkg.zig");
66
test/standalone/use_alias/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) {3pub fn build(b: &Builder) -> %void {
4 b.addCIncludePath(".");4 b.addCIncludePath(".");
55
6 const main = b.addTest("main.zig");6 const main = b.addTest("main.zig");
test/tests.zig+2-2
...@@ -45,6 +45,7 @@ const test_targets = []TestTarget {...@@ -45,6 +45,7 @@ const test_targets = []TestTarget {
45};45};
4646
47error TestFailed;47error TestFailed;
48error CompilationIncorrectlySucceeded;
4849
49const max_stdout_size = 1 * 1024 * 1024; // 1 MB50const max_stdout_size = 1 * 1024 * 1024; // 1 MB
5051
...@@ -602,8 +603,7 @@ pub const CompileErrorContext = struct {...@@ -602,8 +603,7 @@ pub const CompileErrorContext = struct {
602 switch (term) {603 switch (term) {
603 Term.Exited => |code| {604 Term.Exited => |code| {
604 if (code == 0) {605 if (code == 0) {
605 warn("Compilation incorrectly succeeded\n");606 return error.CompilationIncorrectlySucceeded;
606 return error.TestFailed;
607 }607 }
608 },608 },
609 else => {609 else => {