From 7ec783876a565662223268a70ba984e0a132b94a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 11 Jan 2018 23:04:08 -0500 Subject: [PATCH 01/12] functions which can return errors have secret stack trace param See #651 --- src/all_types.hpp | 2 ++ src/analyze.cpp | 39 ++++++++++++++++++++++++++++++++++++--- src/analyze.hpp | 4 ++++ src/codegen.cpp | 29 ++++++++++++++++++++++------- src/ir.cpp | 10 ---------- 5 files changed, 64 insertions(+), 20 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 024c78eb73..caae13d20d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1595,6 +1595,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 { diff --git a/src/analyze.cpp b/src/analyze.cpp index 7e4a861f0f..2e51e5151c 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(); } +static 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,15 @@ 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 last_arg_error_return_trace = 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; @@ -965,6 +980,14 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { } } + if (last_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_count = gen_param_index; fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, @@ -5527,3 +5550,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 6224e64dd5..050630bc07 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -185,4 +185,8 @@ 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); + + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 9ad71a936e..4de0fcec51 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -483,7 +483,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 +521,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"); @@ -562,6 +561,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval"); } } + if (return_type->id == TypeTableEntryIdErrorUnion || return_type->id == TypeTableEntryIdPureError) { + unsigned gen_index = LLVMCountParamTypes(fn_llvm_type) - 1; + addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull"); + } return fn_table_entry->llvm_value; } @@ -2330,7 +2333,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 last_arg_err_ret_stack = src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError; + size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (last_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; @@ -2348,6 +2352,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr gen_param_index += 1; } } + if (last_arg_err_ret_stack) { + gen_param_values[gen_param_index] = LLVMGetUndef(g->ptr_to_stack_trace_type->type_ref); + gen_param_index += 1; + } ZigLLVM_FnInline fn_inline; switch (instruction->fn_inline) { @@ -5088,6 +5096,13 @@ 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: [31]usize,\n" + "};\n\n" + ); + const char *cur_os = nullptr; { buf_appendf(contents, "pub const Os = enum {\n"); diff --git a/src/ir.cpp b/src/ir.cpp index f236910250..340e4fbeda 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8230,16 +8230,6 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out) return ir_resolve_bool(ira, value, out); } -static ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { - Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name)); - resolve_top_level_decl(codegen, tld, false, nullptr); - assert(tld->id == TldIdVar); - TldVar *tld_var = (TldVar *)tld; - ConstExprValue *var_value = tld_var->var->value; - assert(var_value != nullptr); - return var_value; -} - static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) { if (type_is_invalid(value->value.type)) return false; From 32ea6f54e5f05c4173828c4f4c8ab9965a929120 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 12 Jan 2018 02:12:11 -0500 Subject: [PATCH 02/12] *WIP* proof of concept error return traces --- build.zig | 12 ++-- src/all_types.hpp | 2 + src/analyze.cpp | 6 +- src/analyze.hpp | 1 + src/codegen.cpp | 106 ++++++++++++++++++++++++---- src/ir.cpp | 12 ++++ std/build.zig | 6 +- std/debug/index.zig | 112 ++++++++++++++++++++++++++++-- std/special/build_runner.zig | 4 +- std/special/builtin.zig | 2 +- std/special/compiler_rt/index.zig | 2 +- std/special/panic.zig | 8 ++- 12 files changed, 239 insertions(+), 34 deletions(-) diff --git a/build.zig b/build.zig index 7c1563c706..1fa984dcff 100644 --- a/build.zig +++ b/build.zig @@ -10,7 +10,7 @@ const ArrayList = std.ArrayList; const Buffer = std.Buffer; const io = std.io; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { const mode = b.standardReleaseOptions(); var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); @@ -36,7 +36,7 @@ pub fn build(b: &Builder) { const test_step = b.step("test", "Run all the tests"); // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library - const build_info = b.exec([][]const u8{b.zig_exe, "BUILD_INFO"}); + const build_info = try b.exec([][]const u8{b.zig_exe, "BUILD_INFO"}); var index: usize = 0; const cmake_binary_dir = nextValue(&index, build_info); const cxx_compiler = nextValue(&index, build_info); @@ -68,7 +68,7 @@ pub fn build(b: &Builder) { dependOnLib(exe, llvm); if (exe.target.getOs() == builtin.Os.linux) { - const libstdcxx_path_padded = b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"}); + const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"}); const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next(); exe.addObjectFile(libstdcxx_path); @@ -155,9 +155,9 @@ const LibraryDep = struct { }; fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep { - const libs_output = b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); - const includes_output = b.exec([][]const u8{llvm_config_exe, "--includedir"}); - const libdir_output = b.exec([][]const u8{llvm_config_exe, "--libdir"}); + const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); + const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"}); + const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"}); var result = LibraryDep { .libs = ArrayList([]const u8).init(b.allocator), diff --git a/src/all_types.hpp b/src/all_types.hpp index caae13d20d..6476c08625 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1205,6 +1205,7 @@ struct FnTableEntry { uint32_t alignstack_value; ZigList export_list; + bool calls_errorable_function; }; uint32_t fn_table_entry_hash(FnTableEntry*); @@ -1530,6 +1531,7 @@ struct CodeGen { FnTableEntry *panic_fn; LLVMValueRef cur_ret_ptr; LLVMValueRef cur_fn_val; + LLVMValueRef cur_err_ret_trace_val; bool c_want_stdint; bool c_want_stdbool; AstNode *root_export_decl; diff --git a/src/analyze.cpp b/src/analyze.cpp index 2e51e5151c..61d86b5eb2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -869,7 +869,7 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) { zig_unreachable(); } -static TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) { +TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) { if (g->stack_trace_type == nullptr) { ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace"); assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType); @@ -1191,6 +1191,9 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c } TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); + if (type_is_invalid(type_entry)) { + return g->builtin_types.entry_invalid; + } if (fn_type_id.cc != CallingConventionUnspecified) { type_ensure_zero_bits_known(g, type_entry); if (!type_has_bits(type_entry)) { @@ -2586,6 +2589,7 @@ static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntr } static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { + return; // TODO AstNode *proto_node = panic_fn->proto_node; assert(proto_node->type == NodeTypeFnProto); TypeTableEntry *fn_type = panic_fn->type_entry; diff --git a/src/analyze.hpp b/src/analyze.hpp index 050630bc07..3992cefdfc 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -187,6 +187,7 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name); +TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g); #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 4de0fcec51..97cab9a69f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -22,6 +22,8 @@ #include #include +static const size_t stack_trace_ptr_count = 31; + static void init_darwin_native(CodeGen *g) { char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET"); char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET"); @@ -867,16 +869,24 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0)); } -static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) { +static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg) { assert(g->panic_fn != nullptr); LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn); LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc); - ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, ""); + if (stack_trace_arg == nullptr) { + TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); + stack_trace_arg = LLVMConstNull(ptr_to_stack_trace_type->type_ref); + } + LLVMValueRef args[] = { + msg_arg, + stack_trace_arg, + }; + ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, ""); LLVMBuildUnreachable(g->builder); } static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) { - gen_panic(g, get_panic_msg_ptr_val(g, msg_id)); + gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr); } static LLVMValueRef get_memcpy_fn_val(CodeGen *g) { @@ -956,7 +966,11 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2); Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false); - LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), &g->err_tag_type->type_ref, 1, false); + LLVMTypeRef arg_types[] = { + g->err_tag_type->type_ref, + g->ptr_to_stack_trace_type->type_ref, + }; + LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); addLLVMFnAttr(fn_val, "noreturn"); addLLVMFnAttr(fn_val, "cold"); @@ -1008,7 +1022,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, ""); gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type); - gen_panic(g, global_slice); + gen_panic(g, global_slice, LLVMGetParam(fn_val, 1)); LLVMPositionBuilderAtEnd(g->builder, prev_block); LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); @@ -1019,7 +1033,16 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) { LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g); - ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified), + LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val; + if (err_ret_trace_val == nullptr) { + TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); + err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref); + } + LLVMValueRef args[] = { + err_val, + err_ret_trace_val, + }; + ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); LLVMBuildUnreachable(g->builder); } @@ -1299,6 +1322,50 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { LLVMValueRef value = ir_llvm_value(g, return_instruction->value); TypeTableEntry *return_type = return_instruction->value->value.type; + + bool is_err_return = false; + if (return_type->id == TypeTableEntryIdErrorUnion) { + if (return_instruction->value->value.special == ConstValSpecialStatic) { + is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr; + } else if (return_instruction->value->value.special == ConstValSpecialRuntime) { + is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError; + // TODO: emit a branch to check if the return value is an error + } + } else if (return_type->id == TypeTableEntryIdPureError) { + is_err_return = true; + } + if (is_err_return) { + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; + + // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer(); + // stack_trace.index += 1; + + LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError"); + + LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block); + size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; + LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, ""); + size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; + LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, ""); + + // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer(); + LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, ""); + LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), ""); + LLVMValueRef address_indices[] = { + LLVMConstNull(usize_type_ref), + modded_val, + }; + LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, addresses_field_ptr, address_indices, 2, ""); + LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, block_address, usize_type_ref, ""); + gen_store_untyped(g, address_value, address_slot, 0, false); + + // stack_trace.index += 1; + LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), ""); + gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false); + + LLVMBuildBr(g->builder, return_block); + LLVMPositionBuilderAtEnd(g->builder, return_block); + } if (handle_is_ptr(return_type)) { if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) { assert(g->cur_ret_ptr); @@ -2353,7 +2420,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } } if (last_arg_err_ret_stack) { - gen_param_values[gen_param_index] = LLVMGetUndef(g->ptr_to_stack_trace_type->type_ref); + gen_param_values[gen_param_index] = g->cur_err_ret_trace_val; gen_param_index += 1; } @@ -3482,7 +3549,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec } static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) { - gen_panic(g, ir_llvm_value(g, instruction->msg)); + gen_panic(g, ir_llvm_value(g, instruction->msg), nullptr); return nullptr; } @@ -4501,7 +4568,8 @@ static void do_code_gen(CodeGen *g) { LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); g->cur_fn = fn_table_entry; g->cur_fn_val = fn; - if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) { + TypeTableEntry *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; + if (handle_is_ptr(return_type)) { g->cur_ret_ptr = LLVMGetParam(fn, 0); } else { g->cur_ret_ptr = nullptr; @@ -4510,6 +4578,18 @@ static void do_code_gen(CodeGen *g) { build_all_basic_blocks(g, fn_table_entry); clear_debug_source_node(g); + if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) { + g->cur_err_ret_trace_val = LLVMGetParam(fn, LLVMCountParamTypes(fn_table_entry->type_entry->data.fn.raw_type_ref) - 1); + } else if (fn_table_entry->calls_errorable_function) { + g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type)); + size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; + LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, ""); + TypeTableEntry *usize = g->builtin_types.entry_usize; + gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false); + } else { + g->cur_err_ret_trace_val = nullptr; + } + // allocate temporary stack data for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) { IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i); @@ -5096,12 +5176,11 @@ static void define_builtin_compile_vars(CodeGen *g) { os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); Buf *contents = buf_alloc(); - buf_append_str(contents, + buf_appendf(contents, "pub const StackTrace = struct {\n" " index: usize,\n" - " instruction_addresses: [31]usize,\n" - "};\n\n" - ); + " instruction_addresses: [%" ZIG_PRI_usize "]usize,\n" + "};\n\n", stack_trace_ptr_count); const char *cur_os = nullptr; { @@ -5266,6 +5345,7 @@ static void define_builtin_compile_vars(CodeGen *g) { g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->compile_var_import = add_source_file(g, g->compile_var_package, abs_full_path, contents); + scan_import(g, g->compile_var_import); } static void init(CodeGen *g) { diff --git a/src/ir.cpp b/src/ir.cpp index 340e4fbeda..5dd608083f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10043,9 +10043,21 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; ir_add_alloca(ira, new_call_instruction, return_type); + if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) { + parent_fn_entry->calls_errorable_function = true; + } + return ir_finish_anal(ira, return_type); } + FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); + assert(fn_type_id->return_type != nullptr); + assert(parent_fn_entry != nullptr); + if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) { + parent_fn_entry->calls_errorable_function = true; + } + + IrInstruction **casted_args = allocate(call_param_count); size_t next_arg_index = 0; if (first_arg_ptr) { diff --git a/std/build.zig b/std/build.zig index cd6b3811ea..6984715466 100644 --- a/std/build.zig +++ b/std/build.zig @@ -721,11 +721,9 @@ pub const Builder = struct { return error.FileNotFound; } - pub fn exec(self: &Builder, argv: []const []const u8) -> []u8 { + pub fn exec(self: &Builder, argv: []const []const u8) -> %[]u8 { const max_output_size = 100 * 1024; - const result = os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size) catch |err| { - std.debug.panic("Unable to spawn {}: {}", argv[0], @errorName(err)); - }; + const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size); switch (result.term) { os.ChildProcess.Term.Exited => |code| { if (code != 0) { diff --git a/std/debug/index.zig b/std/debug/index.zig index 464974b7de..fa23040794 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -37,10 +37,16 @@ fn getStderrStream() -> %&io.OutStream { } } -/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. -pub fn dumpStackTrace() { +/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. +pub fn dumpCurrentStackTrace() { const stderr = getStderrStream() catch return; - writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return; + writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return; +} + +/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. +pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) { + const stderr = getStderrStream() catch return; + writeStackTrace(stack_trace, stderr, global_allocator, stderr_file.isTty()) catch return; } /// This function invokes undefined behavior when `ok` is `false`. @@ -88,7 +94,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn { const stderr = getStderrStream() catch os.abort(); stderr.print(format ++ "\n", args) catch os.abort(); - writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort(); + writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort(); os.abort(); } @@ -101,7 +107,103 @@ const RESET = "\x1b[0m"; error PathNotFound; error InvalidDebugInfo; -pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, +pub fn writeStackTrace(st_addrs: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool) -> %void { + switch (builtin.object_format) { + builtin.ObjectFormat.elf => { + var stack_trace = ElfStackTrace { + .self_exe_file = undefined, + .elf = undefined, + .debug_info = undefined, + .debug_abbrev = undefined, + .debug_str = undefined, + .debug_line = undefined, + .debug_ranges = null, + .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), + .compile_unit_list = ArrayList(CompileUnit).init(allocator), + }; + const st = &stack_trace; + st.self_exe_file = try os.openSelfExe(); + defer st.self_exe_file.close(); + + try st.elf.openFile(allocator, &st.self_exe_file); + defer st.elf.close(); + + st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; + st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; + st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; + st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; + st.debug_ranges = (try st.elf.findSection(".debug_ranges")); + try scanAllCompileUnits(st); + + var ignored_count: usize = 0; + + var frame_index: usize = undefined; + var frames_left: usize = undefined; + if (st_addrs.index < st_addrs.instruction_addresses.len) { + frame_index = 0; + frames_left = st_addrs.index; + } else { + frame_index = (st_addrs.index + 1) % st_addrs.instruction_addresses.len; + frames_left = st_addrs.instruction_addresses.len; + } + + while (frames_left != 0) : ({frames_left -= 1; frame_index = (frame_index + 1) % st_addrs.instruction_addresses.len;}) { + const return_address = st_addrs.instruction_addresses[frame_index]; + + // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal + // at compile time. I'll call it issue #313 + const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; + + const compile_unit = findCompileUnit(st, return_address) catch { + try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", + return_address); + continue; + }; + const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name); + if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| { + defer line_info.deinit(); + try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ + DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", + line_info.file_name, line_info.line, line_info.column, + return_address, compile_unit_name); + if (printLineFromFile(st.allocator(), out_stream, line_info)) { + if (line_info.column == 0) { + try out_stream.write("\n"); + } else { + {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { + try out_stream.writeByte(' '); + }} + try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); + } + } else |err| switch (err) { + error.EndOfFile, error.PathNotFound => {}, + else => return err, + } + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + try out_stream.print(ptr_hex ++ " in ??? ({})\n", + return_address, compile_unit_name); + }, + else => return err, + } + } + }, + builtin.ObjectFormat.coff => { + try out_stream.write("(stack trace unavailable for COFF object format)\n"); + }, + builtin.ObjectFormat.macho => { + try out_stream.write("(stack trace unavailable for Mach-O object format)\n"); + }, + builtin.ObjectFormat.wasm => { + try out_stream.write("(stack trace unavailable for WASM object format)\n"); + }, + builtin.ObjectFormat.unknown => { + try out_stream.write("(stack trace unavailable for unknown object format)\n"); + }, + } +} + +pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, ignore_frame_count: usize) -> %void { switch (builtin.object_format) { diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index 24d9c756e7..b293a1e64d 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -112,7 +112,7 @@ pub fn main() -> %void { } builder.setInstallPrefix(prefix); - root.build(&builder); + root.build(&builder) catch unreachable; if (builder.validateUserInputDidItFail()) return usageAndErr(&builder, true, try stderr_stream); @@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) // run the build script to collect the options if (!already_ran_build) { builder.setInstallPrefix(null); - root.build(builder); + root.build(builder) catch unreachable; } // This usage text has to be synchronized with src/main.cpp diff --git a/std/special/builtin.zig b/std/special/builtin.zig index e6c09863ca..dd77ba9c75 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -5,7 +5,7 @@ const builtin = @import("builtin"); // Avoid dragging in the debug safety mechanisms into this .o file, // unless we're trying to test this file. -pub coldcc fn panic(msg: []const u8) -> noreturn { +pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { if (builtin.is_test) { @import("std").debug.panic("{}", msg); } else { diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 5034a6fd90..96f34e56ff 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -74,7 +74,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; // Avoid dragging in the debug safety mechanisms into this .o file, // unless we're trying to test this file. -pub coldcc fn panic(msg: []const u8) -> noreturn { +pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { if (is_test) { @import("std").debug.panic("{}", msg); } else { diff --git a/std/special/panic.zig b/std/special/panic.zig index 03c2586739..690c4afa1c 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -4,14 +4,20 @@ // have to be added in the compiler. const builtin = @import("builtin"); +const std = @import("std"); -pub coldcc fn panic(msg: []const u8) -> noreturn { +pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { switch (builtin.os) { // TODO: fix panic in zen. builtin.Os.freestanding, builtin.Os.zen => { while (true) {} }, else => { + if (error_return_trace) |trace| { + std.debug.warn("{}\n", msg); + std.debug.dumpStackTrace(trace); + @import("std").debug.panic(""); + } @import("std").debug.panic("{}", msg); }, } From 4551489b924fad262eb3343b68fc4c0e18ed6a97 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 13 Jan 2018 01:00:50 -0500 Subject: [PATCH 03/12] typecheck the panic function --- src/analyze.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 61d86b5eb2..b52cb019d3 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2584,17 +2584,16 @@ 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))); } static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { - return; // TODO AstNode *proto_node = panic_fn->proto_node; assert(proto_node->type == NodeTypeFnProto); TypeTableEntry *fn_type = panic_fn->type_entry; 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); @@ -2603,6 +2602,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); From 971a6fc5317b0e2d3a1f611702a4892f4aace942 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 14 Jan 2018 10:19:21 -0500 Subject: [PATCH 04/12] fix duplicate stack trace code --- std/debug/index.zig | 266 +++++++++++++++--------------------- std/special/test_runner.zig | 12 +- 2 files changed, 119 insertions(+), 159 deletions(-) diff --git a/std/debug/index.zig b/std/debug/index.zig index fa23040794..f7f6ffa323 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. @@ -40,13 +44,29 @@ fn getStderrStream() -> %&io.OutStream { /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. pub fn dumpCurrentStackTrace() { const stderr = getStderrStream() catch return; - writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return; + 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(stack_trace: &builtin.StackTrace) { const stderr = getStderrStream() catch return; - writeStackTrace(stack_trace, stderr, global_allocator, stderr_file.isTty()) 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`. @@ -94,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(); - writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort(); + dumpCurrentStackTrace(); os.abort(); } @@ -107,108 +127,88 @@ const RESET = "\x1b[0m"; error PathNotFound; error InvalidDebugInfo; -pub fn writeStackTrace(st_addrs: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool) -> %void { - switch (builtin.object_format) { - builtin.ObjectFormat.elf => { - var stack_trace = ElfStackTrace { - .self_exe_file = undefined, - .elf = undefined, - .debug_info = undefined, - .debug_abbrev = undefined, - .debug_str = undefined, - .debug_line = undefined, - .debug_ranges = null, - .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), - .compile_unit_list = ArrayList(CompileUnit).init(allocator), - }; - const st = &stack_trace; - st.self_exe_file = try os.openSelfExe(); - defer st.self_exe_file.close(); +pub fn writeStackTrace(stack_trace: &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; + } - try st.elf.openFile(allocator, &st.self_exe_file); - defer st.elf.close(); - - st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; - st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; - st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; - st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; - st.debug_ranges = (try st.elf.findSection(".debug_ranges")); - try scanAllCompileUnits(st); - - var ignored_count: usize = 0; - - var frame_index: usize = undefined; - var frames_left: usize = undefined; - if (st_addrs.index < st_addrs.instruction_addresses.len) { - frame_index = 0; - frames_left = st_addrs.index; - } else { - frame_index = (st_addrs.index + 1) % st_addrs.instruction_addresses.len; - frames_left = st_addrs.instruction_addresses.len; - } - - while (frames_left != 0) : ({frames_left -= 1; frame_index = (frame_index + 1) % st_addrs.instruction_addresses.len;}) { - const return_address = st_addrs.instruction_addresses[frame_index]; - - // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal - // at compile time. I'll call it issue #313 - const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; - - const compile_unit = findCompileUnit(st, return_address) catch { - try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", - return_address); - continue; - }; - const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name); - if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| { - defer line_info.deinit(); - try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ - DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", - line_info.file_name, line_info.line, line_info.column, - return_address, compile_unit_name); - if (printLineFromFile(st.allocator(), out_stream, line_info)) { - if (line_info.column == 0) { - try out_stream.write("\n"); - } else { - {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { - try out_stream.writeByte(' '); - }} - try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); - } - } else |err| switch (err) { - error.EndOfFile, error.PathNotFound => {}, - else => return err, - } - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - try out_stream.print(ptr_hex ++ " in ??? ({})\n", - return_address, compile_unit_name); - }, - else => return err, - } - } - }, - builtin.ObjectFormat.coff => { - try out_stream.write("(stack trace unavailable for COFF object format)\n"); - }, - builtin.ObjectFormat.macho => { - try out_stream.write("(stack trace unavailable for Mach-O object format)\n"); - }, - builtin.ObjectFormat.wasm => { - try out_stream.write("(stack trace unavailable for WASM object format)\n"); - }, - builtin.ObjectFormat.unknown => { - try out_stream.write("(stack trace unavailable for unknown object format)\n"); - }, + 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, tty_color: bool, - ignore_frame_count: usize) -> %void +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 { + // 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, usize(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, @@ -219,12 +219,11 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat .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; @@ -232,67 +231,19 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat 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; }, } } @@ -330,7 +281,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, @@ -350,6 +301,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/special/test_runner.zig b/std/special/test_runner.zig index 1a36f50b62..027251c52c 100644 --- a/std/special/test_runner.zig +++ b/std/special/test_runner.zig @@ -8,10 +8,14 @@ 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; - }; + if (builtin.is_test) { + test_fn.func() catch unreachable; + } else { + test_fn.func() catch |err| { + warn("{}\n", err); + return err; + }; + } warn("OK\n"); } From fa024f80924d35e4aac4b4edcac88193def03175 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 14 Jan 2018 14:35:43 -0500 Subject: [PATCH 05/12] error return trace pointer prefixes other params instead of being last. This increases the chances that it can remain in the same register between calls. --- src/analyze.cpp | 18 +++++++++--------- src/codegen.cpp | 34 +++++++++++++++++++++++----------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index b52cb019d3..f7c0f832a2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -925,7 +925,7 @@ 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 last_arg_error_return_trace = fn_type_id->return_type->id == TypeTableEntryIdErrorUnion || + bool prefix_arg_error_return_trace = fn_type_id->return_type->id == TypeTableEntryIdErrorUnion || fn_type_id->return_type->id == TypeTableEntryIdPureError; // +1 for maybe making the first argument the return value // +1 for maybe last argument the error return trace @@ -951,6 +951,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]; @@ -980,14 +988,6 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { } } - if (last_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_count = gen_param_index; fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, diff --git a/src/codegen.cpp b/src/codegen.cpp index 97cab9a69f..8b2cf2bcd4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -406,6 +406,16 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) { zig_unreachable(); } +static uint32_t get_err_ret_trace_arg_index(FnTableEntry *fn_table_entry) { + 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; @@ -563,9 +573,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval"); } } - if (return_type->id == TypeTableEntryIdErrorUnion || return_type->id == TypeTableEntryIdPureError) { - unsigned gen_index = LLVMCountParamTypes(fn_llvm_type) - 1; - addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull"); + + uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(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; @@ -2400,8 +2411,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); - bool last_arg_err_ret_stack = src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError; - size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (last_arg_err_ret_stack ? 1 : 0); + bool prefix_arg_err_ret_stack = 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; @@ -2409,6 +2420,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; @@ -2419,10 +2434,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr gen_param_index += 1; } } - if (last_arg_err_ret_stack) { - gen_param_values[gen_param_index] = g->cur_err_ret_trace_val; - gen_param_index += 1; - } ZigLLVM_FnInline fn_inline; switch (instruction->fn_inline) { @@ -4578,8 +4589,9 @@ static void do_code_gen(CodeGen *g) { build_all_basic_blocks(g, fn_table_entry); clear_debug_source_node(g); - if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) { - g->cur_err_ret_trace_val = LLVMGetParam(fn, LLVMCountParamTypes(fn_table_entry->type_entry->data.fn.raw_type_ref) - 1); + uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(fn_table_entry); + if (err_ret_trace_arg_index != UINT32_MAX) { + g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index); } else if (fn_table_entry->calls_errorable_function) { 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; From 793f031c4ca7fdd230ef262895acf3e454be49dd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 14 Jan 2018 15:17:07 -0500 Subject: [PATCH 06/12] remove 32-bit windows from supported targets list we still want to support it, but there are too many bugs to claim that we support it right now. See #537 --- README.md | 2 +- src-self-hosted/parser.zig | 6 ------ std/crypto/sha2.zig | 40 -------------------------------------- std/io_test.zig | 5 ----- std/math/acosh.zig | 5 ----- std/math/cos.zig | 5 ----- std/math/cosh.zig | 5 ----- std/math/index.zig | 15 -------------- std/math/ln.zig | 5 ----- std/math/log.zig | 5 ----- std/math/log10.zig | 5 ----- std/math/log2.zig | 5 ----- std/math/pow.zig | 6 ------ std/math/round.zig | 5 ----- std/math/sin.zig | 5 ----- std/math/sinh.zig | 5 ----- std/math/tan.zig | 5 ----- std/math/tanh.zig | 5 ----- std/rand.zig | 20 ------------------- std/sort.zig | 20 ------------------- test/tests.zig | 5 ----- 21 files changed, 1 insertion(+), 178 deletions(-) diff --git a/README.md b/README.md index 084d62a24e..6a9fa4e580 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ that counts as "freestanding" for the purposes of this table. | | freestanding | linux | macosx | windows | other | |-------------|--------------|---------|---------|---------|---------| -|i386 | OK | planned | OK | OK | planned | +|i386 | OK | planned | OK | planned | planned | |x86_64 | OK | OK | OK | OK | planned | |arm | OK | planned | planned | N/A | planned | |aarch64 | OK | planned | planned | planned | planned | diff --git a/src-self-hosted/parser.zig b/src-self-hosted/parser.zig index b4bf9a1377..88b6dd9bb0 100644 --- a/src-self-hosted/parser.zig +++ b/src-self-hosted/parser.zig @@ -1146,12 +1146,6 @@ fn testCanonical(source: []const u8) { } test "zig fmt" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } - testCanonical( \\extern fn puts(s: &const u8) -> c_int; \\ diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig index cfe4640fe7..8eba240155 100644 --- a/std/crypto/sha2.zig +++ b/std/crypto/sha2.zig @@ -270,22 +270,12 @@ fn Sha2_32(comptime params: Sha2Params32) -> type { return struct { };} test "sha224 single" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // https://github.com/zig-lang/zig/issues/537 - return; - } - debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == Sha224.hash("")); debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == Sha224.hash("abc")); debug.assert(0xc97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3 == Sha224.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")); } test "sha224 streaming" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // https://github.com/zig-lang/zig/issues/537 - return; - } - var h = Sha224.init(); debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == h.final()); @@ -302,22 +292,12 @@ test "sha224 streaming" { } test "sha256 single" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // https://github.com/zig-lang/zig/issues/537 - return; - } - debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == Sha256.hash("")); debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == Sha256.hash("abc")); debug.assert(0xcf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1 == Sha256.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")); } test "sha256 streaming" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // https://github.com/zig-lang/zig/issues/537 - return; - } - var h = Sha256.init(); debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == h.final()); @@ -621,11 +601,6 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct { };} test "sha384 single" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // https://github.com/zig-lang/zig/issues/537 - return; - } - const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b; debug.assert(h1 == Sha384.hash("")); @@ -637,11 +612,6 @@ test "sha384 single" { } test "sha384 streaming" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // https://github.com/zig-lang/zig/issues/537 - return; - } - var h = Sha384.init(); const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b; @@ -661,11 +631,6 @@ test "sha384 streaming" { } test "sha512 single" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // https://github.com/zig-lang/zig/issues/537 - return; - } - const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e; debug.assert(h1 == Sha512.hash("")); @@ -677,11 +642,6 @@ test "sha512 single" { } test "sha512 streaming" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // https://github.com/zig-lang/zig/issues/537 - return; - } - var h = Sha512.init(); const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e; diff --git a/std/io_test.zig b/std/io_test.zig index 3b1f646616..1767a546ea 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -8,11 +8,6 @@ const os = std.os; const builtin = @import("builtin"); test "write a file, read it, then delete it" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } var data: [1024]u8 = undefined; var rng = Rand.init(1234); rng.fillBytes(data[0..]); diff --git a/std/math/acosh.zig b/std/math/acosh.zig index 150bcd4543..91094302dd 100644 --- a/std/math/acosh.zig +++ b/std/math/acosh.zig @@ -55,11 +55,6 @@ fn acosh64(x: f64) -> f64 { } test "math.acosh" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } assert(acosh(f32(1.5)) == acosh32(1.5)); assert(acosh(f64(1.5)) == acosh64(1.5)); } diff --git a/std/math/cos.zig b/std/math/cos.zig index a65ea1ace8..88a0193db1 100644 --- a/std/math/cos.zig +++ b/std/math/cos.zig @@ -146,11 +146,6 @@ test "math.cos" { } test "math.cos32" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } const epsilon = 0.000001; assert(math.approxEq(f32, cos32(0.0), 1.0, epsilon)); diff --git a/std/math/cosh.zig b/std/math/cosh.zig index 05565d6b3a..6e57e85d14 100644 --- a/std/math/cosh.zig +++ b/std/math/cosh.zig @@ -81,11 +81,6 @@ fn cosh64(x: f64) -> f64 { } test "math.cosh" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } assert(cosh(f32(1.5)) == cosh32(1.5)); assert(cosh(f64(1.5)) == cosh64(1.5)); } diff --git a/std/math/index.zig b/std/math/index.zig index d925740030..07a4d9a731 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -359,11 +359,6 @@ pub fn divTrunc(comptime T: type, numerator: T, denominator: T) -> %T { } test "math.divTrunc" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } testDivTrunc(); comptime testDivTrunc(); } @@ -389,11 +384,6 @@ pub fn divFloor(comptime T: type, numerator: T, denominator: T) -> %T { } test "math.divFloor" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } testDivFloor(); comptime testDivFloor(); } @@ -423,11 +413,6 @@ pub fn divExact(comptime T: type, numerator: T, denominator: T) -> %T { } test "math.divExact" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } testDivExact(); comptime testDivExact(); } diff --git a/std/math/ln.zig b/std/math/ln.zig index c5a5c93842..57e2ffdec8 100644 --- a/std/math/ln.zig +++ b/std/math/ln.zig @@ -147,11 +147,6 @@ pub fn ln_64(x_: f64) -> f64 { } test "math.ln" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } assert(ln(f32(0.2)) == ln_32(0.2)); assert(ln(f64(0.2)) == ln_64(0.2)); } diff --git a/std/math/log.zig b/std/math/log.zig index 4edb77bb1a..6bed4c0da1 100644 --- a/std/math/log.zig +++ b/std/math/log.zig @@ -56,11 +56,6 @@ test "math.log float" { } test "math.log float_special" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } assert(log(f32, 2, 0.2301974) == math.log2(f32(0.2301974))); assert(log(f32, 10, 0.2301974) == math.log10(f32(0.2301974))); diff --git a/std/math/log10.zig b/std/math/log10.zig index 0b6fc31aa7..e27d3eb272 100644 --- a/std/math/log10.zig +++ b/std/math/log10.zig @@ -172,11 +172,6 @@ pub fn log10_64(x_: f64) -> f64 { } test "math.log10" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } assert(log10(f32(0.2)) == log10_32(0.2)); assert(log10(f64(0.2)) == log10_64(0.2)); } diff --git a/std/math/log2.zig b/std/math/log2.zig index a9789e47cf..7839759974 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -170,11 +170,6 @@ pub fn log2_64(x_: f64) -> f64 { } test "math.log2" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } assert(log2(f32(0.2)) == log2_32(0.2)); assert(log2(f64(0.2)) == log2_64(0.2)); } diff --git a/std/math/pow.zig b/std/math/pow.zig index b21fb3a921..c0157ff16e 100644 --- a/std/math/pow.zig +++ b/std/math/pow.zig @@ -176,12 +176,6 @@ fn isOddInteger(x: f64) -> bool { } test "math.pow" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } - const epsilon = 0.000001; assert(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon)); diff --git a/std/math/round.zig b/std/math/round.zig index 3abee040ef..06b9e41f58 100644 --- a/std/math/round.zig +++ b/std/math/round.zig @@ -98,11 +98,6 @@ test "math.round" { } test "math.round32" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } assert(round32(1.3) == 1.0); assert(round32(-1.3) == -1.0); assert(round32(0.2) == 0.0); diff --git a/std/math/sin.zig b/std/math/sin.zig index 99008af469..5b1090e99e 100644 --- a/std/math/sin.zig +++ b/std/math/sin.zig @@ -150,11 +150,6 @@ test "math.sin" { } test "math.sin32" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } const epsilon = 0.000001; assert(math.approxEq(f32, sin32(0.0), 0.0, epsilon)); diff --git a/std/math/sinh.zig b/std/math/sinh.zig index ca06bc615a..b0057f897b 100644 --- a/std/math/sinh.zig +++ b/std/math/sinh.zig @@ -88,11 +88,6 @@ fn sinh64(x: f64) -> f64 { } test "math.sinh" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } assert(sinh(f32(1.5)) == sinh32(1.5)); assert(sinh(f64(1.5)) == sinh64(1.5)); } diff --git a/std/math/tan.zig b/std/math/tan.zig index e62e9b8899..9afe102d64 100644 --- a/std/math/tan.zig +++ b/std/math/tan.zig @@ -136,11 +136,6 @@ test "math.tan" { } test "math.tan32" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } const epsilon = 0.000001; assert(math.approxEq(f32, tan32(0.0), 0.0, epsilon)); diff --git a/std/math/tanh.zig b/std/math/tanh.zig index 813b4f4561..8560538cdf 100644 --- a/std/math/tanh.zig +++ b/std/math/tanh.zig @@ -112,11 +112,6 @@ fn tanh64(x: f64) -> f64 { } test "math.tanh" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } assert(tanh(f32(1.5)) == tanh32(1.5)); assert(tanh(f64(1.5)) == tanh64(1.5)); } diff --git a/std/rand.zig b/std/rand.zig index 09109cfbf7..5f4c9c2015 100644 --- a/std/rand.zig +++ b/std/rand.zig @@ -194,11 +194,6 @@ fn MersenneTwister( } test "rand float 32" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } var r = Rand.init(42); var i: usize = 0; while (i < 1000) : (i += 1) { @@ -209,11 +204,6 @@ test "rand float 32" { } test "rand.MT19937_64" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } var rng = MT19937_64.init(rand_test.mt64_seed); for (rand_test.mt64_data) |value| { assert(value == rng.get()); @@ -221,11 +211,6 @@ test "rand.MT19937_64" { } test "rand.MT19937_32" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } var rng = MT19937_32.init(rand_test.mt32_seed); for (rand_test.mt32_data) |value| { assert(value == rng.get()); @@ -233,11 +218,6 @@ test "rand.MT19937_32" { } test "rand.Rand.range" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } var r = Rand.init(42); testRange(&r, -4, 3); testRange(&r, -4, -1); diff --git a/std/sort.zig b/std/sort.zig index 02cb9acb2e..8554ab495a 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -1020,11 +1020,6 @@ fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) -> bool { } test "std.sort" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } const u8cases = [][]const []const u8 { [][]const u8{"", ""}, [][]const u8{"a", "a"}, @@ -1061,11 +1056,6 @@ test "std.sort" { } test "std.sort descending" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } const rev_cases = [][]const []const i32 { [][]const i32{[]i32{}, []i32{}}, [][]const i32{[]i32{1}, []i32{1}}, @@ -1085,11 +1075,6 @@ test "std.sort descending" { } test "another sort case" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } var arr = []i32{ 5, 3, 1, 2, 4 }; sort(i32, arr[0..], i32asc); @@ -1097,11 +1082,6 @@ test "another sort case" { } test "sort fuzz testing" { - if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { - // TODO get this test passing - // https://github.com/zig-lang/zig/issues/537 - return; - } var rng = std.rand.Rand.init(0x12345678); const test_case_count = 10; var i: usize = 0; diff --git a/test/tests.zig b/test/tests.zig index 20e1e94459..4969379a6d 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -42,11 +42,6 @@ const test_targets = []TestTarget { .arch = builtin.Arch.x86_64, .environ = builtin.Environ.msvc, }, - TestTarget { - .os = builtin.Os.windows, - .arch = builtin.Arch.i386, - .environ = builtin.Environ.msvc, - }, }; error TestFailed; From f0df2cdde9181f954dc124dab004663e56c86d0f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 14 Jan 2018 16:26:06 -0500 Subject: [PATCH 07/12] error return traces use a zig-provided function to save binary size --- src/all_types.hpp | 2 + src/codegen.cpp | 110 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 86 insertions(+), 26 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 6476c08625..7a3f1a9b79 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1574,6 +1574,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; diff --git a/src/codegen.cpp b/src/codegen.cpp index 8b2cf2bcd4..e346225742 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -892,7 +892,8 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace msg_arg, stack_trace_arg, }; - ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, ""); + LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, ""); + LLVMSetTailCall(call_instruction, true); LLVMBuildUnreachable(g->builder); } @@ -919,6 +920,79 @@ 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_ptr_count] = 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, ""); + + LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, ""); + LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), ""); + LLVMValueRef address_indices[] = { + LLVMConstNull(usize_type_ref), + modded_val, + }; + LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, addresses_field_ptr, address_indices, 2, ""); + + LLVMValueRef 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; @@ -1053,8 +1127,9 @@ static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) { err_val, err_ret_trace_val, }; - ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified), + 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); } @@ -1346,36 +1421,19 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns is_err_return = true; } if (is_err_return) { - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; - - // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer(); - // stack_trace.index += 1; - LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError"); - LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block); - size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; - LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, ""); - size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; - LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, ""); - // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer(); - LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, ""); - LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), ""); - LLVMValueRef address_indices[] = { - LLVMConstNull(usize_type_ref), - modded_val, + LLVMValueRef return_err_fn = get_return_err_fn(g); + LLVMValueRef args[] = { + g->cur_err_ret_trace_val, + block_address, }; - LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, addresses_field_ptr, address_indices, 2, ""); - LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, block_address, usize_type_ref, ""); - gen_store_untyped(g, address_value, address_slot, 0, false); - - // stack_trace.index += 1; - LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), ""); - gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false); - LLVMBuildBr(g->builder, return_block); LLVMPositionBuilderAtEnd(g->builder, return_block); + 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)) { From d973b40884be1c7874805c81981cac7edca5605b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 14 Jan 2018 19:40:02 -0500 Subject: [PATCH 08/12] stack traces are a variable number of frames --- src/codegen.cpp | 57 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index e346225742..96c0879615 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -22,8 +22,6 @@ #include #include -static const size_t stack_trace_ptr_count = 31; - static void init_darwin_native(CodeGen *g) { char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET"); char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET"); @@ -958,7 +956,7 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; - // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = return_address; + // 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; @@ -966,13 +964,21 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { 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, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), ""); + LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, len_value, ""); LLVMValueRef address_indices[] = { - LLVMConstNull(usize_type_ref), modded_val, }; - LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, addresses_field_ptr, address_indices, 2, ""); + + 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, ""); @@ -1052,8 +1058,8 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false); LLVMTypeRef arg_types[] = { - g->err_tag_type->type_ref, 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); @@ -1077,7 +1083,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), @@ -1107,7 +1113,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, LLVMGetParam(fn_val, 1)); + gen_panic(g, global_slice, LLVMGetParam(fn_val, 0)); LLVMPositionBuilderAtEnd(g->builder, prev_block); LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); @@ -1124,8 +1130,8 @@ static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) { err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref); } LLVMValueRef args[] = { - err_val, 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, ""); @@ -4651,11 +4657,34 @@ static void do_code_gen(CodeGen *g) { if (err_ret_trace_arg_index != UINT32_MAX) { g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index); } else if (fn_table_entry->calls_errorable_function) { + // 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, ""); - TypeTableEntry *usize = g->builtin_types.entry_usize; 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; } @@ -5246,11 +5275,11 @@ static void define_builtin_compile_vars(CodeGen *g) { os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); Buf *contents = buf_alloc(); - buf_appendf(contents, + buf_append_str(contents, "pub const StackTrace = struct {\n" " index: usize,\n" - " instruction_addresses: [%" ZIG_PRI_usize "]usize,\n" - "};\n\n", stack_trace_ptr_count); + " instruction_addresses: []usize,\n" + "};\n\n"); const char *cur_os = nullptr; { From 7b57454cc11371b71097967656e19f0a1736d733 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 15 Jan 2018 00:01:02 -0500 Subject: [PATCH 09/12] clean up error return tracing * error return tracing is disabled in release-fast mode * add @errorReturnTrace * zig build API changes build return type from `void` to `%void` * allow `void`, `noreturn`, and `u8` from main. closes #535 --- doc/langref.html.in | 8 +++ example/mix_o_files/build.zig | 2 +- example/shared_library/build.zig | 2 +- src/all_types.hpp | 7 +++ src/analyze.cpp | 12 ++--- src/codegen.cpp | 76 ++++++++++++++++++---------- src/ir.cpp | 35 +++++++++++++ src/ir_print.cpp | 7 +++ std/build.zig | 4 +- std/debug/index.zig | 9 ++-- std/io.zig | 2 +- std/os/index.zig | 11 +--- std/special/bootstrap.zig | 45 ++++++++++++---- std/special/build_runner.zig | 8 +-- std/special/test_runner.zig | 9 +--- test/compile_errors.zig | 18 +++---- test/debug_safety.zig | 40 +++++++-------- test/standalone/issue_339/build.zig | 2 +- test/standalone/issue_339/test.zig | 3 +- test/standalone/pkg_import/build.zig | 2 +- test/standalone/use_alias/build.zig | 2 +- test/tests.zig | 4 +- 22 files changed, 197 insertions(+), 111 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 2476790105..94bc780959 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -142,6 +142,7 @@
  • @TagType
  • @EnumTagType
  • @errorName
  • +
  • @errorReturnTrace
  • @fence
  • @fieldParentPtr
  • @frameAddress
  • @@ -4412,6 +4413,13 @@ test.zig:6:2: error: found compile log statement or all calls have a compile-time known value for err, then no error name table will be generated.

    +

    @errorReturnTrace

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

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

    @fence

    @fence(order: AtomicOrder)

    diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig index 391af9924a..4bba69b091 100644 --- a/example/mix_o_files/build.zig +++ b/example/mix_o_files/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { const obj = b.addObject("base64", "base64.zig"); const exe = b.addCExecutable("test"); diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig index 9b7f3793c6..147b54401c 100644 --- a/example/shared_library/build.zig +++ b/example/shared_library/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); const exe = b.addCExecutable("test"); diff --git a/src/all_types.hpp b/src/all_types.hpp index 7a3f1a9b79..b401097647 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1274,6 +1274,7 @@ enum BuiltinFnId { BuiltinFnIdSetAlignStack, BuiltinFnIdArgType, BuiltinFnIdExport, + BuiltinFnIdErrorReturnTrace, }; struct BuiltinFnEntry { @@ -1499,6 +1500,7 @@ struct CodeGen { Buf triple_str; BuildMode build_mode; bool is_test_build; + bool have_err_ret_tracing; uint32_t target_os_index; uint32_t target_arch_index; uint32_t target_environ_index; @@ -1902,6 +1904,7 @@ enum IrInstructionId { IrInstructionIdSetAlignStack, IrInstructionIdArgType, IrInstructionIdExport, + IrInstructionIdErrorReturnTrace, }; struct IrInstruction { @@ -2723,6 +2726,10 @@ struct IrInstructionExport { IrInstruction *target; }; +struct IrInstructionErrorReturnTrace { + IrInstruction base; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index f7c0f832a2..8028ace289 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -925,8 +925,9 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { if (!skip_debug_info) { bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) && handle_is_ptr(fn_type_id->return_type); - bool prefix_arg_error_return_trace = fn_type_id->return_type->id == TypeTableEntryIdErrorUnion || - fn_type_id->return_type->id == TypeTableEntryIdPureError; + bool prefix_arg_error_return_trace = g->have_err_ret_tracing && + (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion || + fn_type_id->return_type->id == TypeTableEntryIdPureError); // +1 for maybe making the first argument the return value // +1 for maybe last argument the error return trace LLVMTypeRef *gen_param_types = allocate(2 + fn_type_id->param_count); @@ -2711,13 +2712,6 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { { if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) { g->main_fn = fn_table_entry; - TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void); - TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; - if (actual_return_type != err_void) { - add_node_error(g, fn_proto->return_type, - buf_sprintf("expected return type of main to be '%%void', instead is '%s'", - buf_ptr(&actual_return_type->name))); - } } else if ((import->package == g->panic_package || g->have_pub_panic) && buf_eql_str(&fn_table_entry->symbol_name, "panic")) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 96c0879615..5113aa7275 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -404,7 +404,10 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) { zig_unreachable(); } -static uint32_t get_err_ret_trace_arg_index(FnTableEntry *fn_table_entry) { +static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_entry) { + if (!g->have_err_ret_tracing) { + return UINT32_MAX; + } TypeTableEntry *fn_type = fn_table_entry->type_entry; TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) { @@ -572,7 +575,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { } } - uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(fn_table_entry); + uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); if (err_ret_trace_arg_index != UINT32_MAX) { addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); } @@ -1415,31 +1418,33 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns LLVMValueRef value = ir_llvm_value(g, return_instruction->value); TypeTableEntry *return_type = return_instruction->value->value.type; - bool is_err_return = false; - if (return_type->id == TypeTableEntryIdErrorUnion) { - if (return_instruction->value->value.special == ConstValSpecialStatic) { - is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr; - } else if (return_instruction->value->value.special == ConstValSpecialRuntime) { - is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError; - // TODO: emit a branch to check if the return value is an error + if (g->have_err_ret_tracing) { + bool is_err_return = false; + if (return_type->id == TypeTableEntryIdErrorUnion) { + if (return_instruction->value->value.special == ConstValSpecialStatic) { + is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr; + } else if (return_instruction->value->value.special == ConstValSpecialRuntime) { + is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError; + // TODO: emit a branch to check if the return value is an error + } + } else if (return_type->id == TypeTableEntryIdPureError) { + is_err_return = true; } - } 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); + 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); + LLVMValueRef return_err_fn = get_return_err_fn(g); + LLVMValueRef args[] = { + g->cur_err_ret_trace_val, + block_address, + }; + LLVMBuildBr(g->builder, return_block); + LLVMPositionBuilderAtEnd(g->builder, return_block); + LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 2, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); + LLVMSetTailCall(call_instruction, true); + } } if (handle_is_ptr(return_type)) { if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) { @@ -2475,7 +2480,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr TypeTableEntry *src_return_type = fn_type_id->return_type; bool ret_has_bits = type_has_bits(src_return_type); bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type); - bool prefix_arg_err_ret_stack = src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError; + bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError); size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0); bool is_var_args = fn_type_id->is_var_args; LLVMValueRef *gen_param_values = allocate(actual_param_count); @@ -3031,6 +3036,16 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I return target_val; } +static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable, + IrInstructionErrorReturnTrace *instruction) +{ + TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); + if (g->cur_err_ret_trace_val == nullptr) { + return LLVMConstNull(ptr_to_stack_trace_type->type_ref); + } + return g->cur_err_ret_trace_val; +} + static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { switch (atomic_order) { case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered; @@ -3804,6 +3819,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction); case IrInstructionIdAlignCast: return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction); + case IrInstructionIdErrorReturnTrace: + return ir_render_error_return_trace(g, executable, (IrInstructionErrorReturnTrace *)instruction); } zig_unreachable(); } @@ -4653,10 +4670,10 @@ static void do_code_gen(CodeGen *g) { build_all_basic_blocks(g, fn_table_entry); clear_debug_source_node(g); - uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(fn_table_entry); + uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); if (err_ret_trace_arg_index != UINT32_MAX) { g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index); - } else if (fn_table_entry->calls_errorable_function) { + } else if (g->have_err_ret_tracing && fn_table_entry->calls_errorable_function) { // TODO call graph analysis to find out what this number needs to be for every function static const size_t stack_trace_ptr_count = 30; @@ -5251,6 +5268,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1); create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2); create_builtin_fn(g, BuiltinFnIdExport, "export", 3); + create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); } static const char *bool_to_str(bool b) { @@ -5553,6 +5571,8 @@ static void init(CodeGen *g) { } } + g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease; + define_builtin_fns(g); define_builtin_compile_vars(g); } diff --git a/src/ir.cpp b/src/ir.cpp index 5dd608083f..d6844f32ba 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -572,6 +572,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) { return IrInstructionIdArgType; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) { + return IrInstructionIdErrorReturnTrace; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2305,6 +2309,12 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } +static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionErrorReturnTrace *instruction = ir_build_instruction(irb, scope, source_node); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3731,6 +3741,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); } + case BuiltinFnIdErrorReturnTrace: + { + return ir_build_error_return_trace(irb, scope, node); + } } zig_unreachable(); } @@ -9568,6 +9582,24 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi return ira->codegen->builtin_types.entry_void; } +static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, + IrInstructionErrorReturnTrace *instruction) +{ + FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); + TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); + TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); + if (fn_entry == nullptr || !fn_entry->calls_errorable_function || !ira->codegen->have_err_ret_tracing) { + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_maybe = nullptr; + return nullable_type; + } + + IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, + instruction->base.source_node); + ir_link_new_instruction(new_instruction, &instruction->base); + return nullable_type; +} + static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i) { @@ -15324,6 +15356,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction); case IrInstructionIdExport: return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction); + case IrInstructionIdErrorReturnTrace: + return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction); } zig_unreachable(); } @@ -15507,6 +15541,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdOpaqueType: case IrInstructionIdArgType: case IrInstructionIdTagType: + case IrInstructionIdErrorReturnTrace: return false; case IrInstructionIdAsm: { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index f5aba2a45d..930d22f21a 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -996,6 +996,10 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) { } } +static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { + fprintf(irp->f, "@errorReturnTrace()"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); @@ -1308,6 +1312,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdExport: ir_print_export(irp, (IrInstructionExport *)instruction); break; + case IrInstructionIdErrorReturnTrace: + ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/std/build.zig b/std/build.zig index 6984715466..5d79b00c4f 100644 --- a/std/build.zig +++ b/std/build.zig @@ -247,11 +247,11 @@ pub const Builder = struct { defer wanted_steps.deinit(); if (step_names.len == 0) { - wanted_steps.append(&self.default_step) catch unreachable; + try wanted_steps.append(&self.default_step); } else { for (step_names) |step_name| { const s = try self.getTopLevelStepByName(step_name); - wanted_steps.append(s) catch unreachable; + try wanted_steps.append(s); } } diff --git a/std/debug/index.zig b/std/debug/index.zig index f7f6ffa323..5dc7b0dbfa 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -56,7 +56,7 @@ pub fn dumpCurrentStackTrace() { } /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. -pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) { +pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) { const stderr = getStderrStream() catch return; const debug_info = openSelfDebugInfo(global_allocator) catch |err| { stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; @@ -127,7 +127,7 @@ const RESET = "\x1b[0m"; error PathNotFound; error InvalidDebugInfo; -pub fn writeStackTrace(stack_trace: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, +pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool) -> %void { var frame_index: usize = undefined; @@ -167,6 +167,9 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat } fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void { + if (builtin.os == builtin.Os.windows) { + return error.UnsupportedDebugInfo; + } // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal // at compile time. I'll call it issue #313 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; @@ -177,7 +180,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a return; }; const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); - if (getLineNumberInfo(debug_info, compile_unit, usize(address) - 1)) |line_info| { + if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { defer line_info.deinit(); try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", diff --git a/std/io.zig b/std/io.zig index c6fd5502a2..605553b0ea 100644 --- a/std/io.zig +++ b/std/io.zig @@ -224,7 +224,7 @@ pub const File = struct { }; } }, - else => @compileError("unsupported OS"), + else => @compileError("unsupported OS: " ++ @tagName(builtin.os)), } } diff --git a/std/os/index.zig b/std/os/index.zig index e338359522..fd8eb84ab4 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -148,7 +148,7 @@ pub coldcc fn abort() -> noreturn { } /// Exits the program cleanly with the specified status code. -pub coldcc fn exit(status: i32) -> noreturn { +pub coldcc fn exit(status: u8) -> noreturn { if (builtin.link_libc) { c.exit(status); } @@ -157,14 +157,7 @@ pub coldcc fn exit(status: i32) -> noreturn { posix.exit(status); }, Os.windows => { - // Map a possibly negative status code to a non-negative status for the systems default - // integer width. - const p_status = if (@sizeOf(c_uint) < @sizeOf(u32)) - @truncate(c_uint, @bitCast(u32, status)) - else - c_uint(@bitCast(u32, status)); - - windows.ExitProcess(p_status); + windows.ExitProcess(status); }, else => @compileError("Unsupported OS"), } diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 55d28a2d0e..77bb7316a9 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -21,8 +21,7 @@ comptime { } extern fn zenMain() -> noreturn { - root.main() catch std.os.posix.exit(1); - std.os.posix.exit(0); + std.os.posix.exit(callMain()); } nakedcc fn _start() -> noreturn { @@ -43,29 +42,55 @@ nakedcc fn _start() -> noreturn { extern fn WinMainCRTStartup() -> noreturn { @setAlignStack(16); - root.main() catch std.os.windows.ExitProcess(1); - std.os.windows.ExitProcess(0); + std.os.windows.ExitProcess(callMain()); } fn posixCallMainAndExit() -> noreturn { const argc = *argc_ptr; const argv = @ptrCast(&&u8, &argc_ptr[1]); const envp = @ptrCast(&?&u8, &argv[argc + 1]); - callMain(argc, argv, envp) catch std.os.posix.exit(1); - std.os.posix.exit(0); + std.os.posix.exit(callMainWithArgs(argc, argv, envp)); } -fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void { +fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) -> u8 { std.os.ArgIteratorPosix.raw = argv[0..argc]; var env_count: usize = 0; while (envp[env_count] != null) : (env_count += 1) {} std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count]; - return root.main(); + return callMain(); } extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 { - callMain(usize(c_argc), c_argv, c_envp) catch return 1; - return 0; + return callMainWithArgs(usize(c_argc), c_argv, c_envp); +} + +fn callMain() -> u8 { + switch (@typeId(@typeOf(root.main).ReturnType)) { + builtin.TypeId.NoReturn => { + root.main(); + }, + builtin.TypeId.Void => { + root.main(); + return 0; + }, + builtin.TypeId.Int => { + if (@typeOf(root.main).ReturnType.bit_count != 8) { + @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); + } + return root.main(); + }, + builtin.TypeId.ErrorUnion => { + root.main() catch |err| { + std.debug.warn("error: {}\n", @errorName(err)); + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace); + } + return 1; + }; + return 0; + }, + else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"), + } } diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index b293a1e64d..4722aff07a 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -14,7 +14,7 @@ pub fn main() -> %void { var arg_it = os.args(); // TODO use a more general purpose allocator here - var inc_allocator = std.heap.IncrementingAllocator.init(40 * 1024 * 1024) catch unreachable; + var inc_allocator = try std.heap.IncrementingAllocator.init(40 * 1024 * 1024); defer inc_allocator.deinit(); const allocator = &inc_allocator.allocator; @@ -107,12 +107,12 @@ pub fn main() -> %void { return usageAndErr(&builder, false, try stderr_stream); } } else { - targets.append(arg) catch unreachable; + try targets.append(arg); } } builder.setInstallPrefix(prefix); - root.build(&builder) catch unreachable; + try root.build(&builder); if (builder.validateUserInputDidItFail()) return usageAndErr(&builder, true, try stderr_stream); @@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) // run the build script to collect the options if (!already_ran_build) { builder.setInstallPrefix(null); - root.build(builder) catch unreachable; + try root.build(builder); } // This usage text has to be synchronized with src/main.cpp diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig index 027251c52c..c5c0bad40c 100644 --- a/std/special/test_runner.zig +++ b/std/special/test_runner.zig @@ -8,14 +8,7 @@ pub fn main() -> %void { for (test_fn_list) |test_fn, i| { warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name); - if (builtin.is_test) { - test_fn.func() catch unreachable; - } else { - test_fn.func() catch |err| { - warn("{}\n", err); - return err; - }; - } + try test_fn.func(); warn("OK\n"); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 005fe55e41..6b2ea545ed 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) { + cases.add("wrong return type for main", + \\pub fn main() -> f32 { } + , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); + + cases.add("double ?? on main return value", + \\pub fn main() -> ??void { + \\} + , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); + cases.add("bad identifier in function with struct defined inside function which references local const", \\export fn entry() { \\ const BlockKind = u32; @@ -1059,15 +1068,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:2:5: error: expected type 'void', found 'error'"); - cases.add("wrong return type for main", - \\pub fn main() { } - , ".tmp_source.zig:1:15: error: expected return type of main to be '%void', instead is 'void'"); - - cases.add("double ?? on main return value", - \\pub fn main() -> ??void { - \\} - , ".tmp_source.zig:1:18: error: expected return type of main to be '%void', instead is '??void'"); - cases.add("invalid pointer for var type", \\extern fn ext() -> usize; \\var bytes: [ext()]u8 = undefined; diff --git a/test/debug_safety.zig b/test/debug_safety.zig index fde5b061ee..b32ffb34f0 100644 --- a/test/debug_safety.zig +++ b/test/debug_safety.zig @@ -2,7 +2,7 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompareOutputContext) { cases.addDebugSafety("calling panic", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() -> %void { @@ -11,7 +11,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("out of bounds slice access", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() -> %void { @@ -25,7 +25,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("integer addition overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -39,7 +39,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("integer subtraction overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -53,7 +53,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("integer multiplication overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -67,7 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("integer negation overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -81,7 +81,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("signed integer division overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -95,7 +95,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("signed shift left overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -109,7 +109,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("unsigned shift left overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -123,7 +123,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("signed shift right overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -137,7 +137,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("unsigned shift right overflow", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -151,7 +151,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("integer division by zero", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -164,7 +164,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("exact division failure", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -178,7 +178,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("cast []u8 to bigger slice of wrong size", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -192,7 +192,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("value does not fit in shortening cast", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -206,7 +206,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("signed integer not fitting in cast to unsigned integer", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Whatever; @@ -220,7 +220,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("unwrap error", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) { \\ @import("std").os.exit(126); // good \\ } @@ -236,7 +236,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("cast integer to error and no code matches", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() -> %void { @@ -248,7 +248,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("@alignCast misaligned", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\error Wrong; @@ -265,7 +265,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { ); cases.addDebugSafety("bad union field access", - \\pub fn panic(message: []const u8) -> noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn { \\ @import("std").os.exit(126); \\} \\ diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig index 50fa10c593..b7b288f7a1 100644 --- a/test/standalone/issue_339/build.zig +++ b/test/standalone/issue_339/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { const obj = b.addObject("test", "test.zig"); const test_step = b.step("test", "Test the program"); diff --git a/test/standalone/issue_339/test.zig b/test/standalone/issue_339/test.zig index e0d6c3f3e5..dd97ffa9b1 100644 --- a/test/standalone/issue_339/test.zig +++ b/test/standalone/issue_339/test.zig @@ -1,4 +1,5 @@ -pub fn panic(msg: []const u8) -> noreturn { @breakpoint(); while (true) {} } +const StackTrace = @import("builtin").StackTrace; +pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) -> noreturn { @breakpoint(); while (true) {} } fn bar() -> %void {} diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig index 044787dd4d..f40011d834 100644 --- a/test/standalone/pkg_import/build.zig +++ b/test/standalone/pkg_import/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { const exe = b.addExecutable("test", "test.zig"); exe.addPackagePath("my_pkg", "pkg.zig"); diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig index 03398f1a41..beca6bc304 100644 --- a/test/standalone/use_alias/build.zig +++ b/test/standalone/use_alias/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) { +pub fn build(b: &Builder) -> %void { b.addCIncludePath("."); const main = b.addTest("main.zig"); diff --git a/test/tests.zig b/test/tests.zig index 20e1e94459..5fd995f034 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -50,6 +50,7 @@ const test_targets = []TestTarget { }; error TestFailed; +error CompilationIncorrectlySucceeded; const max_stdout_size = 1 * 1024 * 1024; // 1 MB @@ -607,8 +608,7 @@ pub const CompileErrorContext = struct { switch (term) { Term.Exited => |code| { if (code == 0) { - warn("Compilation incorrectly succeeded\n"); - return error.TestFailed; + return error.CompilationIncorrectlySucceeded; } }, else => { From c9ac607bd3bf2cc1cfb941e54e6bcd53f2fcc59d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 15 Jan 2018 00:14:14 -0500 Subject: [PATCH 10/12] add builtin.have_error_return_tracing --- src/codegen.cpp | 1 + std/special/panic.zig | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 5113aa7275..c72bb07c8b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5444,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"); diff --git a/std/special/panic.zig b/std/special/panic.zig index 690c4afa1c..d26166fdac 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -13,10 +13,12 @@ pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) - while (true) {} }, else => { - if (error_return_trace) |trace| { - std.debug.warn("{}\n", msg); - std.debug.dumpStackTrace(trace); - @import("std").debug.panic(""); + 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); }, From 6ec9933fd80013104982debab9fbff1463582f19 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 15 Jan 2018 16:26:13 -0500 Subject: [PATCH 11/12] fix getting debug info twice in default panic handler --- std/debug/index.zig | 29 +++++++++++++++++++++++++++-- std/special/panic.zig | 8 ++------ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/std/debug/index.zig b/std/debug/index.zig index 5dc7b0dbfa..87e81ab8c2 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -41,10 +41,21 @@ fn getStderrStream() -> %&io.OutStream { } } +var self_debug_info: ?&ElfStackTrace = null; +pub fn getSelfDebugInfo() -> %&ElfStackTrace { + if (self_debug_info) |info| { + return info; + } else { + const info = try openSelfDebugInfo(global_allocator); + self_debug_info = info; + return info; + } +} + /// 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| { + const debug_info = getSelfDebugInfo() catch |err| { stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; return; }; @@ -58,7 +69,7 @@ pub fn dumpCurrentStackTrace() { /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) { const stderr = getStderrStream() catch return; - const debug_info = openSelfDebugInfo(global_allocator) catch |err| { + const debug_info = getSelfDebugInfo() catch |err| { stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; return; }; @@ -119,6 +130,20 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn { os.abort(); } +pub fn panicWithTrace(trace: &const builtin.StackTrace, comptime format: []const u8, args: ...) -> noreturn { + if (panicking) { + os.abort(); + } else { + panicking = true; + } + const stderr = getStderrStream() catch os.abort(); + stderr.print(format ++ "\n", args) catch os.abort(); + dumpStackTrace(trace); + dumpCurrentStackTrace(); + + os.abort(); +} + const GREEN = "\x1b[32;1m"; const WHITE = "\x1b[37;1m"; const DIM = "\x1b[2m"; diff --git a/std/special/panic.zig b/std/special/panic.zig index d26166fdac..1b22658c7f 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -13,12 +13,8 @@ pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) - 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(""); - } + if (error_return_trace) |trace| { + @import("std").debug.panicWithTrace(trace, "{}", msg); } @import("std").debug.panic("{}", msg); }, From 92fc5947fc18ea077f0e02bc4758e9050270e4bb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 15 Jan 2018 20:44:21 -0500 Subject: [PATCH 12/12] fix compiler crash related to @alignOf --- src/analyze.cpp | 1 + src/ir.cpp | 4 +- test/build_examples.zig | 1 + test/standalone/brace_expansion/build.zig | 9 + test/standalone/brace_expansion/main.zig | 254 ++++++++++++++++++++++ 5 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 test/standalone/brace_expansion/build.zig create mode 100644 test/standalone/brace_expansion/main.zig diff --git a/src/analyze.cpp b/src/analyze.cpp index 8028ace289..9da8485014 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2231,6 +2231,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { // is a pointer to this very struct, or a function pointer with parameters that // reference such a type. union_type->data.unionation.zero_bits_known = true; + union_type->data.unionation.zero_bits_loop_flag = false; if (union_type->data.unionation.abi_alignment == 0) { if (union_type->data.unionation.layout == ContainerLayoutPacked) { union_type->data.unionation.abi_alignment = 1; diff --git a/src/ir.cpp b/src/ir.cpp index d6844f32ba..4dd022c09f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9858,7 +9858,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal if (fn_proto_node->data.fn_proto.is_var_args) { ir_add_error(ira, &call_instruction->base, - buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/andrewrk/zig/issues/313")); + buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/zig-lang/zig/issues/313")); return ira->codegen->builtin_types.entry_invalid; } @@ -14011,7 +14011,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); - ensure_complete_type(ira->codegen, type_entry); + type_ensure_zero_bits_known(ira->codegen, type_entry); if (type_is_invalid(type_entry)) return ira->codegen->builtin_types.entry_invalid; diff --git a/test/build_examples.zig b/test/build_examples.zig index 29a15fd515..666dac8df1 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -16,4 +16,5 @@ pub fn addCases(cases: &tests.BuildExamplesContext) { cases.addBuildFile("test/standalone/issue_339/build.zig"); cases.addBuildFile("test/standalone/pkg_import/build.zig"); cases.addBuildFile("test/standalone/use_alias/build.zig"); + cases.addBuildFile("test/standalone/brace_expansion/build.zig"); } diff --git a/test/standalone/brace_expansion/build.zig b/test/standalone/brace_expansion/build.zig new file mode 100644 index 0000000000..d170b50fe0 --- /dev/null +++ b/test/standalone/brace_expansion/build.zig @@ -0,0 +1,9 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: &Builder) -> %void { + const main = b.addTest("main.zig"); + main.setBuildMode(b.standardReleaseOptions()); + + const test_step = b.step("test", "Test it"); + test_step.dependOn(&main.step); +} diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig new file mode 100644 index 0000000000..7b03e230f7 --- /dev/null +++ b/test/standalone/brace_expansion/main.zig @@ -0,0 +1,254 @@ +const std = @import("std"); +const io = std.io; +const mem = std.mem; +const debug = std.debug; +const assert = debug.assert; +const Buffer = std.Buffer; +const ArrayList = std.ArrayList; + +error InvalidInput; +error OutOfMem; + +const Token = union(enum) { + Word: []const u8, + OpenBrace, + CloseBrace, + Comma, + Eof, +}; + +var global_allocator: &mem.Allocator = undefined; + +fn tokenize(input:[] const u8) -> %ArrayList(Token) { + const State = enum { + Start, + Word, + }; + + var token_list = ArrayList(Token).init(global_allocator); + var tok_begin: usize = undefined; + var state = State.Start; + + for (input) |b, i| { + switch (state) { + State.Start => switch (b) { + 'a'...'z', 'A'...'Z' => { + state = State.Word; + tok_begin = i; + }, + '{' => try token_list.append(Token.OpenBrace), + '}' => try token_list.append(Token.CloseBrace), + ',' => try token_list.append(Token.Comma), + else => return error.InvalidInput, + }, + State.Word => switch (b) { + 'a'...'z', 'A'...'Z' => {}, + '{', '}', ',' => { + try token_list.append(Token { .Word = input[tok_begin..i] }); + switch (b) { + '{' => try token_list.append(Token.OpenBrace), + '}' => try token_list.append(Token.CloseBrace), + ',' => try token_list.append(Token.Comma), + else => unreachable, + } + state = State.Start; + }, + else => return error.InvalidInput, + }, + } + } + switch (state) { + State.Start => {}, + State.Word => try token_list.append(Token {.Word = input[tok_begin..] }), + } + try token_list.append(Token.Eof); + return token_list; +} + +const Node = union(enum) { + Scalar: []const u8, + List: ArrayList(Node), + Combine: []Node, +}; + +fn parse(tokens: &const ArrayList(Token), token_index: &usize) -> %Node { + const first_token = tokens.items[*token_index]; + *token_index += 1; + + const result_node = switch (first_token) { + Token.Word => |word| Node { .Scalar = word }, + Token.OpenBrace => blk: { + var list = ArrayList(Node).init(global_allocator); + while (true) { + try list.append(try parse(tokens, token_index)); + + const token = tokens.items[*token_index]; + *token_index += 1; + + switch (token) { + Token.CloseBrace => break, + Token.Comma => continue, + else => return error.InvalidInput, + } + } + break :blk Node { .List = list }; + }, + else => return error.InvalidInput, + }; + + switch (tokens.items[*token_index]) { + Token.Word, Token.OpenBrace => { + const pair = try global_allocator.alloc(Node, 2); + pair[0] = result_node; + pair[1] = try parse(tokens, token_index); + return Node { .Combine = pair }; + }, + else => return result_node, + } +} + +fn expandString(input: []const u8, output: &Buffer) -> %void { + const tokens = try tokenize(input); + if (tokens.len == 1) { + return output.resize(0); + } + + var token_index: usize = 0; + const root = try parse(tokens, &token_index); + const last_token = tokens.items[token_index]; + switch (last_token) { + Token.Eof => {}, + else => return error.InvalidInput, + } + + var result_list = ArrayList(Buffer).init(global_allocator); + defer result_list.deinit(); + + try expandNode(root, &result_list); + + try output.resize(0); + for (result_list.toSliceConst()) |buf, i| { + if (i != 0) { + try output.appendByte(' '); + } + try output.append(buf.toSliceConst()); + } +} + +const ListOfBuffer0 = ArrayList(Buffer); // TODO this is working around a compiler bug, fix and delete this + +fn expandNode(node: &const Node, output: &ListOfBuffer0) -> %void { + assert(output.len == 0); + switch (*node) { + Node.Scalar => |scalar| { + try output.append(try Buffer.init(global_allocator, scalar)); + }, + Node.Combine => |pair| { + const a_node = pair[0]; + const b_node = pair[1]; + + var child_list_a = ArrayList(Buffer).init(global_allocator); + try expandNode(a_node, &child_list_a); + + var child_list_b = ArrayList(Buffer).init(global_allocator); + try expandNode(b_node, &child_list_b); + + for (child_list_a.toSliceConst()) |buf_a| { + for (child_list_b.toSliceConst()) |buf_b| { + var combined_buf = try Buffer.initFromBuffer(buf_a); + try combined_buf.append(buf_b.toSliceConst()); + try output.append(combined_buf); + } + } + }, + Node.List => |list| { + for (list.toSliceConst()) |child_node| { + var child_list = ArrayList(Buffer).init(global_allocator); + try expandNode(child_node, &child_list); + + for (child_list.toSliceConst()) |buf| { + try output.append(buf); + } + } + }, + } +} + +pub fn main() -> %void { + var stdin_file = try io.getStdIn(); + var stdout_file = try io.getStdOut(); + + var inc_allocator = try std.heap.IncrementingAllocator.init(2 * 1024 * 1024); + defer inc_allocator.deinit(); + + global_allocator = &inc_allocator.allocator; + + var stdin_buf = try Buffer.initSize(global_allocator, 0); + defer stdin_buf.deinit(); + + var stdin_adapter = io.FileInStream.init(&stdin_file); + try stdin_adapter.stream.readAllBuffer(&stdin_buf, @maxValue(usize)); + + var result_buf = try Buffer.initSize(global_allocator, 0); + defer result_buf.deinit(); + + try expandString(stdin_buf.toSlice(), &result_buf); + try stdout_file.write(result_buf.toSliceConst()); +} + +test "invalid inputs" { + global_allocator = std.debug.global_allocator; + + expectError("}ABC", error.InvalidInput); + expectError("{ABC", error.InvalidInput); + expectError("}{", error.InvalidInput); + expectError("{}", error.InvalidInput); + expectError("A,B,C", error.InvalidInput); + expectError("{A{B,C}", error.InvalidInput); + expectError("{A,}", error.InvalidInput); + + expectError("\n", error.InvalidInput); +} + +fn expectError(test_input: []const u8, expected_err: error) { + var output_buf = Buffer.initSize(global_allocator, 0) catch unreachable; + defer output_buf.deinit(); + + if (expandString("}ABC", &output_buf)) { + unreachable; + } else |err| { + assert(expected_err == err); + } +} + +test "valid inputs" { + global_allocator = std.debug.global_allocator; + + expectExpansion("{x,y,z}", "x y z"); + expectExpansion("{A,B}{x,y}", "Ax Ay Bx By"); + expectExpansion("{A,B{x,y}}", "A Bx By"); + + expectExpansion("{ABC}", "ABC"); + expectExpansion("{A,B,C}", "A B C"); + expectExpansion("ABC", "ABC"); + + expectExpansion("", ""); + expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh"); + expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh"); + expectExpansion("{A,B}a", "Aa Ba"); + expectExpansion("{C,{x,y}}", "C x y"); + expectExpansion("z{C,{x,y}}", "zC zx zy"); + expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg"); + expectExpansion("a{x,y}b", "axb ayb"); + expectExpansion("z{{a,b}}", "za zb"); + expectExpansion("a{b}", "ab"); +} + +fn expectExpansion(test_input: []const u8, expected_result: []const u8) { + var result = Buffer.initSize(global_allocator, 0) catch unreachable; + defer result.deinit(); + + expandString(test_input, &result) catch unreachable; + + assert(mem.eql(u8, result.toSlice(), expected_result)); +}