| author | |
| committer | |
| log | fc100d7b3b27bd514dca4e02c160e5b96d4da648 |
| tree | b365fbdf02c7a35d81d9037a15e1e3917a6e77de |
| parent | 8a859afd580f438f549ee69a3e3487eb5d119fad |
* add `@compileLog(...)` builtin function
- Helps debug code running at compile time
- See #240
* fix crash when there is an error on the start value of a slice
* add implicit cast from int and float types to int and float
literals if the value is known at compile time
* make array concatenation work with slices in addition to
arrays and c string literals
* fix compile error message for something not having field access
* fix crash when `@setDebugSafety()` was called from a
function being evaluated at compile-time
* fix compile-time evaluation of overflow math builtins.
* avoid debug safety panic handler in builtin.o and compiler_rt.o
since we use no debug safety in these modules anyway
* add compiler_rt functions for division on ARM
- Closes #254
* move default panic handler to std.debug so users can
call it manually
* std.io.printf supports a width in the format specifier15 files changed, 622 insertions(+), 78 deletions(-)
doc/langref.md+16-3| ... | ... | @@ -639,10 +639,23 @@ const b: u8 = @truncate(u8, a); |
| 639 | 639 | |
| 640 | 640 | ### @compileError(comptime msg: []u8) |
| 641 | 641 | |
| 642 | This function, when semantically analyzed, causes a compile error with the message `msg`. | |
| 642 | This function, when semantically analyzed, causes a compile error with the | |
| 643 | message `msg`. | |
| 643 | 644 | |
| 644 | There are several ways that code avoids being semantically checked, such as using `if` | |
| 645 | or `switch` with compile time constants, and comptime functions. | |
| 645 | There are several ways that code avoids being semantically checked, such as | |
| 646 | using `if` or `switch` with compile time constants, and comptime functions. | |
| 647 | ||
| 648 | ### @compileLog(args: ...) | |
| 649 | ||
| 650 | This function, when semantically analyzed, causes a compile error, but it does | |
| 651 | not prevent compile-time code from continuing to run, and it otherwise does not | |
| 652 | interfere with analysis. | |
| 653 | ||
| 654 | Each of the arguments will be serialized to a printable debug value and output | |
| 655 | to stderr, and then a newline at the end. | |
| 656 | ||
| 657 | This function can be used to do "printf debugging" on compile-time executing | |
| 658 | code. | |
| 646 | 659 | |
| 647 | 660 | ### @intType(comptime is_signed: bool, comptime bit_count: u8) -> type |
| 648 | 661 |
src/all_types.hpp+9| ... | ... | @@ -1096,6 +1096,7 @@ enum BuiltinFnId { |
| 1096 | 1096 | BuiltinFnIdCUndef, |
| 1097 | 1097 | BuiltinFnIdCompileVar, |
| 1098 | 1098 | BuiltinFnIdCompileErr, |
| 1099 | BuiltinFnIdCompileLog, | |
| 1099 | 1100 | BuiltinFnIdGeneratedCode, |
| 1100 | 1101 | BuiltinFnIdCtz, |
| 1101 | 1102 | BuiltinFnIdClz, |
| ... | ... | @@ -1541,6 +1542,7 @@ enum IrInstructionId { |
| 1541 | 1542 | IrInstructionIdMinValue, |
| 1542 | 1543 | IrInstructionIdMaxValue, |
| 1543 | 1544 | IrInstructionIdCompileErr, |
| 1545 | IrInstructionIdCompileLog, | |
| 1544 | 1546 | IrInstructionIdErrName, |
| 1545 | 1547 | IrInstructionIdEmbedFile, |
| 1546 | 1548 | IrInstructionIdCmpxchg, |
| ... | ... | @@ -1993,6 +1995,13 @@ struct IrInstructionCompileErr { |
| 1993 | 1995 | IrInstruction *msg; |
| 1994 | 1996 | }; |
| 1995 | 1997 | |
| 1998 | struct IrInstructionCompileLog { | |
| 1999 | IrInstruction base; | |
| 2000 | ||
| 2001 | size_t msg_count; | |
| 2002 | IrInstruction **msg_list; | |
| 2003 | }; | |
| 2004 | ||
| 1996 | 2005 | struct IrInstructionErrName { |
| 1997 | 2006 | IrInstruction base; |
| 1998 | 2007 |
src/analyze.cpp+23-1| ... | ... | @@ -3439,7 +3439,8 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue * |
| 3439 | 3439 | void render_const_value(Buf *buf, ConstExprValue *const_val) { |
| 3440 | 3440 | switch (const_val->special) { |
| 3441 | 3441 | case ConstValSpecialRuntime: |
| 3442 | zig_unreachable(); | |
| 3442 | buf_appendf(buf, "(runtime value)"); | |
| 3443 | return; | |
| 3443 | 3444 | case ConstValSpecialUndef: |
| 3444 | 3445 | buf_appendf(buf, "undefined"); |
| 3445 | 3446 | return; |
| ... | ... | @@ -3522,7 +3523,28 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) { |
| 3522 | 3523 | } |
| 3523 | 3524 | case TypeTableEntryIdArray: |
| 3524 | 3525 | { |
| 3526 | TypeTableEntry *child_type = canon_type->data.array.child_type; | |
| 3525 | 3527 | uint64_t len = canon_type->data.array.len; |
| 3528 | ||
| 3529 | // if it's []u8, assume UTF-8 and output a string | |
| 3530 | if (child_type->id == TypeTableEntryIdInt && | |
| 3531 | child_type->data.integral.bit_count == 8 && | |
| 3532 | !child_type->data.integral.is_signed) | |
| 3533 | { | |
| 3534 | buf_append_char(buf, '"'); | |
| 3535 | for (uint64_t i = 0; i < len; i += 1) { | |
| 3536 | ConstExprValue *child_value = &const_val->data.x_array.elements[i]; | |
| 3537 | uint64_t x = child_value->data.x_bignum.data.x_uint; | |
| 3538 | if (x == '"') { | |
| 3539 | buf_append_str(buf, "\\\""); | |
| 3540 | } else { | |
| 3541 | buf_append_char(buf, x); | |
| 3542 | } | |
| 3543 | } | |
| 3544 | buf_append_char(buf, '"'); | |
| 3545 | return; | |
| 3546 | } | |
| 3547 | ||
| 3526 | 3548 | buf_appendf(buf, "%s{", buf_ptr(&canon_type->name)); |
| 3527 | 3549 | for (uint64_t i = 0; i < len; i += 1) { |
| 3528 | 3550 | if (i != 0) |
src/codegen.cpp+2| ... | ... | @@ -2377,6 +2377,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 2377 | 2377 | case IrInstructionIdMinValue: |
| 2378 | 2378 | case IrInstructionIdMaxValue: |
| 2379 | 2379 | case IrInstructionIdCompileErr: |
| 2380 | case IrInstructionIdCompileLog: | |
| 2380 | 2381 | case IrInstructionIdArrayLen: |
| 2381 | 2382 | case IrInstructionIdImport: |
| 2382 | 2383 | case IrInstructionIdCImport: |
| ... | ... | @@ -3791,6 +3792,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 3791 | 3792 | create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2); |
| 3792 | 3793 | create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); |
| 3793 | 3794 | create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); |
| 3795 | create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); | |
| 3794 | 3796 | create_builtin_fn(g, BuiltinFnIdIntType, "intType", 2); |
| 3795 | 3797 | create_builtin_fn(g, BuiltinFnIdUnreachable, "unreachable", 0); |
| 3796 | 3798 | create_builtin_fn(g, BuiltinFnIdSetFnTest, "setFnTest", 1); |
src/ir.cpp+125-6| ... | ... | @@ -375,6 +375,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileErr *) { |
| 375 | 375 | return IrInstructionIdCompileErr; |
| 376 | 376 | } |
| 377 | 377 | |
| 378 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileLog *) { | |
| 379 | return IrInstructionIdCompileLog; | |
| 380 | } | |
| 381 | ||
| 378 | 382 | static constexpr IrInstructionId ir_instruction_id(IrInstructionErrName *) { |
| 379 | 383 | return IrInstructionIdErrName; |
| 380 | 384 | } |
| ... | ... | @@ -1510,6 +1514,20 @@ static IrInstruction *ir_build_compile_err(IrBuilder *irb, Scope *scope, AstNode |
| 1510 | 1514 | return &instruction->base; |
| 1511 | 1515 | } |
| 1512 | 1516 | |
| 1517 | static IrInstruction *ir_build_compile_log(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 1518 | size_t msg_count, IrInstruction **msg_list) | |
| 1519 | { | |
| 1520 | IrInstructionCompileLog *instruction = ir_build_instruction<IrInstructionCompileLog>(irb, scope, source_node); | |
| 1521 | instruction->msg_count = msg_count; | |
| 1522 | instruction->msg_list = msg_list; | |
| 1523 | ||
| 1524 | for (size_t i = 0; i < msg_count; i += 1) { | |
| 1525 | ir_ref_instruction(msg_list[i], irb->current_basic_block); | |
| 1526 | } | |
| 1527 | ||
| 1528 | return &instruction->base; | |
| 1529 | } | |
| 1530 | ||
| 1513 | 1531 | static IrInstruction *ir_build_err_name(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { |
| 1514 | 1532 | IrInstructionErrName *instruction = ir_build_instruction<IrInstructionErrName>(irb, scope, source_node); |
| 1515 | 1533 | instruction->value = value; |
| ... | ... | @@ -2461,6 +2479,12 @@ static IrInstruction *ir_instruction_compileerr_get_dep(IrInstructionCompileErr |
| 2461 | 2479 | } |
| 2462 | 2480 | } |
| 2463 | 2481 | |
| 2482 | static IrInstruction *ir_instruction_compilelog_get_dep(IrInstructionCompileLog *instruction, size_t index) { | |
| 2483 | if (index < instruction->msg_count) | |
| 2484 | return instruction->msg_list[index]; | |
| 2485 | return nullptr; | |
| 2486 | } | |
| 2487 | ||
| 2464 | 2488 | static IrInstruction *ir_instruction_errname_get_dep(IrInstructionErrName *instruction, size_t index) { |
| 2465 | 2489 | switch (index) { |
| 2466 | 2490 | case 0: return instruction->value; |
| ... | ... | @@ -2848,6 +2872,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 2848 | 2872 | return ir_instruction_maxvalue_get_dep((IrInstructionMaxValue *) instruction, index); |
| 2849 | 2873 | case IrInstructionIdCompileErr: |
| 2850 | 2874 | return ir_instruction_compileerr_get_dep((IrInstructionCompileErr *) instruction, index); |
| 2875 | case IrInstructionIdCompileLog: | |
| 2876 | return ir_instruction_compilelog_get_dep((IrInstructionCompileLog *) instruction, index); | |
| 2851 | 2877 | case IrInstructionIdErrName: |
| 2852 | 2878 | return ir_instruction_errname_get_dep((IrInstructionErrName *) instruction, index); |
| 2853 | 2879 | case IrInstructionIdEmbedFile: |
| ... | ... | @@ -3767,7 +3793,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3767 | 3793 | BuiltinFnEntry *builtin_fn = entry->value; |
| 3768 | 3794 | size_t actual_param_count = node->data.fn_call_expr.params.length; |
| 3769 | 3795 | |
| 3770 | if (builtin_fn->param_count != actual_param_count) { | |
| 3796 | if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) { | |
| 3771 | 3797 | add_node_error(irb->codegen, node, |
| 3772 | 3798 | buf_sprintf("expected %zu arguments, found %zu", |
| 3773 | 3799 | builtin_fn->param_count, actual_param_count)); |
| ... | ... | @@ -3958,6 +3984,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3958 | 3984 | |
| 3959 | 3985 | return ir_build_compile_err(irb, scope, node, arg0_value); |
| 3960 | 3986 | } |
| 3987 | case BuiltinFnIdCompileLog: | |
| 3988 | { | |
| 3989 | IrInstruction **args = allocate<IrInstruction*>(actual_param_count); | |
| 3990 | ||
| 3991 | for (size_t i = 0; i < actual_param_count; i += 1) { | |
| 3992 | AstNode *arg_node = node->data.fn_call_expr.params.at(i); | |
| 3993 | args[i] = ir_gen_node(irb, arg_node, scope); | |
| 3994 | if (args[i] == irb->codegen->invalid_instruction) | |
| 3995 | return irb->codegen->invalid_instruction; | |
| 3996 | } | |
| 3997 | ||
| 3998 | return ir_build_compile_log(irb, scope, node, actual_param_count, args); | |
| 3999 | } | |
| 3961 | 4000 | case BuiltinFnIdErrName: |
| 3962 | 4001 | { |
| 3963 | 4002 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -5254,7 +5293,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) |
| 5254 | 5293 | return irb->codegen->invalid_instruction; |
| 5255 | 5294 | |
| 5256 | 5295 | IrInstruction *start_value = ir_gen_node(irb, start_node, scope); |
| 5257 | if (ptr_value == irb->codegen->invalid_instruction) | |
| 5296 | if (start_value == irb->codegen->invalid_instruction) | |
| 5258 | 5297 | return irb->codegen->invalid_instruction; |
| 5259 | 5298 | |
| 5260 | 5299 | IrInstruction *end_value; |
| ... | ... | @@ -5800,6 +5839,16 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, |
| 5800 | 5839 | } |
| 5801 | 5840 | } |
| 5802 | 5841 | |
| 5842 | // implicit typed number to integer or float literal. | |
| 5843 | // works when the number is known | |
| 5844 | if (value->value.special == ConstValSpecialStatic) { | |
| 5845 | if (actual_type->id == TypeTableEntryIdInt && expected_type->id == TypeTableEntryIdNumLitInt) { | |
| 5846 | return ImplicitCastMatchResultYes; | |
| 5847 | } else if (actual_type->id == TypeTableEntryIdFloat && expected_type->id == TypeTableEntryIdNumLitFloat) { | |
| 5848 | return ImplicitCastMatchResultYes; | |
| 5849 | } | |
| 5850 | } | |
| 5851 | ||
| 5803 | 5852 | // implicit undefined literal to anything |
| 5804 | 5853 | if (actual_type->id == TypeTableEntryIdUndefLit) { |
| 5805 | 5854 | return ImplicitCastMatchResultYes; |
| ... | ... | @@ -6654,6 +6703,19 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour |
| 6654 | 6703 | return result; |
| 6655 | 6704 | } |
| 6656 | 6705 | |
| 6706 | static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction *source_instr, | |
| 6707 | IrInstruction *target, TypeTableEntry *wanted_type) | |
| 6708 | { | |
| 6709 | ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); | |
| 6710 | if (!val) | |
| 6711 | return ira->codegen->invalid_instruction; | |
| 6712 | ||
| 6713 | IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, | |
| 6714 | source_instr->source_node, wanted_type, true); | |
| 6715 | bignum_init_bignum(&result->value.data.x_bignum, &val->data.x_bignum); | |
| 6716 | return result; | |
| 6717 | } | |
| 6718 | ||
| 6657 | 6719 | static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, |
| 6658 | 6720 | TypeTableEntry *wanted_type, IrInstruction *value) |
| 6659 | 6721 | { |
| ... | ... | @@ -6858,6 +6920,15 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 6858 | 6920 | } |
| 6859 | 6921 | } |
| 6860 | 6922 | |
| 6923 | // explicit cast from typed number to integer or float literal. | |
| 6924 | // works when the number is known at compile time | |
| 6925 | if (instr_is_comptime(value) && | |
| 6926 | ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdNumLitInt) || | |
| 6927 | (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdNumLitFloat))) | |
| 6928 | { | |
| 6929 | return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); | |
| 6930 | } | |
| 6931 | ||
| 6861 | 6932 | // explicit cast from %void to integer type which can fit it |
| 6862 | 6933 | bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion && |
| 6863 | 6934 | !type_has_bits(actual_type->data.error.child_type); |
| ... | ... | @@ -7552,6 +7623,13 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * |
| 7552 | 7623 | op1_array_val = op1_val->data.x_ptr.base_ptr; |
| 7553 | 7624 | op1_array_index = op1_val->data.x_ptr.index; |
| 7554 | 7625 | op1_array_end = op1_array_val->data.x_array.size - 1; |
| 7626 | } else if (is_slice(op1_canon_type)) { | |
| 7627 | TypeTableEntry *ptr_type = op1_canon_type->data.structure.fields[slice_ptr_index].type_entry; | |
| 7628 | child_type = ptr_type->data.pointer.child_type; | |
| 7629 | ConstExprValue *ptr_val = &op1_val->data.x_struct.fields[slice_ptr_index]; | |
| 7630 | op1_array_val = ptr_val->data.x_ptr.base_ptr; | |
| 7631 | op1_array_index = ptr_val->data.x_ptr.index; | |
| 7632 | op1_array_end = op1_array_val->data.x_array.size; | |
| 7555 | 7633 | } else { |
| 7556 | 7634 | ir_add_error(ira, op1, |
| 7557 | 7635 | buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name))); |
| ... | ... | @@ -7585,6 +7663,18 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * |
| 7585 | 7663 | op2_array_val = op2_val->data.x_ptr.base_ptr; |
| 7586 | 7664 | op2_array_index = op2_val->data.x_ptr.index; |
| 7587 | 7665 | op2_array_end = op2_array_val->data.x_array.size - 1; |
| 7666 | } else if (is_slice(op2_canon_type)) { | |
| 7667 | TypeTableEntry *ptr_type = op2_canon_type->data.structure.fields[slice_ptr_index].type_entry; | |
| 7668 | if (ptr_type->data.pointer.child_type != child_type) { | |
| 7669 | ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", | |
| 7670 | buf_ptr(&child_type->name), | |
| 7671 | buf_ptr(&op2->value.type->name))); | |
| 7672 | return ira->codegen->builtin_types.entry_invalid; | |
| 7673 | } | |
| 7674 | ConstExprValue *ptr_val = &op2_val->data.x_struct.fields[slice_ptr_index]; | |
| 7675 | op2_array_val = ptr_val->data.x_ptr.base_ptr; | |
| 7676 | op2_array_index = ptr_val->data.x_ptr.index; | |
| 7677 | op2_array_end = op2_array_val->data.x_array.size; | |
| 7588 | 7678 | } else { |
| 7589 | 7679 | ir_add_error(ira, op2, |
| 7590 | 7680 | buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name))); |
| ... | ... | @@ -9177,7 +9267,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru |
| 9177 | 9267 | } |
| 9178 | 9268 | } else { |
| 9179 | 9269 | ir_add_error(ira, &field_ptr_instruction->base, |
| 9180 | buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); | |
| 9270 | buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name))); | |
| 9181 | 9271 | return ira->codegen->builtin_types.entry_invalid; |
| 9182 | 9272 | } |
| 9183 | 9273 | } else if (container_type->id == TypeTableEntryIdNamespace) { |
| ... | ... | @@ -9528,6 +9618,12 @@ static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira, |
| 9528 | 9618 | if (!target_val) |
| 9529 | 9619 | return ira->codegen->builtin_types.entry_invalid; |
| 9530 | 9620 | |
| 9621 | if (ira->new_irb.exec->is_inline) { | |
| 9622 | // ignore setDebugSafety when running functions at compile time | |
| 9623 | ir_build_const_from(ira, &set_debug_safety_instruction->base, false); | |
| 9624 | return ira->codegen->builtin_types.entry_void; | |
| 9625 | } | |
| 9626 | ||
| 9531 | 9627 | bool *safety_off_ptr; |
| 9532 | 9628 | AstNode **safety_set_node_ptr; |
| 9533 | 9629 | if (target_type->id == TypeTableEntryIdBlock) { |
| ... | ... | @@ -10703,6 +10799,26 @@ static TypeTableEntry *ir_analyze_instruction_compile_err(IrAnalyze *ira, |
| 10703 | 10799 | return ira->codegen->builtin_types.entry_invalid; |
| 10704 | 10800 | } |
| 10705 | 10801 | |
| 10802 | static TypeTableEntry *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstructionCompileLog *instruction) { | |
| 10803 | Buf buf = BUF_INIT; | |
| 10804 | fprintf(stderr, "| "); | |
| 10805 | for (size_t i = 0; i < instruction->msg_count; i += 1) { | |
| 10806 | IrInstruction *msg = instruction->msg_list[i]->other; | |
| 10807 | if (msg->value.type->id == TypeTableEntryIdInvalid) | |
| 10808 | return ira->codegen->builtin_types.entry_invalid; | |
| 10809 | buf_resize(&buf, 0); | |
| 10810 | render_const_value(&buf, &msg->value); | |
| 10811 | const char *comma_str = (i != 0) ? ", " : ""; | |
| 10812 | fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf)); | |
| 10813 | } | |
| 10814 | fprintf(stderr, "\n"); | |
| 10815 | ||
| 10816 | ir_add_error(ira, &instruction->base, buf_sprintf("found compile log statement")); | |
| 10817 | ||
| 10818 | ir_build_const_from(ira, &instruction->base, false); | |
| 10819 | return ira->codegen->builtin_types.entry_void; | |
| 10820 | } | |
| 10821 | ||
| 10706 | 10822 | static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErrName *instruction) { |
| 10707 | 10823 | IrInstruction *value = instruction->value->other; |
| 10708 | 10824 | if (value->value.type->id == TypeTableEntryIdInvalid) |
| ... | ... | @@ -11602,13 +11718,13 @@ static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInst |
| 11602 | 11718 | out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); |
| 11603 | 11719 | break; |
| 11604 | 11720 | case IrOverflowOpSub: |
| 11605 | out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); | |
| 11721 | out_val->data.x_bool = bignum_sub(dest_bignum, op1_bignum, op2_bignum); | |
| 11606 | 11722 | break; |
| 11607 | 11723 | case IrOverflowOpMul: |
| 11608 | out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); | |
| 11724 | out_val->data.x_bool = bignum_mul(dest_bignum, op1_bignum, op2_bignum); | |
| 11609 | 11725 | break; |
| 11610 | 11726 | case IrOverflowOpShl: |
| 11611 | out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); | |
| 11727 | out_val->data.x_bool = bignum_shl(dest_bignum, op1_bignum, op2_bignum); | |
| 11612 | 11728 | break; |
| 11613 | 11729 | } |
| 11614 | 11730 | if (!bignum_fits_in_bits(dest_bignum, canon_type->data.integral.bit_count, |
| ... | ... | @@ -12007,6 +12123,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 12007 | 12123 | return ir_analyze_instruction_max_value(ira, (IrInstructionMaxValue *)instruction); |
| 12008 | 12124 | case IrInstructionIdCompileErr: |
| 12009 | 12125 | return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction); |
| 12126 | case IrInstructionIdCompileLog: | |
| 12127 | return ir_analyze_instruction_compile_log(ira, (IrInstructionCompileLog *)instruction); | |
| 12010 | 12128 | case IrInstructionIdErrName: |
| 12011 | 12129 | return ir_analyze_instruction_err_name(ira, (IrInstructionErrName *)instruction); |
| 12012 | 12130 | case IrInstructionIdTypeName: |
| ... | ... | @@ -12164,6 +12282,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 12164 | 12282 | case IrInstructionIdSetDebugSafety: |
| 12165 | 12283 | case IrInstructionIdImport: |
| 12166 | 12284 | case IrInstructionIdCompileErr: |
| 12285 | case IrInstructionIdCompileLog: | |
| 12167 | 12286 | case IrInstructionIdCImport: |
| 12168 | 12287 | case IrInstructionIdCInclude: |
| 12169 | 12288 | case IrInstructionIdCDefine: |
src/ir_print.cpp+14| ... | ... | @@ -532,6 +532,17 @@ static void ir_print_compile_err(IrPrint *irp, IrInstructionCompileErr *instruct |
| 532 | 532 | fprintf(irp->f, ")"); |
| 533 | 533 | } |
| 534 | 534 | |
| 535 | static void ir_print_compile_log(IrPrint *irp, IrInstructionCompileLog *instruction) { | |
| 536 | fprintf(irp->f, "@compileLog("); | |
| 537 | for (size_t i = 0; i < instruction->msg_count; i += 1) { | |
| 538 | if (i != 0) | |
| 539 | fprintf(irp->f, ","); | |
| 540 | IrInstruction *msg = instruction->msg_list[i]; | |
| 541 | ir_print_other_instruction(irp, msg); | |
| 542 | } | |
| 543 | fprintf(irp->f, ")"); | |
| 544 | } | |
| 545 | ||
| 535 | 546 | static void ir_print_err_name(IrPrint *irp, IrInstructionErrName *instruction) { |
| 536 | 547 | fprintf(irp->f, "@errorName("); |
| 537 | 548 | ir_print_other_instruction(irp, instruction->value); |
| ... | ... | @@ -990,6 +1001,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 990 | 1001 | case IrInstructionIdCompileErr: |
| 991 | 1002 | ir_print_compile_err(irp, (IrInstructionCompileErr *)instruction); |
| 992 | 1003 | break; |
| 1004 | case IrInstructionIdCompileLog: | |
| 1005 | ir_print_compile_log(irp, (IrInstructionCompileLog *)instruction); | |
| 1006 | break; | |
| 993 | 1007 | case IrInstructionIdErrName: |
| 994 | 1008 | ir_print_err_name(irp, (IrInstructionErrName *)instruction); |
| 995 | 1009 | break; |
std/builtin.zig+5| ... | ... | @@ -29,3 +29,8 @@ export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) { |
| 29 | 29 | while (index != n; index += 1) |
| 30 | 30 | d[index] = s[index]; |
| 31 | 31 | } |
| 32 | ||
| 33 | // Avoid dragging in the debug safety mechanisms into this .o file. | |
| 34 | pub fn panic(message: []const u8) -> unreachable { | |
| 35 | @unreachable(); | |
| 36 | } |
std/compiler_rt.zig+261-2| ... | ... | @@ -1,3 +1,13 @@ |
| 1 | // Avoid dragging in the debug safety mechanisms into this .o file, | |
| 2 | // unless we're trying to test this file. | |
| 3 | pub fn panic(message: []const u8) -> unreachable { | |
| 4 | if (@compileVar("is_test")) { | |
| 5 | @import("std").debug.panic(message); | |
| 6 | } else { | |
| 7 | @unreachable(); | |
| 8 | } | |
| 9 | } | |
| 10 | ||
| 1 | 11 | const CHAR_BIT = 8; |
| 2 | 12 | const du_int = u64; |
| 3 | 13 | const di_int = i64; |
| ... | ... | @@ -212,6 +222,106 @@ export fn __umoddi3(a: du_int, b: du_int) -> du_int { |
| 212 | 222 | return r; |
| 213 | 223 | } |
| 214 | 224 | |
| 225 | fn isArmArch() -> bool { | |
| 226 | return switch (@compileVar("arch")) { | |
| 227 | Arch.armv8_2a, | |
| 228 | Arch.armv8_1a, | |
| 229 | Arch.armv8, | |
| 230 | Arch.armv8m_baseline, | |
| 231 | Arch.armv8m_mainline, | |
| 232 | Arch.armv7, | |
| 233 | Arch.armv7em, | |
| 234 | Arch.armv7m, | |
| 235 | Arch.armv7s, | |
| 236 | Arch.armv7k, | |
| 237 | Arch.armv6, | |
| 238 | Arch.armv6m, | |
| 239 | Arch.armv6k, | |
| 240 | Arch.armv6t2, | |
| 241 | Arch.armv5, | |
| 242 | Arch.armv5te, | |
| 243 | Arch.armv4t, | |
| 244 | Arch.armeb => true, | |
| 245 | else => false, | |
| 246 | }; | |
| 247 | } | |
| 248 | ||
| 249 | export nakedcc fn __aeabi_uidivmod() { | |
| 250 | @setDebugSafety(this, false); | |
| 251 | ||
| 252 | if (comptime isArmArch()) { | |
| 253 | asm volatile ( | |
| 254 | \\ push { lr } | |
| 255 | \\ sub sp, sp, #4 | |
| 256 | \\ mov r2, sp | |
| 257 | \\ bl __udivmodsi4 | |
| 258 | \\ ldr r1, [sp] | |
| 259 | \\ add sp, sp, #4 | |
| 260 | \\ pop { pc } | |
| 261 | ::: "r2", "r1"); | |
| 262 | @unreachable(); | |
| 263 | } | |
| 264 | ||
| 265 | @setFnVisible(this, false); | |
| 266 | } | |
| 267 | ||
| 268 | export fn __udivmodsi4(a: su_int, b: su_int, rem: &su_int) -> su_int { | |
| 269 | @setDebugSafety(this, false); | |
| 270 | ||
| 271 | const d = __udivsi3(a, b); | |
| 272 | *rem = su_int(si_int(a) -% (si_int(d) * si_int(b))); | |
| 273 | return d; | |
| 274 | } | |
| 275 | ||
| 276 | ||
| 277 | // TODO make this an alias instead of an extra function call | |
| 278 | // https://github.com/andrewrk/zig/issues/256 | |
| 279 | ||
| 280 | export fn __aeabi_uidiv(n: su_int, d: su_int) -> su_int { | |
| 281 | @setDebugSafety(this, false); | |
| 282 | ||
| 283 | return __udivsi3(n, d); | |
| 284 | } | |
| 285 | ||
| 286 | export fn __udivsi3(n: su_int, d: su_int) -> su_int { | |
| 287 | @setDebugSafety(this, false); | |
| 288 | ||
| 289 | const n_uword_bits: c_uint = @sizeOf(su_int) * CHAR_BIT; | |
| 290 | // special cases | |
| 291 | if (d == 0) | |
| 292 | return 0; // ?! | |
| 293 | if (n == 0) | |
| 294 | return 0; | |
| 295 | var sr: c_uint = @clz(d) - @clz(n); | |
| 296 | // 0 <= sr <= n_uword_bits - 1 or sr large | |
| 297 | if (sr > n_uword_bits - 1) // d > r | |
| 298 | return 0; | |
| 299 | if (sr == n_uword_bits - 1) // d == 1 | |
| 300 | return n; | |
| 301 | sr += 1; | |
| 302 | // 1 <= sr <= n_uword_bits - 1 | |
| 303 | // Not a special case | |
| 304 | var q: su_int = n << (n_uword_bits - sr); | |
| 305 | var r: su_int = n >> sr; | |
| 306 | var carry: su_int = 0; | |
| 307 | while (sr > 0; sr -= 1) { | |
| 308 | // r:q = ((r:q) << 1) | carry | |
| 309 | r = (r << 1) | (q >> (n_uword_bits - 1)); | |
| 310 | q = (q << 1) | carry; | |
| 311 | // carry = 0; | |
| 312 | // if (r.all >= d.all) | |
| 313 | // { | |
| 314 | // r.all -= d.all; | |
| 315 | // carry = 1; | |
| 316 | // } | |
| 317 | const s = si_int(d - r - 1) >> si_int(n_uword_bits - 1); | |
| 318 | carry = su_int(s & 1); | |
| 319 | r -= d & su_int(s); | |
| 320 | } | |
| 321 | q = (q << 1) | carry; | |
| 322 | return q; | |
| 323 | } | |
| 324 | ||
| 215 | 325 | fn test_umoddi3() { |
| 216 | 326 | @setFnTest(this); |
| 217 | 327 | |
| ... | ... | @@ -257,6 +367,155 @@ fn test_one_udivmoddi4(a: du_int, b: du_int, expected_q: du_int, expected_r: du_ |
| 257 | 367 | assert(r == expected_r); |
| 258 | 368 | } |
| 259 | 369 | |
| 260 | fn assert(b: bool) { | |
| 261 | if (!b) @unreachable(); | |
| 370 | fn test_udivsi3() { | |
| 371 | @setFnTest(this); | |
| 372 | ||
| 373 | const cases = [][3]su_int { | |
| 374 | []su_int{0x00000000, 0x00000001, 0x00000000}, | |
| 375 | []su_int{0x00000000, 0x00000002, 0x00000000}, | |
| 376 | []su_int{0x00000000, 0x00000003, 0x00000000}, | |
| 377 | []su_int{0x00000000, 0x00000010, 0x00000000}, | |
| 378 | []su_int{0x00000000, 0x078644FA, 0x00000000}, | |
| 379 | []su_int{0x00000000, 0x0747AE14, 0x00000000}, | |
| 380 | []su_int{0x00000000, 0x7FFFFFFF, 0x00000000}, | |
| 381 | []su_int{0x00000000, 0x80000000, 0x00000000}, | |
| 382 | []su_int{0x00000000, 0xFFFFFFFD, 0x00000000}, | |
| 383 | []su_int{0x00000000, 0xFFFFFFFE, 0x00000000}, | |
| 384 | []su_int{0x00000000, 0xFFFFFFFF, 0x00000000}, | |
| 385 | []su_int{0x00000001, 0x00000001, 0x00000001}, | |
| 386 | []su_int{0x00000001, 0x00000002, 0x00000000}, | |
| 387 | []su_int{0x00000001, 0x00000003, 0x00000000}, | |
| 388 | []su_int{0x00000001, 0x00000010, 0x00000000}, | |
| 389 | []su_int{0x00000001, 0x078644FA, 0x00000000}, | |
| 390 | []su_int{0x00000001, 0x0747AE14, 0x00000000}, | |
| 391 | []su_int{0x00000001, 0x7FFFFFFF, 0x00000000}, | |
| 392 | []su_int{0x00000001, 0x80000000, 0x00000000}, | |
| 393 | []su_int{0x00000001, 0xFFFFFFFD, 0x00000000}, | |
| 394 | []su_int{0x00000001, 0xFFFFFFFE, 0x00000000}, | |
| 395 | []su_int{0x00000001, 0xFFFFFFFF, 0x00000000}, | |
| 396 | []su_int{0x00000002, 0x00000001, 0x00000002}, | |
| 397 | []su_int{0x00000002, 0x00000002, 0x00000001}, | |
| 398 | []su_int{0x00000002, 0x00000003, 0x00000000}, | |
| 399 | []su_int{0x00000002, 0x00000010, 0x00000000}, | |
| 400 | []su_int{0x00000002, 0x078644FA, 0x00000000}, | |
| 401 | []su_int{0x00000002, 0x0747AE14, 0x00000000}, | |
| 402 | []su_int{0x00000002, 0x7FFFFFFF, 0x00000000}, | |
| 403 | []su_int{0x00000002, 0x80000000, 0x00000000}, | |
| 404 | []su_int{0x00000002, 0xFFFFFFFD, 0x00000000}, | |
| 405 | []su_int{0x00000002, 0xFFFFFFFE, 0x00000000}, | |
| 406 | []su_int{0x00000002, 0xFFFFFFFF, 0x00000000}, | |
| 407 | []su_int{0x00000003, 0x00000001, 0x00000003}, | |
| 408 | []su_int{0x00000003, 0x00000002, 0x00000001}, | |
| 409 | []su_int{0x00000003, 0x00000003, 0x00000001}, | |
| 410 | []su_int{0x00000003, 0x00000010, 0x00000000}, | |
| 411 | []su_int{0x00000003, 0x078644FA, 0x00000000}, | |
| 412 | []su_int{0x00000003, 0x0747AE14, 0x00000000}, | |
| 413 | []su_int{0x00000003, 0x7FFFFFFF, 0x00000000}, | |
| 414 | []su_int{0x00000003, 0x80000000, 0x00000000}, | |
| 415 | []su_int{0x00000003, 0xFFFFFFFD, 0x00000000}, | |
| 416 | []su_int{0x00000003, 0xFFFFFFFE, 0x00000000}, | |
| 417 | []su_int{0x00000003, 0xFFFFFFFF, 0x00000000}, | |
| 418 | []su_int{0x00000010, 0x00000001, 0x00000010}, | |
| 419 | []su_int{0x00000010, 0x00000002, 0x00000008}, | |
| 420 | []su_int{0x00000010, 0x00000003, 0x00000005}, | |
| 421 | []su_int{0x00000010, 0x00000010, 0x00000001}, | |
| 422 | []su_int{0x00000010, 0x078644FA, 0x00000000}, | |
| 423 | []su_int{0x00000010, 0x0747AE14, 0x00000000}, | |
| 424 | []su_int{0x00000010, 0x7FFFFFFF, 0x00000000}, | |
| 425 | []su_int{0x00000010, 0x80000000, 0x00000000}, | |
| 426 | []su_int{0x00000010, 0xFFFFFFFD, 0x00000000}, | |
| 427 | []su_int{0x00000010, 0xFFFFFFFE, 0x00000000}, | |
| 428 | []su_int{0x00000010, 0xFFFFFFFF, 0x00000000}, | |
| 429 | []su_int{0x078644FA, 0x00000001, 0x078644FA}, | |
| 430 | []su_int{0x078644FA, 0x00000002, 0x03C3227D}, | |
| 431 | []su_int{0x078644FA, 0x00000003, 0x028216FE}, | |
| 432 | []su_int{0x078644FA, 0x00000010, 0x0078644F}, | |
| 433 | []su_int{0x078644FA, 0x078644FA, 0x00000001}, | |
| 434 | []su_int{0x078644FA, 0x0747AE14, 0x00000001}, | |
| 435 | []su_int{0x078644FA, 0x7FFFFFFF, 0x00000000}, | |
| 436 | []su_int{0x078644FA, 0x80000000, 0x00000000}, | |
| 437 | []su_int{0x078644FA, 0xFFFFFFFD, 0x00000000}, | |
| 438 | []su_int{0x078644FA, 0xFFFFFFFE, 0x00000000}, | |
| 439 | []su_int{0x078644FA, 0xFFFFFFFF, 0x00000000}, | |
| 440 | []su_int{0x0747AE14, 0x00000001, 0x0747AE14}, | |
| 441 | []su_int{0x0747AE14, 0x00000002, 0x03A3D70A}, | |
| 442 | []su_int{0x0747AE14, 0x00000003, 0x026D3A06}, | |
| 443 | []su_int{0x0747AE14, 0x00000010, 0x00747AE1}, | |
| 444 | []su_int{0x0747AE14, 0x078644FA, 0x00000000}, | |
| 445 | []su_int{0x0747AE14, 0x0747AE14, 0x00000001}, | |
| 446 | []su_int{0x0747AE14, 0x7FFFFFFF, 0x00000000}, | |
| 447 | []su_int{0x0747AE14, 0x80000000, 0x00000000}, | |
| 448 | []su_int{0x0747AE14, 0xFFFFFFFD, 0x00000000}, | |
| 449 | []su_int{0x0747AE14, 0xFFFFFFFE, 0x00000000}, | |
| 450 | []su_int{0x0747AE14, 0xFFFFFFFF, 0x00000000}, | |
| 451 | []su_int{0x7FFFFFFF, 0x00000001, 0x7FFFFFFF}, | |
| 452 | []su_int{0x7FFFFFFF, 0x00000002, 0x3FFFFFFF}, | |
| 453 | []su_int{0x7FFFFFFF, 0x00000003, 0x2AAAAAAA}, | |
| 454 | []su_int{0x7FFFFFFF, 0x00000010, 0x07FFFFFF}, | |
| 455 | []su_int{0x7FFFFFFF, 0x078644FA, 0x00000011}, | |
| 456 | []su_int{0x7FFFFFFF, 0x0747AE14, 0x00000011}, | |
| 457 | []su_int{0x7FFFFFFF, 0x7FFFFFFF, 0x00000001}, | |
| 458 | []su_int{0x7FFFFFFF, 0x80000000, 0x00000000}, | |
| 459 | []su_int{0x7FFFFFFF, 0xFFFFFFFD, 0x00000000}, | |
| 460 | []su_int{0x7FFFFFFF, 0xFFFFFFFE, 0x00000000}, | |
| 461 | []su_int{0x7FFFFFFF, 0xFFFFFFFF, 0x00000000}, | |
| 462 | []su_int{0x80000000, 0x00000001, 0x80000000}, | |
| 463 | []su_int{0x80000000, 0x00000002, 0x40000000}, | |
| 464 | []su_int{0x80000000, 0x00000003, 0x2AAAAAAA}, | |
| 465 | []su_int{0x80000000, 0x00000010, 0x08000000}, | |
| 466 | []su_int{0x80000000, 0x078644FA, 0x00000011}, | |
| 467 | []su_int{0x80000000, 0x0747AE14, 0x00000011}, | |
| 468 | []su_int{0x80000000, 0x7FFFFFFF, 0x00000001}, | |
| 469 | []su_int{0x80000000, 0x80000000, 0x00000001}, | |
| 470 | []su_int{0x80000000, 0xFFFFFFFD, 0x00000000}, | |
| 471 | []su_int{0x80000000, 0xFFFFFFFE, 0x00000000}, | |
| 472 | []su_int{0x80000000, 0xFFFFFFFF, 0x00000000}, | |
| 473 | []su_int{0xFFFFFFFD, 0x00000001, 0xFFFFFFFD}, | |
| 474 | []su_int{0xFFFFFFFD, 0x00000002, 0x7FFFFFFE}, | |
| 475 | []su_int{0xFFFFFFFD, 0x00000003, 0x55555554}, | |
| 476 | []su_int{0xFFFFFFFD, 0x00000010, 0x0FFFFFFF}, | |
| 477 | []su_int{0xFFFFFFFD, 0x078644FA, 0x00000022}, | |
| 478 | []su_int{0xFFFFFFFD, 0x0747AE14, 0x00000023}, | |
| 479 | []su_int{0xFFFFFFFD, 0x7FFFFFFF, 0x00000001}, | |
| 480 | []su_int{0xFFFFFFFD, 0x80000000, 0x00000001}, | |
| 481 | []su_int{0xFFFFFFFD, 0xFFFFFFFD, 0x00000001}, | |
| 482 | []su_int{0xFFFFFFFD, 0xFFFFFFFE, 0x00000000}, | |
| 483 | []su_int{0xFFFFFFFD, 0xFFFFFFFF, 0x00000000}, | |
| 484 | []su_int{0xFFFFFFFE, 0x00000001, 0xFFFFFFFE}, | |
| 485 | []su_int{0xFFFFFFFE, 0x00000002, 0x7FFFFFFF}, | |
| 486 | []su_int{0xFFFFFFFE, 0x00000003, 0x55555554}, | |
| 487 | []su_int{0xFFFFFFFE, 0x00000010, 0x0FFFFFFF}, | |
| 488 | []su_int{0xFFFFFFFE, 0x078644FA, 0x00000022}, | |
| 489 | []su_int{0xFFFFFFFE, 0x0747AE14, 0x00000023}, | |
| 490 | []su_int{0xFFFFFFFE, 0x7FFFFFFF, 0x00000002}, | |
| 491 | []su_int{0xFFFFFFFE, 0x80000000, 0x00000001}, | |
| 492 | []su_int{0xFFFFFFFE, 0xFFFFFFFD, 0x00000001}, | |
| 493 | []su_int{0xFFFFFFFE, 0xFFFFFFFE, 0x00000001}, | |
| 494 | []su_int{0xFFFFFFFE, 0xFFFFFFFF, 0x00000000}, | |
| 495 | []su_int{0xFFFFFFFF, 0x00000001, 0xFFFFFFFF}, | |
| 496 | []su_int{0xFFFFFFFF, 0x00000002, 0x7FFFFFFF}, | |
| 497 | []su_int{0xFFFFFFFF, 0x00000003, 0x55555555}, | |
| 498 | []su_int{0xFFFFFFFF, 0x00000010, 0x0FFFFFFF}, | |
| 499 | []su_int{0xFFFFFFFF, 0x078644FA, 0x00000022}, | |
| 500 | []su_int{0xFFFFFFFF, 0x0747AE14, 0x00000023}, | |
| 501 | []su_int{0xFFFFFFFF, 0x7FFFFFFF, 0x00000002}, | |
| 502 | []su_int{0xFFFFFFFF, 0x80000000, 0x00000001}, | |
| 503 | []su_int{0xFFFFFFFF, 0xFFFFFFFD, 0x00000001}, | |
| 504 | []su_int{0xFFFFFFFF, 0xFFFFFFFE, 0x00000001}, | |
| 505 | []su_int{0xFFFFFFFF, 0xFFFFFFFF, 0x00000001}, | |
| 506 | }; | |
| 507 | ||
| 508 | for (cases) |case| { | |
| 509 | test_one_udivsi3(case[0], case[1], case[2]); | |
| 510 | } | |
| 511 | } | |
| 512 | ||
| 513 | fn test_one_udivsi3(a: su_int, b: su_int, expected_q: su_int) { | |
| 514 | const q: su_int = __udivsi3(a, b); | |
| 515 | assert(q == expected_q); | |
| 516 | } | |
| 517 | ||
| 518 | ||
| 519 | fn assert(ok: bool) { | |
| 520 | if (!ok) @unreachable(); | |
| 262 | 521 | } |
std/debug.zig+21| ... | ... | @@ -13,6 +13,27 @@ pub fn assert(ok: bool) { |
| 13 | 13 | if (!ok) @unreachable() |
| 14 | 14 | } |
| 15 | 15 | |
| 16 | var panicking = false; | |
| 17 | /// This is the default panic implementation. | |
| 18 | pub coldcc fn panic(message: []const u8) -> unreachable { | |
| 19 | // TODO | |
| 20 | // if (@atomicRmw(AtomicOp.XChg, &panicking, true, AtomicOrder.SeqCst)) { } | |
| 21 | if (panicking) { | |
| 22 | // Panicked during a panic. | |
| 23 | // TODO detect if a different thread caused the panic, because in that case | |
| 24 | // we would want to return here instead of calling abort, so that the thread | |
| 25 | // which first called panic can finish printing a stack trace. | |
| 26 | os.abort(); | |
| 27 | } else { | |
| 28 | panicking = true; | |
| 29 | } | |
| 30 | ||
| 31 | %%io.stderr.printf("{}\n", message); | |
| 32 | %%printStackTrace(); | |
| 33 | ||
| 34 | os.abort(); | |
| 35 | } | |
| 36 | ||
| 16 | 37 | pub fn printStackTrace() -> %void { |
| 17 | 38 | %return writeStackTrace(&io.stderr); |
| 18 | 39 | %return io.stderr.flush(); |
std/io.zig+94-40| ... | ... | @@ -100,14 +100,20 @@ pub const OutStream = struct { |
| 100 | 100 | Start, |
| 101 | 101 | OpenBrace, |
| 102 | 102 | CloseBrace, |
| 103 | Hex: bool, | |
| 103 | Integer, | |
| 104 | IntegerWidth, | |
| 104 | 105 | }; |
| 105 | 106 | |
| 106 | 107 | /// Calls print and then flushes the buffer. |
| 107 | 108 | pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void { |
| 108 | comptime var start_index: usize = 0; | |
| 109 | comptime var start_index = 0; | |
| 109 | 110 | comptime var state = State.Start; |
| 110 | comptime var next_arg: usize = 0; | |
| 111 | comptime var next_arg = 0; | |
| 112 | comptime var radix = 0; | |
| 113 | comptime var uppercase = false; | |
| 114 | comptime var width = 0; | |
| 115 | comptime var width_start = 0; | |
| 116 | ||
| 111 | 117 | inline for (format) |c, i| { |
| 112 | 118 | switch (state) { |
| 113 | 119 | State.Start => switch (c) { |
| ... | ... | @@ -132,11 +138,23 @@ pub const OutStream = struct { |
| 132 | 138 | state = State.Start; |
| 133 | 139 | start_index = i + 1; |
| 134 | 140 | }, |
| 141 | 'd' => { | |
| 142 | radix = 10; | |
| 143 | uppercase = false; | |
| 144 | width = 0; | |
| 145 | state = State.Integer; | |
| 146 | }, | |
| 135 | 147 | 'x' => { |
| 136 | state = State.Hex { false }; | |
| 148 | radix = 16; | |
| 149 | uppercase = false; | |
| 150 | width = 0; | |
| 151 | state = State.Integer; | |
| 137 | 152 | }, |
| 138 | 153 | 'X' => { |
| 139 | state = State.Hex { true }; | |
| 154 | radix = 16; | |
| 155 | uppercase = true; | |
| 156 | width = 0; | |
| 157 | state = State.Integer; | |
| 140 | 158 | }, |
| 141 | 159 | else => @compileError("Unknown format character: " ++ c), |
| 142 | 160 | }, |
| ... | ... | @@ -147,14 +165,29 @@ pub const OutStream = struct { |
| 147 | 165 | }, |
| 148 | 166 | else => @compileError("Single '}' encountered in format string"), |
| 149 | 167 | }, |
| 150 | State.Hex => |uppercase| switch (c) { | |
| 168 | State.Integer => switch (c) { | |
| 169 | '}' => { | |
| 170 | self.printInt(args[next_arg], radix, uppercase, width); | |
| 171 | next_arg += 1; | |
| 172 | state = State.Start; | |
| 173 | start_index = i + 1; | |
| 174 | }, | |
| 175 | '0' ... '9' => { | |
| 176 | width_start = i; | |
| 177 | state = State.IntegerWidth; | |
| 178 | }, | |
| 179 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), | |
| 180 | }, | |
| 181 | State.IntegerWidth => switch (c) { | |
| 151 | 182 | '}' => { |
| 152 | self.printInt(args[next_arg], 16, uppercase); | |
| 183 | width = comptime %%parseUnsigned(usize, format[width_start...i], 10); | |
| 184 | self.printInt(args[next_arg], radix, uppercase, width); | |
| 153 | 185 | next_arg += 1; |
| 154 | 186 | state = State.Start; |
| 155 | 187 | start_index = i + 1; |
| 156 | 188 | }, |
| 157 | else => @compileError("Expected '}' after 'x'/'X' in format string"), | |
| 189 | '0' ... '9' => {}, | |
| 190 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), | |
| 158 | 191 | }, |
| 159 | 192 | } |
| 160 | 193 | } |
| ... | ... | @@ -162,10 +195,8 @@ pub const OutStream = struct { |
| 162 | 195 | if (args.len != next_arg) { |
| 163 | 196 | @compileError("Unused arguments"); |
| 164 | 197 | } |
| 165 | // TODO https://github.com/andrewrk/zig/issues/253 | |
| 166 | switch (state) { | |
| 167 | State.Start => {}, | |
| 168 | else => @compileError("Incomplete format string: " ++ format), | |
| 198 | if (state != State.Start) { | |
| 199 | @compileError("Incomplete format string: " ++ format); | |
| 169 | 200 | } |
| 170 | 201 | } |
| 171 | 202 | if (start_index < format.len) { |
| ... | ... | @@ -177,7 +208,7 @@ pub const OutStream = struct { |
| 177 | 208 | pub fn printValue(self: &OutStream, value: var) -> %void { |
| 178 | 209 | const T = @typeOf(value); |
| 179 | 210 | if (@isInteger(T)) { |
| 180 | return self.printInt(value, 10, false); | |
| 211 | return self.printInt(value, 10, false, 0); | |
| 181 | 212 | } else if (@isFloat(T)) { |
| 182 | 213 | return self.printFloat(T, value); |
| 183 | 214 | } else if (@canImplicitCast([]const u8, value)) { |
| ... | ... | @@ -190,11 +221,11 @@ pub const OutStream = struct { |
| 190 | 221 | } |
| 191 | 222 | } |
| 192 | 223 | |
| 193 | pub fn printInt(self: &OutStream, x: var, base: u8, uppercase: bool) -> %void { | |
| 224 | pub fn printInt(self: &OutStream, x: var, base: u8, uppercase: bool, width: usize) -> %void { | |
| 194 | 225 | if (self.index + max_int_digits >= self.buffer.len) { |
| 195 | 226 | %return self.flush(); |
| 196 | 227 | } |
| 197 | const amt_printed = bufPrintInt(self.buffer[self.index...], x, base, uppercase); | |
| 228 | const amt_printed = bufPrintInt(self.buffer[self.index...], x, base, uppercase, width); | |
| 198 | 229 | self.index += amt_printed; |
| 199 | 230 | } |
| 200 | 231 | |
| ... | ... | @@ -474,24 +505,29 @@ fn digitToChar(digit: u8, uppercase: bool) -> u8 { |
| 474 | 505 | } |
| 475 | 506 | |
| 476 | 507 | /// Guaranteed to not use more than max_int_digits |
| 477 | pub fn bufPrintInt(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize { | |
| 508 | pub fn bufPrintInt(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { | |
| 478 | 509 | if (@typeOf(x).is_signed) |
| 479 | bufPrintSigned(out_buf, x, base, uppercase) | |
| 510 | bufPrintSigned(out_buf, x, base, uppercase, width) | |
| 480 | 511 | else |
| 481 | bufPrintUnsigned(out_buf, x, base, uppercase) | |
| 512 | bufPrintUnsigned(out_buf, x, base, uppercase, width) | |
| 482 | 513 | } |
| 483 | 514 | |
| 484 | fn bufPrintSigned(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize { | |
| 515 | fn bufPrintSigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { | |
| 485 | 516 | const uint = @intType(false, @typeOf(x).bit_count); |
| 517 | // include the sign in the width | |
| 518 | const new_width = if (width == 0) 0 else (width - 1); | |
| 519 | var new_value: uint = undefined; | |
| 486 | 520 | if (x < 0) { |
| 487 | 521 | out_buf[0] = '-'; |
| 488 | return 1 + bufPrintUnsigned(out_buf[1...], uint(-(x + 1)) + 1, base, uppercase); | |
| 522 | new_value = uint(-(x + 1)) + 1; | |
| 489 | 523 | } else { |
| 490 | return bufPrintUnsigned(out_buf, uint(x), base, uppercase); | |
| 524 | out_buf[0] = '+'; | |
| 525 | new_value = uint(x); | |
| 491 | 526 | } |
| 527 | return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width); | |
| 492 | 528 | } |
| 493 | 529 | |
| 494 | fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize { | |
| 530 | fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { | |
| 495 | 531 | // max_int_digits accounts for the minus sign. when printing an unsigned |
| 496 | 532 | // number we don't need to do that. |
| 497 | 533 | var buf: [max_int_digits - 1]u8 = undefined; |
| ... | ... | @@ -508,18 +544,11 @@ fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize { |
| 508 | 544 | } |
| 509 | 545 | |
| 510 | 546 | const src_buf = buf[index...]; |
| 511 | mem.copy(u8, out_buf, src_buf); | |
| 512 | return src_buf.len; | |
| 513 | } | |
| 514 | ||
| 515 | fn parseU64DigitTooBig() { | |
| 516 | @setFnTest(this); | |
| 547 | const padding = if (width > src_buf.len) (width - src_buf.len) else 0; | |
| 517 | 548 | |
| 518 | parseUnsigned(u64, "123a", 10) %% |err| { | |
| 519 | if (err == error.InvalidChar) return; | |
| 520 | @unreachable(); | |
| 521 | }; | |
| 522 | @unreachable(); | |
| 549 | mem.set(u8, out_buf[0...padding], '0'); | |
| 550 | mem.copy(u8, out_buf[padding...], src_buf); | |
| 551 | return src_buf.len + padding; | |
| 523 | 552 | } |
| 524 | 553 | |
| 525 | 554 | pub fn openSelfExe(stream: &InStream) -> %void { |
| ... | ... | @@ -535,18 +564,43 @@ pub fn openSelfExe(stream: &InStream) -> %void { |
| 535 | 564 | } |
| 536 | 565 | } |
| 537 | 566 | |
| 538 | fn bufPrintIntToSlice(buf: []u8, x: var, base: u8, uppercase: bool) -> []u8 { | |
| 539 | return buf[0...bufPrintInt(buf, x, base, uppercase)]; | |
| 567 | fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> []u8 { | |
| 568 | return buf[0...bufPrintInt(buf, value, base, uppercase, width)]; | |
| 569 | } | |
| 570 | ||
| 571 | fn testParseU64DigitTooBig() { | |
| 572 | @setFnTest(this); | |
| 573 | ||
| 574 | parseUnsigned(u64, "123a", 10) %% |err| { | |
| 575 | if (err == error.InvalidChar) return; | |
| 576 | @unreachable(); | |
| 577 | }; | |
| 578 | @unreachable(); | |
| 579 | } | |
| 580 | ||
| 581 | fn testParseUnsignedComptime() { | |
| 582 | @setFnTest(this); | |
| 583 | ||
| 584 | comptime { | |
| 585 | assert(%%parseUnsigned(usize, "2", 10) == 2); | |
| 586 | } | |
| 540 | 587 | } |
| 541 | 588 | |
| 542 | 589 | fn testBufPrintInt() { |
| 543 | 590 | @setFnTest(this); |
| 544 | 591 | |
| 545 | 592 | var buf: [max_int_digits]u8 = undefined; |
| 546 | assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 2, false), "-101111000110000101001110")); | |
| 547 | assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 10, false), "-12345678")); | |
| 548 | assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, false), "-bc614e")); | |
| 549 | assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, true), "-BC614E")); | |
| 593 | assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); | |
| 594 | assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); | |
| 595 | assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); | |
| 596 | assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); | |
| 597 | ||
| 598 | assert(mem.eql(bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); | |
| 599 | ||
| 600 | assert(mem.eql(bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); | |
| 601 | assert(mem.eql(bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); | |
| 602 | assert(mem.eql(bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); | |
| 550 | 603 | |
| 551 | assert(mem.eql(bufPrintIntToSlice(buf, u32(12345678), 10, true), "12345678")); | |
| 604 | assert(mem.eql(bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); | |
| 605 | assert(mem.eql(bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); | |
| 552 | 606 | } |
std/math.zig+17| ... | ... | @@ -1,3 +1,5 @@ |
| 1 | const assert = @import("debug.zig").assert; | |
| 2 | ||
| 1 | 3 | pub const Cmp = enum { |
| 2 | 4 | Equal, |
| 3 | 5 | Greater, |
| ... | ... | @@ -66,3 +68,18 @@ fn getReturnTypeForAbs(comptime T: type) -> type { |
| 66 | 68 | } |
| 67 | 69 | } |
| 68 | 70 | |
| 71 | fn testMath() { | |
| 72 | @setFnTest(this); | |
| 73 | ||
| 74 | assert(%%mulOverflow(i32, 3, 4) == 12); | |
| 75 | assert(%%addOverflow(i32, 3, 4) == 7); | |
| 76 | assert(%%subOverflow(i32, 3, 4) == -1); | |
| 77 | assert(%%shlOverflow(i32, 0b11, 4) == 0b110000); | |
| 78 | ||
| 79 | comptime { | |
| 80 | assert(%%mulOverflow(i32, 3, 4) == 12); | |
| 81 | assert(%%addOverflow(i32, 3, 4) == 7); | |
| 82 | assert(%%subOverflow(i32, 3, 4) == -1); | |
| 83 | assert(%%shlOverflow(i32, 0b11, 4) == 0b110000); | |
| 84 | } | |
| 85 | } |
std/panic.zig+1-22| ... | ... | @@ -3,31 +3,10 @@ |
| 3 | 3 | // If this file wants to import other files *by name*, support for that would |
| 4 | 4 | // have to be added in the compiler. |
| 5 | 5 | |
| 6 | var panicking = false; | |
| 7 | 6 | pub coldcc fn panic(message: []const u8) -> unreachable { |
| 8 | 7 | if (@compileVar("os") == Os.freestanding) { |
| 9 | 8 | while (true) {} |
| 10 | 9 | } else { |
| 11 | const std = @import("std"); | |
| 12 | const io = std.io; | |
| 13 | const debug = std.debug; | |
| 14 | const os = std.os; | |
| 15 | ||
| 16 | // TODO | |
| 17 | // if (@atomicRmw(AtomicOp.XChg, &panicking, true, AtomicOrder.SeqCst)) { | |
| 18 | if (panicking) { | |
| 19 | // Panicked during a panic. | |
| 20 | // TODO detect if a different thread caused the panic, because in that case | |
| 21 | // we would want to return here instead of calling abort, so that the thread | |
| 22 | // which first called panic can finish printing a stack trace. | |
| 23 | os.abort(); | |
| 24 | } else { | |
| 25 | panicking = true; | |
| 26 | } | |
| 27 | ||
| 28 | %%io.stderr.printf("{}\n", message); | |
| 29 | %%debug.printStackTrace(); | |
| 30 | ||
| 31 | os.abort(); | |
| 10 | @import("std").debug.panic(message); | |
| 32 | 11 | } |
| 33 | 12 | } |
test/cases/enum_with_members.zig+2-2| ... | ... | @@ -8,8 +8,8 @@ const ET = enum { |
| 8 | 8 | |
| 9 | 9 | pub fn print(a: &const ET, buf: []u8) -> %usize { |
| 10 | 10 | return switch (*a) { |
| 11 | ET.SINT => |x| { io.bufPrintInt(buf, x, 10, false) }, | |
| 12 | ET.UINT => |x| { io.bufPrintInt(buf, x, 10, false) }, | |
| 11 | ET.SINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) }, | |
| 12 | ET.UINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) }, | |
| 13 | 13 | } |
| 14 | 14 | } |
| 15 | 15 | }; |
test/cases/eval.zig+12| ... | ... | @@ -251,3 +251,15 @@ fn comptimeIterateOverFnPtrList() { |
| 251 | 251 | assert(performFn('o', 0) == 1); |
| 252 | 252 | assert(performFn('w', 99) == 99); |
| 253 | 253 | } |
| 254 | ||
| 255 | fn evalSetDebugSafetyAtCompileTime() { | |
| 256 | @setFnTest(this); | |
| 257 | ||
| 258 | const result = comptime fnWithSetDebugSafety(); | |
| 259 | assert(result == 1234); | |
| 260 | } | |
| 261 | ||
| 262 | fn fnWithSetDebugSafety() -> i32{ | |
| 263 | @setDebugSafety(this, true); | |
| 264 | return 1234; | |
| 265 | } |
test/run_tests.cpp+20-2| ... | ... | @@ -1015,8 +1015,9 @@ const x = foo(); |
| 1015 | 1015 | |
| 1016 | 1016 | add_compile_fail_case("array concatenation with wrong type", R"SOURCE( |
| 1017 | 1017 | const src = "aoeu"; |
| 1018 | const a = src[0...] ++ "foo"; | |
| 1019 | )SOURCE", 1, ".tmp_source.zig:3:14: error: expected array or C string literal, found '[]u8'"); | |
| 1018 | const derp = usize(1234); | |
| 1019 | const a = derp ++ "foo"; | |
| 1020 | )SOURCE", 1, ".tmp_source.zig:4:11: error: expected array or C string literal, found 'usize'"); | |
| 1020 | 1021 | |
| 1021 | 1022 | add_compile_fail_case("non compile time array concatenation", R"SOURCE( |
| 1022 | 1023 | fn f(s: [10]u8) -> []u8 { |
| ... | ... | @@ -1632,6 +1633,23 @@ const some_data: [100]u8 = { |
| 1632 | 1633 | }; |
| 1633 | 1634 | )SOURCE", 1, ".tmp_source.zig:3:32: error: alignment value must be power of 2"); |
| 1634 | 1635 | |
| 1636 | add_compile_fail_case("compile log", R"SOURCE( | |
| 1637 | fn foo() { | |
| 1638 | comptime bar(12, "hi"); | |
| 1639 | } | |
| 1640 | fn bar(a: i32, b: []const u8) { | |
| 1641 | @compileLog("begin"); | |
| 1642 | @compileLog("a", a, "b", b); | |
| 1643 | @compileLog("end"); | |
| 1644 | } | |
| 1645 | )SOURCE", 6, | |
| 1646 | ".tmp_source.zig:6:5: error: found compile log statement", | |
| 1647 | ".tmp_source.zig:3:17: note: called from here", | |
| 1648 | ".tmp_source.zig:7:5: error: found compile log statement", | |
| 1649 | ".tmp_source.zig:3:17: note: called from here", | |
| 1650 | ".tmp_source.zig:8:5: error: found compile log statement", | |
| 1651 | ".tmp_source.zig:3:17: note: called from here"); | |
| 1652 | ||
| 1635 | 1653 | } |
| 1636 | 1654 | |
| 1637 | 1655 | ////////////////////////////////////////////////////////////////////////////// |