Commit 07a71fc322

Andrew Kelley <superjoe30@gmail.com>
2017-02-06 09:10:32
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
1 parent 28f63b8
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;
         };
 
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<LLVMBasicBlockRef> break_block_stack;
@@ -1292,6 +1311,8 @@ struct CodeGen {
 
     IrInstruction *invalid_instruction;
     ConstExprValue const_void_val;
+
+    ConstExprValue panic_msg_vals[PanicMsgIdCount];
 };
 
 enum VarLinkage {
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 (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 (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 (!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;
+                }
             }
         }
     }
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");
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:
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,
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;
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();
+    }
+}
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);
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}")