From 07a71fc3221dfba05caea5a50ebe3dac5c76d643 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Feb 2017 03:10:32 -0500 Subject: [PATCH] improved behavior on debug safety crash * instead of emitting a breakpoint for a debug safety crash, zig calls a panic function which prints an error message and a stack trace and then calls abort. * on freestanding OS, this panic function has a default implementation of a simple infinite loop. * users can override the panic implementation by providing `pub fn panic(message: []const u8) -> unreachable { }` * workaround for LLVM segfaulting when you try to use cold calling convention on ARM. closes #245 --- CMakeLists.txt | 15 +++--- example/guess_number/main.zig | 4 +- src/all_types.hpp | 21 +++++++++ src/analyze.cpp | 88 ++++++++++++++++++++++++++--------- src/codegen.cpp | 84 +++++++++++++++++++++++++++------ src/ir.cpp | 10 ---- std/bootstrap.zig | 5 +- std/debug.zig | 2 + std/panic.zig | 33 +++++++++++++ test/run_tests.cpp | 55 +++++++++++++++++++--- 10 files changed, 254 insertions(+), 63 deletions(-) create mode 100644 std/panic.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e5d58ecc3..5fce41d4cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,10 @@ set(TEST_SOURCES ) set(C_HEADERS + "${CMAKE_SOURCE_DIR}/c_headers/Intrin.h" + "${CMAKE_SOURCE_DIR}/c_headers/__stddef_max_align_t.h" + "${CMAKE_SOURCE_DIR}/c_headers/__wmmintrin_aes.h" + "${CMAKE_SOURCE_DIR}/c_headers/__wmmintrin_pclmul.h" "${CMAKE_SOURCE_DIR}/c_headers/adxintrin.h" "${CMAKE_SOURCE_DIR}/c_headers/ammintrin.h" "${CMAKE_SOURCE_DIR}/c_headers/arm_acle.h" @@ -95,14 +99,13 @@ set(C_HEADERS "${CMAKE_SOURCE_DIR}/c_headers/htmxlintrin.h" "${CMAKE_SOURCE_DIR}/c_headers/ia32intrin.h" "${CMAKE_SOURCE_DIR}/c_headers/immintrin.h" - "${CMAKE_SOURCE_DIR}/c_headers/Intrin.h" "${CMAKE_SOURCE_DIR}/c_headers/inttypes.h" "${CMAKE_SOURCE_DIR}/c_headers/iso646.h" "${CMAKE_SOURCE_DIR}/c_headers/limits.h" "${CMAKE_SOURCE_DIR}/c_headers/lzcntintrin.h" "${CMAKE_SOURCE_DIR}/c_headers/mm3dnow.h" - "${CMAKE_SOURCE_DIR}/c_headers/mmintrin.h" "${CMAKE_SOURCE_DIR}/c_headers/mm_malloc.h" + "${CMAKE_SOURCE_DIR}/c_headers/mmintrin.h" "${CMAKE_SOURCE_DIR}/c_headers/nmmintrin.h" "${CMAKE_SOURCE_DIR}/c_headers/pmmintrin.h" "${CMAKE_SOURCE_DIR}/c_headers/popcntintrin.h" @@ -117,7 +120,6 @@ set(C_HEADERS "${CMAKE_SOURCE_DIR}/c_headers/stdatomic.h" "${CMAKE_SOURCE_DIR}/c_headers/stdbool.h" "${CMAKE_SOURCE_DIR}/c_headers/stddef.h" - "${CMAKE_SOURCE_DIR}/c_headers/__stddef_max_align_t.h" "${CMAKE_SOURCE_DIR}/c_headers/stdint.h" "${CMAKE_SOURCE_DIR}/c_headers/stdnoreturn.h" "${CMAKE_SOURCE_DIR}/c_headers/tbmintrin.h" @@ -127,9 +129,7 @@ set(C_HEADERS "${CMAKE_SOURCE_DIR}/c_headers/vadefs.h" "${CMAKE_SOURCE_DIR}/c_headers/varargs.h" "${CMAKE_SOURCE_DIR}/c_headers/vecintrin.h" - "${CMAKE_SOURCE_DIR}/c_headers/__wmmintrin_aes.h" "${CMAKE_SOURCE_DIR}/c_headers/wmmintrin.h" - "${CMAKE_SOURCE_DIR}/c_headers/__wmmintrin_pclmul.h" "${CMAKE_SOURCE_DIR}/c_headers/x86intrin.h" "${CMAKE_SOURCE_DIR}/c_headers/xmmintrin.h" "${CMAKE_SOURCE_DIR}/c_headers/xopintrin.h" @@ -202,6 +202,8 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/bootstrap.zig" DESTINATION "${ZIG_STD_DES install(FILES "${CMAKE_SOURCE_DIR}/std/builtin.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/compiler_rt.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/cstr.zig" DESTINATION "${ZIG_STD_DEST}") +install(FILES "${CMAKE_SOURCE_DIR}/std/darwin.zig" DESTINATION "${ZIG_STD_DEST}") +install(FILES "${CMAKE_SOURCE_DIR}/std/darwin_x86_64.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/debug.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/dwarf.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/elf.zig" DESTINATION "${ZIG_STD_DEST}") @@ -214,13 +216,12 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/io.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/linux.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/linux_i386.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/linux_x86_64.zig" DESTINATION "${ZIG_STD_DEST}") -install(FILES "${CMAKE_SOURCE_DIR}/std/darwin.zig" DESTINATION "${ZIG_STD_DEST}") -install(FILES "${CMAKE_SOURCE_DIR}/std/darwin_x86_64.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/list.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/math.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/mem.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/net.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/os.zig" DESTINATION "${ZIG_STD_DEST}") +install(FILES "${CMAKE_SOURCE_DIR}/std/panic.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/rand.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/rand_test.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/sort.zig" DESTINATION "${ZIG_STD_DEST}") diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 30a51eab09..35c0d46760 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -18,9 +18,7 @@ pub fn main(args: [][]u8) -> %void { var line_buf : [20]u8 = undefined; const line_len = io.stdin.read(line_buf) %% |err| { - %%io.stdout.printf("Unable to read from stdin: "); - %%io.stdout.printf(@errorName(err)); - %%io.stdout.printf("\n"); + %%io.stdout.printf("Unable to read from stdin: {}\n", @errorName(err)); return err; }; diff --git a/src/all_types.hpp b/src/all_types.hpp index 3f4e20df97..bbd8df4b70 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1132,6 +1132,22 @@ struct BuiltinFnEntry { LLVMValueRef fn_val; }; +enum PanicMsgId { + PanicMsgIdUnreachable, + PanicMsgIdBoundsCheckFailure, + PanicMsgIdCastNegativeToUnsigned, + PanicMsgIdCastTruncatedData, + PanicMsgIdIntegerOverflow, + PanicMsgIdShiftOverflowedBits, + PanicMsgIdDivisionByZero, + PanicMsgIdExactDivisionRemainder, + PanicMsgIdSliceWidenRemainder, + PanicMsgIdUnwrapMaybeFail, + PanicMsgIdUnwrapErrFail, + + PanicMsgIdCount, +}; + uint32_t fn_eval_hash(Scope*); bool fn_eval_eql(Scope *a, Scope *b); @@ -1210,6 +1226,7 @@ struct CodeGen { bool strip_debug_symbols; bool want_h_file; bool have_exported_main; + bool have_exported_panic; bool link_libc; Buf *libc_lib_dir; Buf *libc_static_lib_dir; @@ -1230,6 +1247,7 @@ struct CodeGen { bool is_native_target; PackageTableEntry *root_package; PackageTableEntry *std_package; + PackageTableEntry *panic_package; Buf *root_out_name; bool windows_subsystem_windows; bool windows_subsystem_console; @@ -1252,6 +1270,7 @@ struct CodeGen { OutType out_type; FnTableEntry *cur_fn; FnTableEntry *main_fn; + FnTableEntry *panic_fn; LLVMValueRef cur_ret_ptr; LLVMValueRef cur_fn_val; ZigList break_block_stack; @@ -1292,6 +1311,8 @@ struct CodeGen { IrInstruction *invalid_instruction; ConstExprValue const_void_val; + + ConstExprValue panic_msg_vals[PanicMsgIdCount]; }; enum VarLinkage { diff --git a/src/analyze.cpp b/src/analyze.cpp index 012eb6d7df..23040f2dd7 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -713,7 +713,13 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { fn_type->data.fn.fn_type_id = *fn_type_id; if (fn_type_id->is_cold) { - fn_type->data.fn.calling_convention = LLVMColdCallConv; + if (g->zig_target.arch.arch == ZigLLVM_arm) { + // TODO we want to use coldcc here but it's causing a segfault on ARM + // https://llvm.org/bugs/show_bug.cgi?id=31875 + fn_type->data.fn.calling_convention = LLVMCCallConv; + } else { + fn_type->data.fn.calling_convention = LLVMColdCallConv; + } } else if (fn_type_id->is_extern) { fn_type->data.fn.calling_convention = LLVMCCallConv; } else { @@ -725,8 +731,8 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { // populate the name of the type buf_resize(&fn_type->name, 0); const char *extern_str = fn_type_id->is_extern ? "extern " : ""; - const char *naked_str = fn_type_id->is_naked ? "naked " : ""; - const char *cold_str = fn_type_id->is_cold ? "cold " : ""; + const char *naked_str = fn_type_id->is_naked ? "nakedcc " : ""; + const char *cold_str = fn_type_id->is_cold ? "coldcc " : ""; buf_appendf(&fn_type->name, "%s%s%sfn(", extern_str, naked_str, cold_str); for (size_t i = 0; i < fn_type_id->param_count; i += 1) { FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; @@ -1572,6 +1578,33 @@ static bool scope_is_root_decls(Scope *scope) { zig_unreachable(); } +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_ptr(&fn_type->name))); +} + +static void typecheck_panic_fn(CodeGen *g) { + assert(g->panic_fn); + + AstNode *proto_node = g->panic_fn->proto_node; + assert(proto_node->type == NodeTypeFnProto); + TypeTableEntry *fn_type = g->panic_fn->type_entry; + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + if (fn_type_id->param_count != 1) { + return wrong_panic_prototype(g, proto_node, fn_type); + } + TypeTableEntry *const_u8_slice = get_slice_type(g, g->builtin_types.entry_u8, true); + if (fn_type_id->param_info[0].type != const_u8_slice) { + 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); + } +} + static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { ImportTableEntry *import = tld_fn->base.import; AstNode *proto_node = tld_fn->base.source_node; @@ -1612,27 +1645,39 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { if (fn_def_node) g->fn_defs.append(fn_table_entry); - bool is_main_fn = scope_is_root_decls(tld_fn->base.parent_scope) && - (import == g->root_import) && buf_eql_str(&fn_table_entry->symbol_name, "main"); - if (is_main_fn) - g->main_fn = fn_table_entry; + if (import == g->root_import && scope_is_root_decls(tld_fn->base.parent_scope)) { + if (buf_eql_str(&fn_table_entry->symbol_name, "main")) { + g->main_fn = fn_table_entry; - if (is_main_fn && !g->link_libc && tld_fn->base.visib_mod != VisibModExport) { - 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))); + if (!g->link_libc && tld_fn->base.visib_mod != VisibModExport) { + 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 (buf_eql_str(&fn_table_entry->symbol_name, "panic")) { + g->panic_fn = fn_table_entry; + typecheck_panic_fn(g); + } + } else if (import->package == g->panic_package && scope_is_root_decls(tld_fn->base.parent_scope)) { + if (buf_eql_str(&fn_table_entry->symbol_name, "panic")) { + g->panic_fn = fn_table_entry; + typecheck_panic_fn(g); } } } } static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { - bool want_to_resolve = (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport); - if (want_to_resolve) + if (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport || + (buf_eql_str(tld->name, "panic") && + (decls_scope->import->package == g->panic_package || decls_scope->import == g->root_import))) + { g->resolve_queue.append(tld); + } auto entry = decls_scope->decl_table.put_unique(tld->name, tld); if (entry) { @@ -2548,9 +2593,6 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, assert(import_entry->root); if (g->verbose) { ast_print(stderr, import_entry->root, 0); - //fprintf(stderr, "\nReformatted Source:\n"); - //fprintf(stderr, "---------------------\n"); - //ast_render(stderr, import_entry->root, 4); } import_entry->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); @@ -2571,8 +2613,12 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub); - if (buf_eql_str(proto_name, "main") && is_pub) { - g->have_exported_main = true; + if (is_pub) { + if (buf_eql_str(proto_name, "main")) { + g->have_exported_main = true; + } else if (buf_eql_str(proto_name, "panic")) { + g->have_exported_panic = true; + } } } } diff --git a/src/codegen.cpp b/src/codegen.cpp index 8c6f025a03..7383cf8769 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -450,8 +450,56 @@ static bool ir_want_debug_safety(CodeGen *g, IrInstruction *instruction) { return true; } -static void gen_debug_safety_crash(CodeGen *g) { - LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); +static Buf *panic_msg_buf(PanicMsgId msg_id) { + switch (msg_id) { + case PanicMsgIdCount: + zig_unreachable(); + case PanicMsgIdBoundsCheckFailure: + return buf_create_from_str("index out of bounds"); + case PanicMsgIdCastNegativeToUnsigned: + return buf_create_from_str("attempt to cast negative value to unsigned integer"); + case PanicMsgIdCastTruncatedData: + return buf_create_from_str("integer cast truncated bits"); + case PanicMsgIdIntegerOverflow: + return buf_create_from_str("integer overflow"); + case PanicMsgIdShiftOverflowedBits: + return buf_create_from_str("left shift overflowed bits"); + case PanicMsgIdDivisionByZero: + return buf_create_from_str("division by zero"); + case PanicMsgIdExactDivisionRemainder: + return buf_create_from_str("exact division produced remainder"); + case PanicMsgIdSliceWidenRemainder: + return buf_create_from_str("slice widening size mismatch"); + case PanicMsgIdUnwrapMaybeFail: + return buf_create_from_str("attempt to unwrap null"); + case PanicMsgIdUnwrapErrFail: + return buf_create_from_str("attempt to unwrap error"); + case PanicMsgIdUnreachable: + return buf_create_from_str("reached unreachable code"); + } + zig_unreachable(); +} + +static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { + ConstExprValue *val = &g->panic_msg_vals[msg_id]; + if (val->llvm_global) + return val->llvm_global; + + Buf *buf_msg = panic_msg_buf(msg_id); + ConstExprValue *array_val = create_const_str_lit(g, buf_msg); + init_const_slice(g, val, array_val, 0, buf_len(buf_msg), true); + + render_const_val_global(g, val, ""); + render_const_val(g, val); + + assert(val->llvm_global); + return val->llvm_global; +} + +static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) { + LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn); + LLVMValueRef msg_arg = get_panic_msg_ptr_val(g, msg_id); + ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, g->panic_fn->type_entry->data.fn.calling_convention, ""); LLVMBuildUnreachable(g->builder); } @@ -477,7 +525,7 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, LLVMBuildCondBr(g->builder, lower_ok_val, lower_ok_block, bounds_check_fail_block); LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block); - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdBoundsCheckFailure); if (upper_value) { LLVMPositionBuilderAtEnd(g->builder, lower_ok_block); @@ -520,7 +568,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_debug_safety, Typ LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdCastNegativeToUnsigned); LLVMPositionBuilderAtEnd(g->builder, ok_block); } @@ -559,7 +607,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_debug_safety, Typ LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdCastTruncatedData); LLVMPositionBuilderAtEnd(g->builder, ok_block); return trunc_val; @@ -587,7 +635,7 @@ static LLVMValueRef gen_overflow_op(CodeGen *g, TypeTableEntry *type_entry, AddS LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdIntegerOverflow); LLVMPositionBuilderAtEnd(g->builder, ok_block); return result; @@ -748,7 +796,7 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry, LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdShiftOverflowedBits); LLVMPositionBuilderAtEnd(g->builder, ok_block); return result; @@ -773,7 +821,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val LLVMBuildCondBr(g->builder, is_zero_bit, fail_block, ok_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdDivisionByZero); LLVMPositionBuilderAtEnd(g->builder, ok_block); } @@ -801,7 +849,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdExactDivisionRemainder); LLVMPositionBuilderAtEnd(g->builder, ok_block); } @@ -1038,7 +1086,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdSliceWidenRemainder); LLVMPositionBuilderAtEnd(g->builder, ok_block); } @@ -1162,7 +1210,7 @@ static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable, IrInstructionUnreachable *unreachable_instruction) { if (ir_want_debug_safety(g, &unreachable_instruction->base) || g->is_test_build) { - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdUnreachable); } else { LLVMBuildUnreachable(g->builder); } @@ -1622,7 +1670,7 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdUnwrapMaybeFail); LLVMPositionBuilderAtEnd(g->builder, ok_block); } @@ -2107,7 +2155,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); LLVMPositionBuilderAtEnd(g->builder, err_block); - gen_debug_safety_crash(g); + gen_debug_safety_crash(g, PanicMsgIdUnwrapErrFail); LLVMPositionBuilderAtEnd(g->builder, ok_block); } @@ -3849,6 +3897,12 @@ static PackageTableEntry *create_bootstrap_pkg(CodeGen *g) { return package; } +static PackageTableEntry *create_panic_pkg(CodeGen *g) { + PackageTableEntry *package = new_package(buf_ptr(g->zig_std_dir), ""); + package->package_table.put(buf_create_from_str("std"), g->std_package); + return package; +} + void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *source_code) { Buf source_path = BUF_INIT; os_path_join(src_dir, src_basename, &source_path); @@ -3873,6 +3927,10 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g), "bootstrap.zig"); } } + if (!g->have_exported_panic) { + g->panic_package = create_panic_pkg(g); + add_special_code(g, g->panic_package, "panic.zig"); + } if (g->verbose) { fprintf(stderr, "\nIR Generation and Semantic Analysis:\n"); diff --git a/src/ir.cpp b/src/ir.cpp index 8348ecfb8e..a6add73124 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3878,20 +3878,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - if (exec_fn_entry(irb->exec)) { - add_node_error(irb->codegen, node, buf_sprintf("import valid only at global scope")); - return irb->codegen->invalid_instruction; - } - return ir_build_import(irb, scope, node, arg0_value); } case BuiltinFnIdCImport: { - if (exec_fn_entry(irb->exec)) { - add_node_error(irb->codegen, node, buf_sprintf("C import valid only at global scope")); - return irb->codegen->invalid_instruction; - } - return ir_build_c_import(irb, scope, node); } case BuiltinFnIdCInclude: diff --git a/std/bootstrap.zig b/std/bootstrap.zig index 52acab6544..17e32a96f4 100644 --- a/std/bootstrap.zig +++ b/std/bootstrap.zig @@ -1,8 +1,9 @@ // This file is in a package which has the root source file exposed as "@root". const root = @import("@root"); -const linux = @import("linux.zig"); -const cstr = @import("cstr.zig"); +const std = @import("std"); +const linux = std.linux; +const cstr = std.cstr; const want_start_symbol = switch(@compileVar("os")) { Os.linux => true, diff --git a/std/debug.zig b/std/debug.zig index 85e5503fa8..435d572ac2 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -42,6 +42,8 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { st.debug_str = (%return st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; %return scanAllCompileUnits(st); + %return out_stream.printf("(...work-in-progress stack unwinding code follows...)\n"); + var maybe_fp: ?&const u8 = @frameAddress(); while (true) { const fp = maybe_fp ?? break; diff --git a/std/panic.zig b/std/panic.zig new file mode 100644 index 0000000000..593930a4af --- /dev/null +++ b/std/panic.zig @@ -0,0 +1,33 @@ +// This file is included if and only if the user's main source file does not +// include a public panic function. +// If this file wants to import other files *by name*, support for that would +// have to be added in the compiler. + +var panicking = false; +pub coldcc fn panic(message: []const u8) -> unreachable { + if (@compileVar("os") == Os.freestanding) { + while (true) {} + } else { + const std = @import("std"); + const io = std.io; + const debug = std.debug; + const os = std.os; + + // TODO + // if (@atomicRmw(AtomicOp.XChg, &panicking, true, AtomicOrder.SeqCst)) { + if (panicking) { + // Panicked during a panic. + // TODO detect if a different thread caused the panic, because in that case + // we would want to return here instead of calling abort, so that the thread + // which first called panic can finish printing a stack trace. + os.abort(); + } else { + panicking = true; + } + + %%io.stderr.printf("{}\n", message); + %%debug.printStackTrace(); + + os.abort(); + } +} diff --git a/test/run_tests.cpp b/test/run_tests.cpp index e48c080a54..944d687d12 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1167,13 +1167,6 @@ fn f(n: Number) -> i32 { } )SOURCE", 1, ".tmp_source.zig:9:5: error: enumeration value 'Number.Four' not handled in switch"); - add_compile_fail_case("import inside function body", R"SOURCE( -fn f() { - const std = @import("std"); -} - )SOURCE", 1, ".tmp_source.zig:3:17: error: import valid only at global scope"); - - add_compile_fail_case("normal string with newline", R"SOURCE( const foo = "a b"; @@ -1675,6 +1668,10 @@ const some_data: [100]u8 = { static void add_debug_safety_test_cases(void) { add_debug_safety_case("out of bounds slice access", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} pub fn main(args: [][]u8) -> %void { const a = []i32{1, 2, 3, 4}; baz(bar(a)); @@ -1686,6 +1683,10 @@ fn baz(a: i32) { } )SOURCE"); add_debug_safety_case("integer addition overflow", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = add(65530, 10); @@ -1697,6 +1698,10 @@ fn add(a: u16, b: u16) -> u16 { )SOURCE"); add_debug_safety_case("integer subtraction overflow", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = sub(10, 20); @@ -1708,6 +1713,10 @@ fn sub(a: u16, b: u16) -> u16 { )SOURCE"); add_debug_safety_case("integer multiplication overflow", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = mul(300, 6000); @@ -1719,6 +1728,10 @@ fn mul(a: u16, b: u16) -> u16 { )SOURCE"); add_debug_safety_case("integer negation overflow", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = neg(-32768); @@ -1730,6 +1743,10 @@ fn neg(a: i16) -> i16 { )SOURCE"); add_debug_safety_case("signed shift left overflow", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = shl(-16385, 1); @@ -1741,6 +1758,10 @@ fn shl(a: i16, b: i16) -> i16 { )SOURCE"); add_debug_safety_case("unsigned shift left overflow", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = shl(0b0010111111111111, 3); @@ -1752,6 +1773,10 @@ fn shl(a: u16, b: u16) -> u16 { )SOURCE"); add_debug_safety_case("integer division by zero", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = div0(999, 0); @@ -1762,6 +1787,10 @@ fn div0(a: i32, b: i32) -> i32 { )SOURCE"); add_debug_safety_case("exact division failure", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = divExact(10, 3); @@ -1773,6 +1802,10 @@ fn divExact(a: i32, b: i32) -> i32 { )SOURCE"); add_debug_safety_case("cast []u8 to bigger slice of wrong size", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = widenSlice([]u8{1, 2, 3, 4, 5}); @@ -1784,6 +1817,10 @@ fn widenSlice(slice: []u8) -> []i32 { )SOURCE"); add_debug_safety_case("value does not fit in shortening cast", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = shorten_cast(200); @@ -1795,6 +1832,10 @@ fn shorten_cast(x: i32) -> i8 { )SOURCE"); add_debug_safety_case("signed integer not fitting in cast to unsigned integer", R"SOURCE( +pub fn panic(message: []const u8) -> unreachable { + @breakpoint(); + while (true) {} +} error Whatever; pub fn main(args: [][]u8) -> %void { const x = unsigned_cast(-10);