| author | |
| committer | |
| log | 5a4968484b1ff761766180204151e77a701cff68 |
| tree | c98f5386609d3fed67da3a27be0ac9b1ea3402bb |
| parent | 793f031c4ca7fdd230ef262895acf3e454be49dd |
| parent | 6ec9933fd80013104982debab9fbff1463582f19 |
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; |
| 10 | const Buffer = std.Buffer; | 10 | const Buffer = std.Buffer; |
| 11 | const io = std.io; | 11 | const io = std.io; |
| 12 | 12 | ||
| 13 | pub fn build(b: &Builder) { | 13 | pub fn build(b: &Builder) -> %void { |
| 14 | const mode = b.standardReleaseOptions(); | 14 | const mode = b.standardReleaseOptions(); |
| 15 | 15 | ||
| 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"); |
| 37 | 37 | ||
| 38 | // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library | 38 | // 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); |
| 69 | 69 | ||
| 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); |
| 74 | 74 | ||
| ... | @@ -155,9 +155,9 @@ const LibraryDep = struct { | ... | @@ -155,9 +155,9 @@ const LibraryDep = struct { |
| 155 | }; | 155 | }; |
| 156 | 156 | ||
| 157 | fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep { | 157 | fn 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"}); |
| 161 | 161 | ||
| 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 no | 4413 | 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 @@ |
| 1 | const Builder = @import("std").build.Builder; | 1 | const Builder = @import("std").build.Builder; |
| 2 | 2 | ||
| 3 | pub fn build(b: &Builder) { | 3 | pub fn build(b: &Builder) -> %void { |
| 4 | const obj = b.addObject("base64", "base64.zig"); | 4 | const obj = b.addObject("base64", "base64.zig"); |
| 5 | 5 | ||
| 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 @@ |
| 1 | const Builder = @import("std").build.Builder; | 1 | const Builder = @import("std").build.Builder; |
| 2 | 2 | ||
| 3 | pub fn build(b: &Builder) { | 3 | pub 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)); |
| 5 | 5 | ||
| 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; |
| 1206 | 1206 | ||
| 1207 | ZigList<FnExport> export_list; | 1207 | ZigList<FnExport> export_list; |
| 1208 | bool calls_errorable_function; | ||
| 1208 | }; | 1209 | }; |
| 1209 | 1210 | ||
| 1210 | uint32_t fn_table_entry_hash(FnTableEntry*); | 1211 | uint32_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 | }; |
| 1277 | 1279 | ||
| 1278 | struct BuiltinFnEntry { | 1280 | struct 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; |
| 1574 | 1578 | ||
| 1579 | LLVMValueRef return_err_fn; | ||
| 1580 | |||
| 1575 | IrInstruction *invalid_instruction; | 1581 | IrInstruction *invalid_instruction; |
| 1576 | ConstExprValue const_void_val; | 1582 | ConstExprValue const_void_val; |
| 1577 | 1583 | ||
| ... | @@ -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; |
| 1596 | 1602 | ||
| 1597 | TypeTableEntry *align_amt_type; | 1603 | TypeTableEntry *align_amt_type; |
| 1604 | TypeTableEntry *stack_trace_type; | ||
| 1605 | TypeTableEntry *ptr_to_stack_trace_type; | ||
| 1598 | }; | 1606 | }; |
| 1599 | 1607 | ||
| 1600 | enum VarLinkage { | 1608 | enum 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 | }; |
| 1900 | 1909 | ||
| 1901 | struct IrInstruction { | 1910 | struct IrInstruction { |
| ... | @@ -2717,6 +2726,10 @@ struct IrInstructionExport { | ... | @@ -2717,6 +2726,10 @@ struct IrInstructionExport { |
| 2717 | IrInstruction *target; | 2726 | IrInstruction *target; |
| 2718 | }; | 2727 | }; |
| 2719 | 2728 | ||
| 2729 | struct IrInstructionErrorReturnTrace { | ||
| 2730 | IrInstruction base; | ||
| 2731 | }; | ||
| 2732 | |||
| 2720 | static const size_t slice_ptr_index = 0; | 2733 | static const size_t slice_ptr_index = 0; |
| 2721 | static const size_t slice_len_index = 1; | 2734 | static const size_t slice_len_index = 1; |
| 2722 | 2735 |
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 | } |
| 871 | 871 | ||
| 872 | TypeTableEntry *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 | |||
| 872 | TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { | 882 | TypeTableEntry *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 value | 931 | // +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 val | 933 | 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; |
| 938 | 954 | ||
| 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 | } |
| 1169 | 1193 | ||
| 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) { |
| 2558 | 2585 | ||
| 2559 | static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) { | 2586 | static 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 | } |
| 2564 | 2591 | ||
| ... | @@ -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 | } |
| 2578 | 2605 | ||
| 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 | } |
| 5529 | 5554 | ||
| 5555 | ConstExprValue *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); |
| 185 | Buf *const_value_to_buffer(ConstExprValue *const_val); | 185 | Buf *const_value_to_buffer(ConstExprValue *const_val); |
| 186 | void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc); | 186 | void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc); |
| 187 | 187 | ||
| 188 | |||
| 189 | ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name); | ||
| 190 | TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g); | ||
| 191 | |||
| 192 | |||
| 188 | #endif | 193 | #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 | } |
| 406 | 406 | ||
| 407 | static 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 | |||
| 407 | static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { | 420 | static 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 | } |
| 485 | 498 | ||
| 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 | } |
| 489 | 503 | ||
| ... | @@ -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 | } |
| 522 | 536 | ||
| 523 | if (!type_has_bits(fn_type->data.fn.fn_type_id.return_type)) { | 537 | if (!type_has_bits(return_type)) { |
| 524 | // nothing to do | 538 | // 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 | } |
| 565 | 577 | ||
| 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 | } |
| 568 | 585 | ||
| ... | @@ -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 | } |
| 866 | 883 | ||
| 867 | static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) { | 884 | static 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 | } |
| 874 | 900 | ||
| 875 | static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) { | 901 | static 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 | } |
| 878 | 904 | ||
| 879 | static LLVMValueRef get_memcpy_fn_val(CodeGen *g) { | 905 | static 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 | } |
| 897 | 923 | ||
| 924 | static 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 | |||
| 898 | static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { | 1005 | static 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); |
| 954 | 1061 | ||
| 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); |
| 977 | 1088 | ||
| 978 | LLVMValueRef err_val = LLVMGetParam(fn_val, 0); | 1089 | LLVMValueRef err_val = LLVMGetParam(fn_val, 1); |
| 979 | 1090 | ||
| 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); |
| 1007 | 1118 | ||
| 1008 | gen_panic(g, global_slice); | 1119 | gen_panic(g, global_slice, LLVMGetParam(fn_val, 0)); |
| 1009 | 1120 | ||
| 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) { |
| 1016 | 1127 | ||
| 1017 | static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) { | 1128 | static 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 | } |
| 1023 | 1144 | ||
| ... | @@ -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) { |
| 1296 | static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { | 1417 | static 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 | } |
| 2883 | 3038 | ||
| 3039 | static 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 | |||
| 2884 | static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { | 3049 | static 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 | } |
| 3475 | 3640 | ||
| 3476 | static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) { | 3641 | static 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 | } |
| 3480 | 3645 | ||
| ... | @@ -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); |
| 4504 | 4672 | ||
| 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 data | 4709 | // 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 | } |
| 5068 | 5273 | ||
| 5069 | static const char *bool_to_str(bool b) { | 5274 | static 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(); |
| 5090 | 5295 | ||
| 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)); | ||
| 5236 | 5448 | ||
| 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"); |
| 5238 | 5450 | ||
| ... | @@ -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 | } |
| 5255 | 5468 | ||
| 5256 | static void init(CodeGen *g) { | 5469 | static 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 | } |
| 5361 | 5574 | ||
| 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 | } |
| 574 | 574 | ||
| 575 | static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) { | ||
| 576 | return IrInstructionIdErrorReturnTrace; | ||
| 577 | } | ||
| 578 | |||
| 575 | template<typename T> | 579 | template<typename T> |
| 576 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { | 580 | static 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 | } |
| 2307 | 2311 | ||
| 2312 | static 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 | |||
| 2308 | static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { | 2318 | static 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 |
| 3731 | 3741 | ||
| 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 | } |
| 8232 | 8246 | ||
| 8233 | static 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 | |||
| 8243 | static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) { | 8247 | static 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 | } |
| 9580 | 9584 | ||
| 9585 | static 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 | |||
| 9581 | static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, | 9603 | static 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); |
| 10055 | 10077 | ||
| 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 | } |
| 10058 | 10084 | ||
| 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 | } |
| 998 | 998 | ||
| 999 | static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { | ||
| 1000 | fprintf(irp->f, "@errorReturnTrace()"); | ||
| 1001 | } | ||
| 1002 | |||
| 999 | 1003 | ||
| 1000 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | 1004 | static 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(); |
| 248 | 248 | ||
| 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 | } |
| 257 | 257 | ||
| ... | @@ -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 | } |
| 723 | 723 | ||
| 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; |
| 13 | error MissingDebugInfo; | 13 | error MissingDebugInfo; |
| 14 | error InvalidDebugInfo; | 14 | error InvalidDebugInfo; |
| 15 | error UnsupportedDebugInfo; | 15 | error UnsupportedDebugInfo; |
| 16 | error UnknownObjectFormat; | ||
| 17 | error TodoSupportCoffDebugInfo; | ||
| 18 | error TodoSupportMachoDebugInfo; | ||
| 19 | error TodoSupportCOFFDebugInfo; | ||
| 16 | 20 | ||
| 17 | 21 | ||
| 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 | } |
| 39 | 43 | ||
| 44 | var self_debug_info: ?&ElfStackTrace = null; | ||
| 45 | pub 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. | ||
| 56 | pub 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. |
| 41 | pub fn dumpStackTrace() { | 70 | pub 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 | } |
| 45 | 82 | ||
| 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 { |
| 88 | 125 | ||
| 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 | |||
| 133 | pub 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(); | ||
| 92 | 143 | ||
| 93 | os.abort(); | 144 | os.abort(); |
| 94 | } | 145 | } |
| ... | @@ -101,12 +152,91 @@ const RESET = "\x1b[0m"; | ... | @@ -101,12 +152,91 @@ const RESET = "\x1b[0m"; |
| 101 | error PathNotFound; | 152 | error PathNotFound; |
| 102 | error InvalidDebugInfo; | 153 | error InvalidDebugInfo; |
| 103 | 154 | ||
| 104 | pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, | 155 | pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, |
| 105 | ignore_frame_count: usize) -> %void | 156 | 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 | |||
| 177 | pub 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 | |||
| 194 | fn 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 | |||
| 235 | pub 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(); |
| 123 | 252 | ||
| 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(); |
| 126 | 255 | ||
| 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); |
| 133 | 262 | 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 | } |
| 230 | 311 | ||
| 231 | const ElfStackTrace = struct { | 312 | pub 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 | }; |
| 252 | 338 | ||
| 253 | const PcRange = struct { | 339 | const 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 | } |
| 230 | 230 |
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 | } |
| 149 | 149 | ||
| 150 | /// Exits the program cleanly with the specified status code. | 150 | /// Exits the program cleanly with the specified status code. |
| 151 | pub coldcc fn exit(status: i32) -> noreturn { | 151 | pub 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 default | 160 | 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 | } |
| 22 | 22 | ||
| 23 | extern fn zenMain() -> noreturn { | 23 | extern 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 | } |
| 27 | 26 | ||
| 28 | nakedcc fn _start() -> noreturn { | 27 | nakedcc fn _start() -> noreturn { |
| ... | @@ -43,29 +42,55 @@ nakedcc fn _start() -> noreturn { | ... | @@ -43,29 +42,55 @@ nakedcc fn _start() -> noreturn { |
| 43 | extern fn WinMainCRTStartup() -> noreturn { | 42 | extern fn WinMainCRTStartup() -> noreturn { |
| 44 | @setAlignStack(16); | 43 | @setAlignStack(16); |
| 45 | 44 | ||
| 46 | root.main() catch std.os.windows.ExitProcess(1); | 45 | std.os.windows.ExitProcess(callMain()); |
| 47 | std.os.windows.ExitProcess(0); | ||
| 48 | } | 46 | } |
| 49 | 47 | ||
| 50 | fn posixCallMainAndExit() -> noreturn { | 48 | fn 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 | } |
| 57 | 54 | ||
| 58 | fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void { | 55 | fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) -> u8 { |
| 59 | std.os.ArgIteratorPosix.raw = argv[0..argc]; | 56 | std.os.ArgIteratorPosix.raw = argv[0..argc]; |
| 60 | 57 | ||
| 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]; |
| 64 | 61 | ||
| 65 | return root.main(); | 62 | return callMain(); |
| 66 | } | 63 | } |
| 67 | 64 | ||
| 68 | extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 { | 65 | extern 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 | |||
| 69 | fn 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(); |
| 15 | 15 | ||
| 16 | // TODO use a more general purpose allocator here | 16 | // 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(); |
| 19 | 19 | ||
| 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 | } |
| 113 | 113 | ||
| 114 | builder.setInstallPrefix(prefix); | 114 | builder.setInstallPrefix(prefix); |
| 115 | root.build(&builder); | 115 | try root.build(&builder); |
| 116 | 116 | ||
| 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 options | 129 | // 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 | } |
| 134 | 134 | ||
| 135 | // This usage text has to be synchronized with src/main.cpp | 135 | // 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"); |
| 5 | 5 | ||
| 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. |
| 8 | pub coldcc fn panic(msg: []const u8) -> noreturn { | 8 | pub 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; |
| 74 | 74 | ||
| 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. |
| 77 | pub coldcc fn panic(msg: []const u8) -> noreturn { | 77 | pub 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. |
| 5 | 5 | ||
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | const std = @import("std"); | ||
| 7 | 8 | ||
| 8 | pub coldcc fn panic(msg: []const u8) -> noreturn { | 9 | pub 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); |
| 10 | 10 | ||
| 11 | test_fn.func() catch |err| { | 11 | try test_fn.func(); |
| 12 | warn("{}\n", err); | ||
| 13 | return err; | ||
| 14 | }; | ||
| 15 | 12 | ||
| 16 | warn("OK\n"); | 13 | warn("OK\n"); |
| 17 | } | 14 | } |
test/compile_errors.zig+9-9| ... | @@ -1,6 +1,15 @@ | ... | @@ -1,6 +1,15 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: &tests.CompileErrorContext) { | 3 | pub 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'"); |
| 1061 | 1070 | ||
| 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"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: &tests.CompareOutputContext) { | 3 | pub 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 | ); |
| 12 | 12 | ||
| 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 | ); |
| 26 | 26 | ||
| 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 | ); |
| 40 | 40 | ||
| 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 | ); |
| 54 | 54 | ||
| 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 | ); |
| 68 | 68 | ||
| 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 | ); |
| 82 | 82 | ||
| 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 | ); |
| 96 | 96 | ||
| 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 | ); |
| 110 | 110 | ||
| 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 | ); |
| 124 | 124 | ||
| 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 | ); |
| 138 | 138 | ||
| 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 | ); |
| 152 | 152 | ||
| 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 | ); |
| 165 | 165 | ||
| 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 | ); |
| 179 | 179 | ||
| 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 | ); |
| 193 | 193 | ||
| 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 | ); |
| 207 | 207 | ||
| 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 | ); |
| 221 | 221 | ||
| 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); // good | 225 | \\ @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 | ); |
| 237 | 237 | ||
| 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 | ); |
| 249 | 249 | ||
| 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 | ); |
| 266 | 266 | ||
| 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 @@ |
| 1 | const Builder = @import("std").build.Builder; | 1 | const Builder = @import("std").build.Builder; |
| 2 | 2 | ||
| 3 | pub fn build(b: &Builder) { | 3 | pub fn build(b: &Builder) -> %void { |
| 4 | const obj = b.addObject("test", "test.zig"); | 4 | const obj = b.addObject("test", "test.zig"); |
| 5 | 5 | ||
| 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 @@ |
| 1 | pub fn panic(msg: []const u8) -> noreturn { @breakpoint(); while (true) {} } | 1 | const StackTrace = @import("builtin").StackTrace; |
| 2 | pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) -> noreturn { @breakpoint(); while (true) {} } | ||
| 2 | 3 | ||
| 3 | fn bar() -> %void {} | 4 | fn bar() -> %void {} |
| 4 | 5 |
test/standalone/pkg_import/build.zig+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const Builder = @import("std").build.Builder; | 1 | const Builder = @import("std").build.Builder; |
| 2 | 2 | ||
| 3 | pub fn build(b: &Builder) { | 3 | pub 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"); |
| 6 | 6 |
test/standalone/use_alias/build.zig+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const Builder = @import("std").build.Builder; | 1 | const Builder = @import("std").build.Builder; |
| 2 | 2 | ||
| 3 | pub fn build(b: &Builder) { | 3 | pub fn build(b: &Builder) -> %void { |
| 4 | b.addCIncludePath("."); | 4 | b.addCIncludePath("."); |
| 5 | 5 | ||
| 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 | }; |
| 46 | 46 | ||
| 47 | error TestFailed; | 47 | error TestFailed; |
| 48 | error CompilationIncorrectlySucceeded; | ||
| 48 | 49 | ||
| 49 | const max_stdout_size = 1 * 1024 * 1024; // 1 MB | 50 | const max_stdout_size = 1 * 1024 * 1024; // 1 MB |
| 50 | 51 | ||
| ... | @@ -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 => { |