diff --git a/build.zig b/build.zig index 7c1563c70679..1fa984dcff58 100644 --- a/build.zig +++ b/build.zig @@ -10,7 +10,7 @@ const ArrayList = std.ArrayList; const Buffer = std.Buffer; const io = std.io; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { const mode = b.standardReleaseOptions(); var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); @@ -36,7 +36,7 @@ pub fn build(b: &Builder) { const test_step = b.step("test", "Run all the tests"); // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library - const build_info = b.exec([][]const u8{b.zig_exe, "BUILD_INFO"}); + const build_info = try b.exec([][]const u8{b.zig_exe, "BUILD_INFO"}); var index: usize = 0; const cmake_binary_dir = nextValue(&index, build_info); const cxx_compiler = nextValue(&index, build_info); @@ -68,7 +68,7 @@ pub fn build(b: &Builder) { dependOnLib(exe, llvm); if (exe.target.getOs() == builtin.Os.linux) { - const libstdcxx_path_padded = b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"}); + const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"}); const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next(); exe.addObjectFile(libstdcxx_path); @@ -155,9 +155,9 @@ const LibraryDep = struct { }; fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep { - const libs_output = b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); - const includes_output = b.exec([][]const u8{llvm_config_exe, "--includedir"}); - const libdir_output = b.exec([][]const u8{llvm_config_exe, "--libdir"}); + const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); + const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"}); + const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"}); var result = LibraryDep { .libs = ArrayList([]const u8).init(b.allocator), diff --git a/doc/langref.html.in b/doc/langref.html.in index 247679010583..94bc78095942 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -142,6 +142,7 @@
  • @TagType
  • @EnumTagType
  • @errorName
  • +
  • @errorReturnTrace
  • @fence
  • @fieldParentPtr
  • @frameAddress
  • @@ -4412,6 +4413,13 @@ test.zig:6:2: error: found compile log statement or all calls have a compile-time known value for err, then no error name table will be generated.

    +

    @errorReturnTrace

    +
    @errorReturnTrace() -> ?&builtin.StackTrace
    +

    + If the binary is built with error return tracing, and this function is invoked in a + function that calls a function with an error or error union return type, returns a + stack trace object. Otherwise returns `null`. +

    @fence

    @fence(order: AtomicOrder)

    diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig index 391af9924a63..4bba69b0919a 100644 --- a/example/mix_o_files/build.zig +++ b/example/mix_o_files/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { const obj = b.addObject("base64", "base64.zig"); const exe = b.addCExecutable("test"); diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig index 9b7f3793c6f6..147b54401cc0 100644 --- a/example/shared_library/build.zig +++ b/example/shared_library/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); const exe = b.addCExecutable("test"); diff --git a/src/all_types.hpp b/src/all_types.hpp index 024c78eb73b8..b4010976470c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1205,6 +1205,7 @@ struct FnTableEntry { uint32_t alignstack_value; ZigList export_list; + bool calls_errorable_function; }; uint32_t fn_table_entry_hash(FnTableEntry*); @@ -1273,6 +1274,7 @@ enum BuiltinFnId { BuiltinFnIdSetAlignStack, BuiltinFnIdArgType, BuiltinFnIdExport, + BuiltinFnIdErrorReturnTrace, }; struct BuiltinFnEntry { @@ -1498,6 +1500,7 @@ struct CodeGen { Buf triple_str; BuildMode build_mode; bool is_test_build; + bool have_err_ret_tracing; uint32_t target_os_index; uint32_t target_arch_index; uint32_t target_environ_index; @@ -1530,6 +1533,7 @@ struct CodeGen { FnTableEntry *panic_fn; LLVMValueRef cur_ret_ptr; LLVMValueRef cur_fn_val; + LLVMValueRef cur_err_ret_trace_val; bool c_want_stdint; bool c_want_stdbool; AstNode *root_export_decl; @@ -1572,6 +1576,8 @@ struct CodeGen { size_t largest_err_name_len; LLVMValueRef safety_crash_err_fn; + LLVMValueRef return_err_fn; + IrInstruction *invalid_instruction; ConstExprValue const_void_val; @@ -1595,6 +1601,8 @@ struct CodeGen { ZigList tld_ref_source_node_stack; TypeTableEntry *align_amt_type; + TypeTableEntry *stack_trace_type; + TypeTableEntry *ptr_to_stack_trace_type; }; enum VarLinkage { @@ -1896,6 +1904,7 @@ enum IrInstructionId { IrInstructionIdSetAlignStack, IrInstructionIdArgType, IrInstructionIdExport, + IrInstructionIdErrorReturnTrace, }; struct IrInstruction { @@ -2717,6 +2726,10 @@ struct IrInstructionExport { IrInstruction *target; }; +struct IrInstructionErrorReturnTrace { + IrInstruction base; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index 7e4a861f0f5e..8028ace28900 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -869,6 +869,16 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) { zig_unreachable(); } +TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) { + if (g->stack_trace_type == nullptr) { + ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace"); + assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType); + g->stack_trace_type = stack_trace_type_val->data.x_type; + g->ptr_to_stack_trace_type = get_pointer_to_type(g, g->stack_trace_type, false); + } + return g->ptr_to_stack_trace_type; +} + TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { auto table_entry = g->fn_type_table.maybe_get(fn_type_id); if (table_entry) { @@ -915,10 +925,16 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { if (!skip_debug_info) { bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) && handle_is_ptr(fn_type_id->return_type); + bool prefix_arg_error_return_trace = g->have_err_ret_tracing && + (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion || + fn_type_id->return_type->id == TypeTableEntryIdPureError); // +1 for maybe making the first argument the return value - LLVMTypeRef *gen_param_types = allocate(1 + fn_type_id->param_count); - // +1 because 0 is the return type and +1 for maybe making first arg ret val - ZigLLVMDIType **param_di_types = allocate(2 + fn_type_id->param_count); + // +1 for maybe last argument the error return trace + LLVMTypeRef *gen_param_types = allocate(2 + fn_type_id->param_count); + // +1 because 0 is the return type and + // +1 for maybe making first arg ret val and + // +1 for maybe last argument the error return trace + ZigLLVMDIType **param_di_types = allocate(3 + fn_type_id->param_count); param_di_types[0] = fn_type_id->return_type->di_type; size_t gen_param_index = 0; TypeTableEntry *gen_return_type; @@ -936,6 +952,14 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { } fn_type->data.fn.gen_return_type = gen_return_type; + if (prefix_arg_error_return_trace) { + TypeTableEntry *gen_type = get_ptr_to_stack_trace_type(g); + gen_param_types[gen_param_index] = gen_type->type_ref; + gen_param_index += 1; + // after the gen_param_index += 1 because 0 is the return type + param_di_types[gen_param_index] = gen_type->di_type; + } + fn_type->data.fn.gen_param_info = allocate(fn_type_id->param_count); for (size_t i = 0; i < fn_type_id->param_count; i += 1) { 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 } TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); + if (type_is_invalid(type_entry)) { + return g->builtin_types.entry_invalid; + } if (fn_type_id.cc != CallingConventionUnspecified) { type_ensure_zero_bits_known(g, type_entry); if (!type_has_bits(type_entry)) { @@ -2558,7 +2585,7 @@ static bool scope_is_root_decls(Scope *scope) { static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) { add_node_error(g, proto_node, - buf_sprintf("expected 'fn([]const u8) -> unreachable', found '%s'", + buf_sprintf("expected 'fn([]const u8, ?&builtin.StackTrace) -> unreachable', found '%s'", buf_ptr(&fn_type->name))); } @@ -2567,7 +2594,7 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { assert(proto_node->type == NodeTypeFnProto); TypeTableEntry *fn_type = panic_fn->type_entry; FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - if (fn_type_id->param_count != 1) { + if (fn_type_id->param_count != 2) { return wrong_panic_prototype(g, proto_node, fn_type); } 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) { return wrong_panic_prototype(g, proto_node, fn_type); } + TypeTableEntry *nullable_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g)); + if (fn_type_id->param_info[1].type != nullable_ptr_to_stack_trace_type) { + return wrong_panic_prototype(g, proto_node, fn_type); + } + TypeTableEntry *actual_return_type = fn_type_id->return_type; if (actual_return_type != g->builtin_types.entry_unreachable) { return wrong_panic_prototype(g, proto_node, fn_type); @@ -2680,13 +2712,6 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { { if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) { g->main_fn = fn_table_entry; - TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void); - TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; - if (actual_return_type != err_void) { - add_node_error(g, fn_proto->return_type, - buf_sprintf("expected return type of main to be '%%void', instead is '%s'", - buf_ptr(&actual_return_type->name))); - } } else if ((import->package == g->panic_package || g->have_pub_panic) && buf_eql_str(&fn_table_entry->symbol_name, "panic")) { @@ -5527,3 +5552,13 @@ bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) { return a == b; } +ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { + Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name)); + resolve_top_level_decl(codegen, tld, false, nullptr); + assert(tld->id == TldIdVar); + TldVar *tld_var = (TldVar *)tld; + ConstExprValue *var_value = tld_var->var->value; + assert(var_value != nullptr); + return var_value; +} + diff --git a/src/analyze.hpp b/src/analyze.hpp index 6224e64dd545..3992cefdfcd6 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -185,4 +185,9 @@ PackageTableEntry *new_anonymous_package(void); Buf *const_value_to_buffer(ConstExprValue *const_val); void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc); + +ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name); +TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g); + + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 9ad71a936eff..c72bb07c8bcc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -404,6 +404,19 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) { zig_unreachable(); } +static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_entry) { + if (!g->have_err_ret_tracing) { + return UINT32_MAX; + } + TypeTableEntry *fn_type = fn_table_entry->type_entry; + TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; + if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) { + return UINT32_MAX; + } + bool first_arg_ret = type_has_bits(return_type) && handle_is_ptr(return_type); + return first_arg_ret ? 1 : 0; +} + static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { if (fn_table_entry->llvm_value) return fn_table_entry->llvm_value; @@ -483,7 +496,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { LLVMSetUnnamedAddr(fn_table_entry->llvm_value, true); } - if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) { + TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; + if (return_type->id == TypeTableEntryIdUnreachable) { addLLVMFnAttr(fn_table_entry->llvm_value, "noreturn"); } @@ -520,13 +534,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { // use the ABI alignment, which is fine. } - if (!type_has_bits(fn_type->data.fn.fn_type_id.return_type)) { + if (!type_has_bits(return_type)) { // nothing to do - } else if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdPointer || - fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdFn) - { + } else if (return_type->id == TypeTableEntryIdPointer || return_type->id == TypeTableEntryIdFn) { addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); - } else if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type) && + } else if (handle_is_ptr(return_type) && calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc)) { addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); @@ -563,6 +575,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { } } + uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); + if (err_ret_trace_arg_index != UINT32_MAX) { + addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); + } + return fn_table_entry->llvm_value; } @@ -864,16 +881,25 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0)); } -static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) { +static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg) { assert(g->panic_fn != nullptr); LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn); LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc); - ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, ""); + if (stack_trace_arg == nullptr) { + TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); + stack_trace_arg = LLVMConstNull(ptr_to_stack_trace_type->type_ref); + } + LLVMValueRef args[] = { + msg_arg, + stack_trace_arg, + }; + LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, ""); + LLVMSetTailCall(call_instruction, true); LLVMBuildUnreachable(g->builder); } static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) { - gen_panic(g, get_panic_msg_ptr_val(g, msg_id)); + gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr); } static LLVMValueRef get_memcpy_fn_val(CodeGen *g) { @@ -895,6 +921,87 @@ static LLVMValueRef get_memcpy_fn_val(CodeGen *g) { return g->memcpy_fn_val; } +static LLVMValueRef get_return_err_fn(CodeGen *g) { + if (g->return_err_fn != nullptr) + return g->return_err_fn; + + assert(g->err_tag_type != nullptr); + + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + + LLVMTypeRef arg_types[] = { + // error return trace pointer + get_ptr_to_stack_trace_type(g)->type_ref, + // return address + ptr_u8, + }; + LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); + + Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_return_error"), false); + LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); + addLLVMFnAttr(fn_val, "cold"); + LLVMSetLinkage(fn_val, LLVMInternalLinkage); + LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + addLLVMFnAttr(fn_val, "nounwind"); + add_uwtable_attr(g, fn_val); + addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); + addLLVMArgAttr(fn_val, (unsigned)1, "nonnull"); + if (g->build_mode == BuildModeDebug) { + ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true"); + ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr); + } + + LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); + LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); + LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); + LLVMPositionBuilderAtEnd(g->builder, entry_block); + ZigLLVMClearCurrentDebugLocation(g->builder); + + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; + + // stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address; + + LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); + size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; + LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, ""); + size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; + LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, ""); + + TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry; + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, ""); + size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); + + LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, ""); + LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, ""); + LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, len_value, ""); + LLVMValueRef address_indices[] = { + modded_val, + }; + + LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); + LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, ""); + + LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, LLVMGetParam(fn_val, 1), usize_type_ref, ""); + + LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, return_address, usize_type_ref, ""); + gen_store_untyped(g, address_value, address_slot, 0, false); + + // stack_trace.index += 1; + LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), ""); + gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false); + + // return; + LLVMBuildRetVoid(g->builder); + + LLVMPositionBuilderAtEnd(g->builder, prev_block); + LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); + + g->return_err_fn = fn_val; + return fn_val; +} + static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { if (g->safety_crash_err_fn != nullptr) return g->safety_crash_err_fn; @@ -953,7 +1060,11 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2); Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false); - LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), &g->err_tag_type->type_ref, 1, false); + LLVMTypeRef arg_types[] = { + g->ptr_to_stack_trace_type->type_ref, + g->err_tag_type->type_ref, + }; + LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); addLLVMFnAttr(fn_val, "noreturn"); addLLVMFnAttr(fn_val, "cold"); @@ -975,7 +1086,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { LLVMPositionBuilderAtEnd(g->builder, entry_block); ZigLLVMClearCurrentDebugLocation(g->builder); - LLVMValueRef err_val = LLVMGetParam(fn_val, 0); + LLVMValueRef err_val = LLVMGetParam(fn_val, 1); LLVMValueRef err_table_indices[] = { LLVMConstNull(g->builtin_types.entry_usize->type_ref), @@ -1005,7 +1116,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, ""); gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type); - gen_panic(g, global_slice); + gen_panic(g, global_slice, LLVMGetParam(fn_val, 0)); LLVMPositionBuilderAtEnd(g->builder, prev_block); LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); @@ -1016,8 +1127,18 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) { LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g); - ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified), + LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val; + if (err_ret_trace_val == nullptr) { + TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); + err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref); + } + LLVMValueRef args[] = { + err_ret_trace_val, + err_val, + }; + LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); + LLVMSetTailCall(call_instruction, true); LLVMBuildUnreachable(g->builder); } @@ -1296,6 +1417,35 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { LLVMValueRef value = ir_llvm_value(g, return_instruction->value); TypeTableEntry *return_type = return_instruction->value->value.type; + + if (g->have_err_ret_tracing) { + bool is_err_return = false; + if (return_type->id == TypeTableEntryIdErrorUnion) { + if (return_instruction->value->value.special == ConstValSpecialStatic) { + is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr; + } else if (return_instruction->value->value.special == ConstValSpecialRuntime) { + is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError; + // TODO: emit a branch to check if the return value is an error + } + } else if (return_type->id == TypeTableEntryIdPureError) { + is_err_return = true; + } + if (is_err_return) { + LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError"); + LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block); + + LLVMValueRef return_err_fn = get_return_err_fn(g); + LLVMValueRef args[] = { + g->cur_err_ret_trace_val, + block_address, + }; + LLVMBuildBr(g->builder, return_block); + LLVMPositionBuilderAtEnd(g->builder, return_block); + LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 2, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); + LLVMSetTailCall(call_instruction, true); + } + } if (handle_is_ptr(return_type)) { if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) { assert(g->cur_ret_ptr); @@ -2330,7 +2480,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr TypeTableEntry *src_return_type = fn_type_id->return_type; bool ret_has_bits = type_has_bits(src_return_type); bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type); - size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0); + bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError); + size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0); bool is_var_args = fn_type_id->is_var_args; LLVMValueRef *gen_param_values = allocate(actual_param_count); size_t gen_param_index = 0; @@ -2338,6 +2489,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr gen_param_values[gen_param_index] = instruction->tmp_ptr; gen_param_index += 1; } + if (prefix_arg_err_ret_stack) { + gen_param_values[gen_param_index] = g->cur_err_ret_trace_val; + gen_param_index += 1; + } for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) { IrInstruction *param_instruction = instruction->args[call_i]; TypeTableEntry *param_type = param_instruction->value.type; @@ -2881,6 +3036,16 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I return target_val; } +static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable, + IrInstructionErrorReturnTrace *instruction) +{ + TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); + if (g->cur_err_ret_trace_val == nullptr) { + return LLVMConstNull(ptr_to_stack_trace_type->type_ref); + } + return g->cur_err_ret_trace_val; +} + static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { switch (atomic_order) { case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered; @@ -3474,7 +3639,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec } static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) { - gen_panic(g, ir_llvm_value(g, instruction->msg)); + gen_panic(g, ir_llvm_value(g, instruction->msg), nullptr); return nullptr; } @@ -3654,6 +3819,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction); case IrInstructionIdAlignCast: return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction); + case IrInstructionIdErrorReturnTrace: + return ir_render_error_return_trace(g, executable, (IrInstructionErrorReturnTrace *)instruction); } zig_unreachable(); } @@ -4493,7 +4660,8 @@ static void do_code_gen(CodeGen *g) { LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); g->cur_fn = fn_table_entry; g->cur_fn_val = fn; - if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) { + TypeTableEntry *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; + if (handle_is_ptr(return_type)) { g->cur_ret_ptr = LLVMGetParam(fn, 0); } else { g->cur_ret_ptr = nullptr; @@ -4502,6 +4670,42 @@ static void do_code_gen(CodeGen *g) { build_all_basic_blocks(g, fn_table_entry); clear_debug_source_node(g); + uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); + if (err_ret_trace_arg_index != UINT32_MAX) { + g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index); + } else if (g->have_err_ret_tracing && fn_table_entry->calls_errorable_function) { + // TODO call graph analysis to find out what this number needs to be for every function + static const size_t stack_trace_ptr_count = 30; + + TypeTableEntry *usize = g->builtin_types.entry_usize; + TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count); + LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", + get_abi_alignment(g, array_type)); + g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type)); + size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; + LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, ""); + gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false); + + size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; + LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, ""); + + TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry; + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, ""); + LLVMValueRef zero = LLVMConstNull(usize->type_ref); + LLVMValueRef indices[] = {zero, zero}; + LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val, + indices, 2, ""); + gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr, + get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false)); + + size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); + gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false)); + } else { + g->cur_err_ret_trace_val = nullptr; + } + // allocate temporary stack data for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) { IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i); @@ -5064,6 +5268,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1); create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2); create_builtin_fn(g, BuiltinFnIdExport, "export", 3); + create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); } static const char *bool_to_str(bool b) { @@ -5088,6 +5293,12 @@ static void define_builtin_compile_vars(CodeGen *g) { os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); Buf *contents = buf_alloc(); + buf_append_str(contents, + "pub const StackTrace = struct {\n" + " index: usize,\n" + " instruction_addresses: []usize,\n" + "};\n\n"); + const char *cur_os = nullptr; { buf_appendf(contents, "pub const Os = enum {\n"); @@ -5233,6 +5444,7 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); + buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); @@ -5251,6 +5463,7 @@ static void define_builtin_compile_vars(CodeGen *g) { g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->compile_var_import = add_source_file(g, g->compile_var_package, abs_full_path, contents); + scan_import(g, g->compile_var_import); } static void init(CodeGen *g) { @@ -5359,6 +5572,8 @@ static void init(CodeGen *g) { } } + g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease; + define_builtin_fns(g); define_builtin_compile_vars(g); } diff --git a/src/ir.cpp b/src/ir.cpp index f23691025005..d6844f32ba77 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -572,6 +572,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) { return IrInstructionIdArgType; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) { + return IrInstructionIdErrorReturnTrace; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2305,6 +2309,12 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } +static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionErrorReturnTrace *instruction = ir_build_instruction(irb, scope, source_node); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3731,6 +3741,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); } + case BuiltinFnIdErrorReturnTrace: + { + return ir_build_error_return_trace(irb, scope, node); + } } zig_unreachable(); } @@ -8230,16 +8244,6 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out) return ir_resolve_bool(ira, value, out); } -static ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { - Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name)); - resolve_top_level_decl(codegen, tld, false, nullptr); - assert(tld->id == TldIdVar); - TldVar *tld_var = (TldVar *)tld; - ConstExprValue *var_value = tld_var->var->value; - assert(var_value != nullptr); - return var_value; -} - static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) { if (type_is_invalid(value->value.type)) return false; @@ -9578,6 +9582,24 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi return ira->codegen->builtin_types.entry_void; } +static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, + IrInstructionErrorReturnTrace *instruction) +{ + FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); + TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); + TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); + if (fn_entry == nullptr || !fn_entry->calls_errorable_function || !ira->codegen->have_err_ret_tracing) { + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_maybe = nullptr; + return nullable_type; + } + + IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, + instruction->base.source_node); + ir_link_new_instruction(new_instruction, &instruction->base); + return nullable_type; +} + static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i) { @@ -10053,9 +10075,21 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; ir_add_alloca(ira, new_call_instruction, return_type); + if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) { + parent_fn_entry->calls_errorable_function = true; + } + return ir_finish_anal(ira, return_type); } + FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); + assert(fn_type_id->return_type != nullptr); + assert(parent_fn_entry != nullptr); + if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) { + parent_fn_entry->calls_errorable_function = true; + } + + IrInstruction **casted_args = allocate(call_param_count); size_t next_arg_index = 0; if (first_arg_ptr) { @@ -15322,6 +15356,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction); case IrInstructionIdExport: return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction); + case IrInstructionIdErrorReturnTrace: + return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction); } zig_unreachable(); } @@ -15505,6 +15541,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdOpaqueType: case IrInstructionIdArgType: case IrInstructionIdTagType: + case IrInstructionIdErrorReturnTrace: return false; case IrInstructionIdAsm: { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index f5aba2a45d48..930d22f21ab8 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -996,6 +996,10 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) { } } +static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { + fprintf(irp->f, "@errorReturnTrace()"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); @@ -1308,6 +1312,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdExport: ir_print_export(irp, (IrInstructionExport *)instruction); break; + case IrInstructionIdErrorReturnTrace: + ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/std/build.zig b/std/build.zig index cd6b3811eade..5d79b00c4f8c 100644 --- a/std/build.zig +++ b/std/build.zig @@ -247,11 +247,11 @@ pub const Builder = struct { defer wanted_steps.deinit(); if (step_names.len == 0) { - wanted_steps.append(&self.default_step) catch unreachable; + try wanted_steps.append(&self.default_step); } else { for (step_names) |step_name| { const s = try self.getTopLevelStepByName(step_name); - wanted_steps.append(s) catch unreachable; + try wanted_steps.append(s); } } @@ -721,11 +721,9 @@ pub const Builder = struct { return error.FileNotFound; } - pub fn exec(self: &Builder, argv: []const []const u8) -> []u8 { + pub fn exec(self: &Builder, argv: []const []const u8) -> %[]u8 { const max_output_size = 100 * 1024; - const result = os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size) catch |err| { - std.debug.panic("Unable to spawn {}: {}", argv[0], @errorName(err)); - }; + const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size); switch (result.term) { os.ChildProcess.Term.Exited => |code| { if (code != 0) { diff --git a/std/debug/index.zig b/std/debug/index.zig index 464974b7debe..5dc7b0dbfaf3 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -13,6 +13,10 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; error MissingDebugInfo; error InvalidDebugInfo; error UnsupportedDebugInfo; +error UnknownObjectFormat; +error TodoSupportCoffDebugInfo; +error TodoSupportMachoDebugInfo; +error TodoSupportCOFFDebugInfo; /// Tries to write to stderr, unbuffered, and ignores any error returned. @@ -37,10 +41,32 @@ fn getStderrStream() -> %&io.OutStream { } } +/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. +pub fn dumpCurrentStackTrace() { + const stderr = getStderrStream() catch return; + const debug_info = openSelfDebugInfo(global_allocator) catch |err| { + stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; + return; + }; + defer debug_info.close(); + writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), 1) catch |err| { + stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; + return; + }; +} + /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. -pub fn dumpStackTrace() { +pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) { const stderr = getStderrStream() catch return; - writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return; + const debug_info = openSelfDebugInfo(global_allocator) catch |err| { + stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; + return; + }; + defer debug_info.close(); + writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| { + stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; + return; + }; } /// This function invokes undefined behavior when `ok` is `false`. @@ -88,7 +114,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn { const stderr = getStderrStream() catch os.abort(); stderr.print(format ++ "\n", args) catch os.abort(); - writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort(); + dumpCurrentStackTrace(); os.abort(); } @@ -101,12 +127,91 @@ const RESET = "\x1b[0m"; error PathNotFound; error InvalidDebugInfo; -pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, - ignore_frame_count: usize) -> %void +pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, + debug_info: &ElfStackTrace, tty_color: bool) -> %void +{ + var frame_index: usize = undefined; + var frames_left: usize = undefined; + if (stack_trace.index < stack_trace.instruction_addresses.len) { + frame_index = 0; + frames_left = stack_trace.index; + } else { + frame_index = (stack_trace.index + 1) % stack_trace.instruction_addresses.len; + frames_left = stack_trace.instruction_addresses.len; + } + + while (frames_left != 0) : ({ + frames_left -= 1; + frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; + }) { + const return_address = stack_trace.instruction_addresses[frame_index]; + try printSourceAtAddress(debug_info, out_stream, return_address); + } +} + +pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, + debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) -> %void { + var ignored_count: usize = 0; + + var fp = @ptrToInt(@frameAddress()); + while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) { + if (ignored_count < ignore_frame_count) { + ignored_count += 1; + continue; + } + + const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); + try printSourceAtAddress(debug_info, out_stream, return_address); + } +} + +fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void { + if (builtin.os == builtin.Os.windows) { + return error.UnsupportedDebugInfo; + } + // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal + // at compile time. I'll call it issue #313 + const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; + + const compile_unit = findCompileUnit(debug_info, address) catch { + try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", + address); + return; + }; + const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); + if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { + defer line_info.deinit(); + try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ + DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", + line_info.file_name, line_info.line, line_info.column, + address, compile_unit_name); + if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { + if (line_info.column == 0) { + try out_stream.write("\n"); + } else { + {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { + try out_stream.writeByte(' '); + }} + try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); + } + } else |err| switch (err) { + error.EndOfFile, error.PathNotFound => {}, + else => return err, + } + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); + }, + else => return err, + } +} + +pub fn openSelfDebugInfo(allocator: &mem.Allocator) -> %&ElfStackTrace { switch (builtin.object_format) { builtin.ObjectFormat.elf => { - var stack_trace = ElfStackTrace { + const st = try allocator.create(ElfStackTrace); + *st = ElfStackTrace { .self_exe_file = undefined, .elf = undefined, .debug_info = undefined, @@ -117,12 +222,11 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), .compile_unit_list = ArrayList(CompileUnit).init(allocator), }; - const st = &stack_trace; st.self_exe_file = try os.openSelfExe(); - defer st.self_exe_file.close(); + %defer st.self_exe_file.close(); try st.elf.openFile(allocator, &st.self_exe_file); - defer st.elf.close(); + %defer st.elf.close(); st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; @@ -130,67 +234,19 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; st.debug_ranges = (try st.elf.findSection(".debug_ranges")); try scanAllCompileUnits(st); - - var ignored_count: usize = 0; - - var fp = @ptrToInt(@frameAddress()); - while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) { - if (ignored_count < ignore_frame_count) { - ignored_count += 1; - continue; - } - - const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); - - // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal - // at compile time. I'll call it issue #313 - const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; - - const compile_unit = findCompileUnit(st, return_address) catch { - try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", - return_address); - continue; - }; - const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name); - if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| { - defer line_info.deinit(); - try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ - DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", - line_info.file_name, line_info.line, line_info.column, - return_address, compile_unit_name); - if (printLineFromFile(st.allocator(), out_stream, line_info)) { - if (line_info.column == 0) { - try out_stream.write("\n"); - } else { - {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { - try out_stream.writeByte(' '); - }} - try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); - } - } else |err| switch (err) { - error.EndOfFile, error.PathNotFound => {}, - else => return err, - } - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - try out_stream.print(ptr_hex ++ " in ??? ({})\n", - return_address, compile_unit_name); - }, - else => return err, - } - } + return st; }, builtin.ObjectFormat.coff => { - try out_stream.write("(stack trace unavailable for COFF object format)\n"); + return error.TodoSupportCoffDebugInfo; }, builtin.ObjectFormat.macho => { - try out_stream.write("(stack trace unavailable for Mach-O object format)\n"); + return error.TodoSupportMachoDebugInfo; }, builtin.ObjectFormat.wasm => { - try out_stream.write("(stack trace unavailable for WASM object format)\n"); + return error.TodoSupportCOFFDebugInfo; }, builtin.ObjectFormat.unknown => { - try out_stream.write("(stack trace unavailable for unknown object format)\n"); + return error.UnknownObjectFormat; }, } } @@ -228,7 +284,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_ } } -const ElfStackTrace = struct { +pub const ElfStackTrace = struct { self_exe_file: io.File, elf: elf.Elf, debug_info: &elf.SectionHeader, @@ -248,6 +304,11 @@ const ElfStackTrace = struct { const in_stream = &in_file_stream.stream; return readStringRaw(self.allocator(), in_stream); } + + pub fn close(self: &ElfStackTrace) { + self.self_exe_file.close(); + self.elf.close(); + } }; const PcRange = struct { diff --git a/std/io.zig b/std/io.zig index c6fd5502a2ec..605553b0ea9c 100644 --- a/std/io.zig +++ b/std/io.zig @@ -224,7 +224,7 @@ pub const File = struct { }; } }, - else => @compileError("unsupported OS"), + else => @compileError("unsupported OS: " ++ @tagName(builtin.os)), } } diff --git a/std/os/index.zig b/std/os/index.zig index e33835952260..fd8eb84ab41b 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -148,7 +148,7 @@ pub coldcc fn abort() -> noreturn { } /// Exits the program cleanly with the specified status code. -pub coldcc fn exit(status: i32) -> noreturn { +pub coldcc fn exit(status: u8) -> noreturn { if (builtin.link_libc) { c.exit(status); } @@ -157,14 +157,7 @@ pub coldcc fn exit(status: i32) -> noreturn { posix.exit(status); }, Os.windows => { - // Map a possibly negative status code to a non-negative status for the systems default - // integer width. - const p_status = if (@sizeOf(c_uint) < @sizeOf(u32)) - @truncate(c_uint, @bitCast(u32, status)) - else - c_uint(@bitCast(u32, status)); - - windows.ExitProcess(p_status); + windows.ExitProcess(status); }, else => @compileError("Unsupported OS"), } diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 55d28a2d0e07..77bb7316a927 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -21,8 +21,7 @@ comptime { } extern fn zenMain() -> noreturn { - root.main() catch std.os.posix.exit(1); - std.os.posix.exit(0); + std.os.posix.exit(callMain()); } nakedcc fn _start() -> noreturn { @@ -43,29 +42,55 @@ nakedcc fn _start() -> noreturn { extern fn WinMainCRTStartup() -> noreturn { @setAlignStack(16); - root.main() catch std.os.windows.ExitProcess(1); - std.os.windows.ExitProcess(0); + std.os.windows.ExitProcess(callMain()); } fn posixCallMainAndExit() -> noreturn { const argc = *argc_ptr; const argv = @ptrCast(&&u8, &argc_ptr[1]); const envp = @ptrCast(&?&u8, &argv[argc + 1]); - callMain(argc, argv, envp) catch std.os.posix.exit(1); - std.os.posix.exit(0); + std.os.posix.exit(callMainWithArgs(argc, argv, envp)); } -fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void { +fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) -> u8 { std.os.ArgIteratorPosix.raw = argv[0..argc]; var env_count: usize = 0; while (envp[env_count] != null) : (env_count += 1) {} std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count]; - return root.main(); + return callMain(); } extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 { - callMain(usize(c_argc), c_argv, c_envp) catch return 1; - return 0; + return callMainWithArgs(usize(c_argc), c_argv, c_envp); +} + +fn callMain() -> u8 { + switch (@typeId(@typeOf(root.main).ReturnType)) { + builtin.TypeId.NoReturn => { + root.main(); + }, + builtin.TypeId.Void => { + root.main(); + return 0; + }, + builtin.TypeId.Int => { + if (@typeOf(root.main).ReturnType.bit_count != 8) { + @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); + } + return root.main(); + }, + builtin.TypeId.ErrorUnion => { + root.main() catch |err| { + std.debug.warn("error: {}\n", @errorName(err)); + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace); + } + return 1; + }; + return 0; + }, + else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"), + } } diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index 24d9c756e7c6..4722aff07abc 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -14,7 +14,7 @@ pub fn main() -> %void { var arg_it = os.args(); // TODO use a more general purpose allocator here - var inc_allocator = std.heap.IncrementingAllocator.init(40 * 1024 * 1024) catch unreachable; + var inc_allocator = try std.heap.IncrementingAllocator.init(40 * 1024 * 1024); defer inc_allocator.deinit(); const allocator = &inc_allocator.allocator; @@ -107,12 +107,12 @@ pub fn main() -> %void { return usageAndErr(&builder, false, try stderr_stream); } } else { - targets.append(arg) catch unreachable; + try targets.append(arg); } } builder.setInstallPrefix(prefix); - root.build(&builder); + try root.build(&builder); if (builder.validateUserInputDidItFail()) return usageAndErr(&builder, true, try stderr_stream); @@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) // run the build script to collect the options if (!already_ran_build) { builder.setInstallPrefix(null); - root.build(builder); + try root.build(builder); } // This usage text has to be synchronized with src/main.cpp diff --git a/std/special/builtin.zig b/std/special/builtin.zig index e6c09863ca98..dd77ba9c75c8 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -5,7 +5,7 @@ const builtin = @import("builtin"); // Avoid dragging in the debug safety mechanisms into this .o file, // unless we're trying to test this file. -pub coldcc fn panic(msg: []const u8) -> noreturn { +pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { if (builtin.is_test) { @import("std").debug.panic("{}", msg); } else { diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 5034a6fd9066..96f34e56ff64 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -74,7 +74,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; // Avoid dragging in the debug safety mechanisms into this .o file, // unless we're trying to test this file. -pub coldcc fn panic(msg: []const u8) -> noreturn { +pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { if (is_test) { @import("std").debug.panic("{}", msg); } else { diff --git a/std/special/panic.zig b/std/special/panic.zig index 03c2586739f8..d26166fdace0 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -4,14 +4,22 @@ // have to be added in the compiler. const builtin = @import("builtin"); +const std = @import("std"); -pub coldcc fn panic(msg: []const u8) -> noreturn { +pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { switch (builtin.os) { // TODO: fix panic in zen. builtin.Os.freestanding, builtin.Os.zen => { while (true) {} }, else => { + if (builtin.have_error_return_tracing) { + if (error_return_trace) |trace| { + std.debug.warn("{}\n", msg); + std.debug.dumpStackTrace(trace); + @import("std").debug.panic(""); + } + } @import("std").debug.panic("{}", msg); }, } diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig index 1a36f50b62ba..c5c0bad40c88 100644 --- a/std/special/test_runner.zig +++ b/std/special/test_runner.zig @@ -8,10 +8,7 @@ pub fn main() -> %void { for (test_fn_list) |test_fn, i| { warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name); - test_fn.func() catch |err| { - warn("{}\n", err); - return err; - }; + try test_fn.func(); warn("OK\n"); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 005fe55e413a..6b2ea545edd6 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) { + cases.add("wrong return type for main", + \\pub fn main() -> f32 { } + , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); + + cases.add("double ?? on main return value", + \\pub fn main() -> ??void { + \\} + , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); + cases.add("bad identifier in function with struct defined inside function which references local const", \\export fn entry() { \\ const BlockKind = u32; @@ -1059,15 +1068,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:2:5: error: expected type 'void', found 'error'"); - cases.add("wrong return type for main", - \\pub fn main() { } - , ".tmp_source.zig:1:15: error: expected return type of main to be '%void', instead is 'void'"); - - cases.add("double ?? on main return value", - \\pub fn main() -> ??void { - \\} - , ".tmp_source.zig:1:18: error: expected return type of main to be '%void', instead is '??void'"); - cases.add("invalid pointer for var type", \\extern fn ext() -> usize; \\var bytes: [ext()]u8 = undefined; diff --git a/test/debug_safety.zig b/test/debug_safety.zig index fde5b061eec6..b32ffb34f0fb 100644 --- a/test/debug_safety.zig +++ b/test/debug_safety.zig @@ -2,7 +2,7 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompareOutputContext) { cases.addDebugSafety("calling panic", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() -> %void { @@ -11,7 +11,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("out of bounds slice access", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() -> %void { @@ -25,7 +25,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("integer addition overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -39,7 +39,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("integer subtraction overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -53,7 +53,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("integer multiplication overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -67,7 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("integer negation overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -81,7 +81,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("signed integer division overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -95,7 +95,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("signed shift left overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -109,7 +109,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("unsigned shift left overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -123,7 +123,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("signed shift right overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -137,7 +137,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("unsigned shift right overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -151,7 +151,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("integer division by zero", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -164,7 +164,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("exact division failure", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -178,7 +178,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("cast []u8 to bigger slice of wrong size", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -192,7 +192,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("value does not fit in shortening cast", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -206,7 +206,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("signed integer not fitting in cast to unsigned integer", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -220,7 +220,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("unwrap error", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) { \\ @import("std").os.exit(126); // good \\ } @@ -236,7 +236,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("cast integer to error and no code matches", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() -> %void { @@ -248,7 +248,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("@alignCast misaligned", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Wrong; @@ -265,7 +265,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("bad union field access", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\ diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig index 50fa10c59347..b7b288f7a174 100644 --- a/test/standalone/issue_339/build.zig +++ b/test/standalone/issue_339/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { const obj = b.addObject("test", "test.zig"); const test_step = b.step("test", "Test the program"); diff --git a/test/standalone/issue_339/test.zig b/test/standalone/issue_339/test.zig index e0d6c3f3e51f..dd97ffa9b1ef 100644 --- a/test/standalone/issue_339/test.zig +++ b/test/standalone/issue_339/test.zig @@ -1,4 +1,5 @@ -pub fn panic(msg: []const u8) -> noreturn { @breakpoint(); while (true) {} } +const StackTrace = @import("builtin").StackTrace; +pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) -> noreturn { @breakpoint(); while (true) {} } fn bar() -> %void {} diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig index 044787dd4d3f..f40011d834bd 100644 --- a/test/standalone/pkg_import/build.zig +++ b/test/standalone/pkg_import/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { const exe = b.addExecutable("test", "test.zig"); exe.addPackagePath("my_pkg", "pkg.zig"); diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig index 03398f1a4114..beca6bc30491 100644 --- a/test/standalone/use_alias/build.zig +++ b/test/standalone/use_alias/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { b.addCIncludePath("."); const main = b.addTest("main.zig"); diff --git a/test/tests.zig b/test/tests.zig index 20e1e9445992..5fd995f0346f 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -50,6 +50,7 @@ const test_targets = []TestTarget { }; error TestFailed; +error CompilationIncorrectlySucceeded; const max_stdout_size = 1 * 1024 * 1024; // 1 MB @@ -607,8 +608,7 @@ pub const CompileErrorContext = struct { switch (term) { Term.Exited => |code| { if (code == 0) { - warn("Compilation incorrectly succeeded\n"); - return error.TestFailed; + return error.CompilationIncorrectlySucceeded; } }, else => {