| author | |
| committer | |
| log | c7804277bf390eeba368e3565b2aff0cf96f86b0 |
| tree | cdf926acbc28a278ffeeb92552631a724c2da98d |
| parent | 0b92d689d0e64164bb8908c807db9338d59c41ce |
when the integer part does not fit in the destination integer type
* Also fix incorrect safety triggered for integer casting an
`i32` to a `u7`. closes #1138
* adds compiler-rt function: `__floatuntidf`12 files changed, 291 insertions(+), 9 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -568,6 +568,7 @@ set(ZIG_STD_FILES |
| 568 | 568 | "special/compiler_rt/fixunstfdi.zig" |
| 569 | 569 | "special/compiler_rt/fixunstfsi.zig" |
| 570 | 570 | "special/compiler_rt/fixunstfti.zig" |
| 571 | "special/compiler_rt/floatuntidf.zig" | |
| 571 | 572 | "special/compiler_rt/muloti4.zig" |
| 572 | 573 | "special/compiler_rt/index.zig" |
| 573 | 574 | "special/compiler_rt/udivmod.zig" |
doc/langref.html.in+8-1| ... | ... | @@ -5035,8 +5035,12 @@ test "main" { |
| 5035 | 5035 | <pre><code class="zig">@floatToInt(comptime DestType: type, float: var) DestType</code></pre> |
| 5036 | 5036 | <p> |
| 5037 | 5037 | Converts the integer part of a floating point number to the destination type. |
| 5038 | To convert the other way, use {#link|@intToFloat#}. This cast is always safe. | |
| 5039 | 5038 | </p> |
| 5039 | <p> | |
| 5040 | If the integer part of the floating point number cannot fit in the destination type, | |
| 5041 | it invokes safety-checked {#link|Undefined Behavior#}. | |
| 5042 | </p> | |
| 5043 | {#see_also|@intToFloat#} | |
| 5040 | 5044 | {#header_close#} |
| 5041 | 5045 | |
| 5042 | 5046 | {#header_open|@frameAddress#} |
| ... | ... | @@ -6207,7 +6211,10 @@ comptime { |
| 6207 | 6211 | {#header_close#} |
| 6208 | 6212 | {#header_open|Wrong Union Field Access#} |
| 6209 | 6213 | <p>TODO</p> |
| 6214 | {#header_close#} | |
| 6210 | 6215 | |
| 6216 | {#header_open|Out of Bounds Float To Integer Cast#} | |
| 6217 | <p>TODO</p> | |
| 6211 | 6218 | {#header_close#} |
| 6212 | 6219 | |
| 6213 | 6220 | {#header_close#} |
src/all_types.hpp+1| ... | ... | @@ -1434,6 +1434,7 @@ enum PanicMsgId { |
| 1434 | 1434 | PanicMsgIdIncorrectAlignment, |
| 1435 | 1435 | PanicMsgIdBadUnionField, |
| 1436 | 1436 | PanicMsgIdBadEnumValue, |
| 1437 | PanicMsgIdFloatToInt, | |
| 1437 | 1438 | |
| 1438 | 1439 | PanicMsgIdCount, |
| 1439 | 1440 | }; |
src/codegen.cpp+32-4| ... | ... | @@ -865,6 +865,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 865 | 865 | return buf_create_from_str("access of inactive union field"); |
| 866 | 866 | case PanicMsgIdBadEnumValue: |
| 867 | 867 | return buf_create_from_str("invalid enum value"); |
| 868 | case PanicMsgIdFloatToInt: | |
| 869 | return buf_create_from_str("integer part of floating point value out of bounds"); | |
| 868 | 870 | } |
| 869 | 871 | zig_unreachable(); |
| 870 | 872 | } |
| ... | ... | @@ -1671,7 +1673,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, T |
| 1671 | 1673 | return trunc_val; |
| 1672 | 1674 | } |
| 1673 | 1675 | LLVMValueRef orig_val; |
| 1674 | if (actual_type->data.integral.is_signed) { | |
| 1676 | if (wanted_type->data.integral.is_signed) { | |
| 1675 | 1677 | orig_val = LLVMBuildSExt(g->builder, trunc_val, actual_type->type_ref, ""); |
| 1676 | 1678 | } else { |
| 1677 | 1679 | orig_val = LLVMBuildZExt(g->builder, trunc_val, actual_type->type_ref, ""); |
| ... | ... | @@ -2546,15 +2548,41 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, |
| 2546 | 2548 | } else { |
| 2547 | 2549 | return LLVMBuildUIToFP(g->builder, expr_val, wanted_type->type_ref, ""); |
| 2548 | 2550 | } |
| 2549 | case CastOpFloatToInt: | |
| 2551 | case CastOpFloatToInt: { | |
| 2550 | 2552 | assert(wanted_type->id == TypeTableEntryIdInt); |
| 2551 | 2553 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &cast_instruction->base)); |
| 2554 | ||
| 2555 | bool want_safety = ir_want_runtime_safety(g, &cast_instruction->base); | |
| 2556 | ||
| 2557 | LLVMValueRef result; | |
| 2552 | 2558 | if (wanted_type->data.integral.is_signed) { |
| 2553 | return LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, ""); | |
| 2559 | result = LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, ""); | |
| 2554 | 2560 | } else { |
| 2555 | return LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, ""); | |
| 2561 | result = LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, ""); | |
| 2556 | 2562 | } |
| 2557 | 2563 | |
| 2564 | if (want_safety) { | |
| 2565 | LLVMValueRef back_to_float; | |
| 2566 | if (wanted_type->data.integral.is_signed) { | |
| 2567 | back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); | |
| 2568 | } else { | |
| 2569 | back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); | |
| 2570 | } | |
| 2571 | LLVMValueRef difference = LLVMBuildFSub(g->builder, expr_val, back_to_float, ""); | |
| 2572 | LLVMValueRef one_pos = LLVMConstReal(LLVMTypeOf(expr_val), 1.0f); | |
| 2573 | LLVMValueRef one_neg = LLVMConstReal(LLVMTypeOf(expr_val), -1.0f); | |
| 2574 | LLVMValueRef ok_bit_pos = LLVMBuildFCmp(g->builder, LLVMRealOLT, difference, one_pos, ""); | |
| 2575 | LLVMValueRef ok_bit_neg = LLVMBuildFCmp(g->builder, LLVMRealOGT, difference, one_neg, ""); | |
| 2576 | LLVMValueRef ok_bit = LLVMBuildAnd(g->builder, ok_bit_pos, ok_bit_neg, ""); | |
| 2577 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "FloatCheckOk"); | |
| 2578 | LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "FloatCheckFail"); | |
| 2579 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, bad_block); | |
| 2580 | LLVMPositionBuilderAtEnd(g->builder, bad_block); | |
| 2581 | gen_safety_crash(g, PanicMsgIdFloatToInt); | |
| 2582 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 2583 | } | |
| 2584 | return result; | |
| 2585 | } | |
| 2558 | 2586 | case CastOpBoolToInt: |
| 2559 | 2587 | assert(wanted_type->id == TypeTableEntryIdInt); |
| 2560 | 2588 | assert(actual_type->id == TypeTableEntryIdBool); |
src/ir.cpp+22-3| ... | ... | @@ -9057,7 +9057,8 @@ static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_ |
| 9057 | 9057 | } |
| 9058 | 9058 | } |
| 9059 | 9059 | |
| 9060 | static void eval_const_expr_implicit_cast(CastOp cast_op, | |
| 9060 | static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_instr, | |
| 9061 | CastOp cast_op, | |
| 9061 | 9062 | ConstExprValue *other_val, TypeTableEntry *other_type, |
| 9062 | 9063 | ConstExprValue *const_val, TypeTableEntry *new_type) |
| 9063 | 9064 | { |
| ... | ... | @@ -9129,6 +9130,20 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, |
| 9129 | 9130 | } |
| 9130 | 9131 | case CastOpFloatToInt: |
| 9131 | 9132 | float_init_bigint(&const_val->data.x_bigint, other_val); |
| 9133 | if (new_type->id == TypeTableEntryIdInt) { | |
| 9134 | if (!bigint_fits_in_bits(&const_val->data.x_bigint, new_type->data.integral.bit_count, | |
| 9135 | new_type->data.integral.is_signed)) | |
| 9136 | { | |
| 9137 | Buf *int_buf = buf_alloc(); | |
| 9138 | bigint_append_buf(int_buf, &const_val->data.x_bigint, 10); | |
| 9139 | ||
| 9140 | ir_add_error(ira, source_instr, | |
| 9141 | buf_sprintf("integer value '%s' cannot be stored in type '%s'", | |
| 9142 | buf_ptr(int_buf), buf_ptr(&new_type->name))); | |
| 9143 | return false; | |
| 9144 | } | |
| 9145 | } | |
| 9146 | ||
| 9132 | 9147 | const_val->special = ConstValSpecialStatic; |
| 9133 | 9148 | break; |
| 9134 | 9149 | case CastOpBoolToInt: |
| ... | ... | @@ -9136,6 +9151,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, |
| 9136 | 9151 | const_val->special = ConstValSpecialStatic; |
| 9137 | 9152 | break; |
| 9138 | 9153 | } |
| 9154 | return true; | |
| 9139 | 9155 | } |
| 9140 | 9156 | static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, |
| 9141 | 9157 | TypeTableEntry *wanted_type, CastOp cast_op, bool need_alloca) |
| ... | ... | @@ -9145,8 +9161,11 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 9145 | 9161 | { |
| 9146 | 9162 | IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, |
| 9147 | 9163 | source_instr->source_node, wanted_type); |
| 9148 | eval_const_expr_implicit_cast(cast_op, &value->value, value->value.type, | |
| 9149 | &result->value, wanted_type); | |
| 9164 | if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, | |
| 9165 | &result->value, wanted_type)) | |
| 9166 | { | |
| 9167 | return ira->codegen->invalid_instruction; | |
| 9168 | } | |
| 9150 | 9169 | return result; |
| 9151 | 9170 | } else { |
| 9152 | 9171 | IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op); |
std/math/expm1.zig+8| ... | ... | @@ -20,6 +20,10 @@ pub fn expm1(x: var) @typeOf(x) { |
| 20 | 20 | |
| 21 | 21 | fn expm1_32(x_: f32) f32 { |
| 22 | 22 | @setFloatMode(this, builtin.FloatMode.Strict); |
| 23 | ||
| 24 | if (math.isNan(x_)) | |
| 25 | return math.nan(f32); | |
| 26 | ||
| 23 | 27 | const o_threshold: f32 = 8.8721679688e+01; |
| 24 | 28 | const ln2_hi: f32 = 6.9313812256e-01; |
| 25 | 29 | const ln2_lo: f32 = 9.0580006145e-06; |
| ... | ... | @@ -146,6 +150,10 @@ fn expm1_32(x_: f32) f32 { |
| 146 | 150 | |
| 147 | 151 | fn expm1_64(x_: f64) f64 { |
| 148 | 152 | @setFloatMode(this, builtin.FloatMode.Strict); |
| 153 | ||
| 154 | if (math.isNan(x_)) | |
| 155 | return math.nan(f64); | |
| 156 | ||
| 149 | 157 | const o_threshold: f64 = 7.09782712893383973096e+02; |
| 150 | 158 | const ln2_hi: f64 = 6.93147180369123816490e-01; |
| 151 | 159 | const ln2_lo: f64 = 1.90821492927058770002e-10; |
std/special/compiler_rt/floatuntidf.zig created+60| ... | ... | @@ -0,0 +1,60 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const is_test = builtin.is_test; | |
| 3 | ||
| 4 | const DBL_MANT_DIG = 53; | |
| 5 | ||
| 6 | pub extern fn __floatuntidf(arg: u128) f64 { | |
| 7 | @setRuntimeSafety(is_test); | |
| 8 | ||
| 9 | if (arg == 0) | |
| 10 | return 0.0; | |
| 11 | ||
| 12 | var a = arg; | |
| 13 | const N: u32 = @sizeOf(u128) * 8; | |
| 14 | const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits | |
| 15 | var e: i32 = sd -% 1; // exponent | |
| 16 | if (sd > DBL_MANT_DIG) { | |
| 17 | // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx | |
| 18 | // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR | |
| 19 | // 12345678901234567890123456 | |
| 20 | // 1 = msb 1 bit | |
| 21 | // P = bit DBL_MANT_DIG-1 bits to the right of 1 | |
| 22 | // Q = bit DBL_MANT_DIG bits to the right of 1 | |
| 23 | // R = "or" of all bits to the right of Q | |
| 24 | switch (sd) { | |
| 25 | DBL_MANT_DIG + 1 => { | |
| 26 | a <<= 1; | |
| 27 | }, | |
| 28 | DBL_MANT_DIG + 2 => {}, | |
| 29 | else => { | |
| 30 | const shift_amt = @bitCast(i32, N +% (DBL_MANT_DIG + 2)) -% sd; | |
| 31 | const shift_amt_u7 = @intCast(u7, shift_amt); | |
| 32 | a = (a >> @intCast(u7, sd -% (DBL_MANT_DIG + 2))) | | |
| 33 | @boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0); | |
| 34 | }, | |
| 35 | } | |
| 36 | // finish | |
| 37 | a |= @boolToInt((a & 4) != 0); // Or P into R | |
| 38 | a +%= 1; // round - this step may add a significant bit | |
| 39 | a >>= 2; // dump Q and R | |
| 40 | // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits | |
| 41 | if ((a & (u128(1) << DBL_MANT_DIG)) != 0) { | |
| 42 | a >>= 1; | |
| 43 | e +%= 1; | |
| 44 | } | |
| 45 | // a is now rounded to DBL_MANT_DIG bits | |
| 46 | } else { | |
| 47 | a <<= @intCast(u7, DBL_MANT_DIG -% sd); | |
| 48 | // a is now rounded to DBL_MANT_DIG bits | |
| 49 | } | |
| 50 | ||
| 51 | const high: u64 = @bitCast(u32, (e +% 1023) << 20) | // exponent | |
| 52 | (@truncate(u32, a >> 32) & 0x000FFFFF); // mantissa-high | |
| 53 | const low = @truncate(u32, a); // mantissa-low | |
| 54 | ||
| 55 | return @bitCast(f64, low | (high << 32)); | |
| 56 | } | |
| 57 | ||
| 58 | test "import floatuntidf" { | |
| 59 | _ = @import("floatuntidf_test.zig"); | |
| 60 | } |
std/special/compiler_rt/floatuntidf_test.zig created+81| ... | ... | @@ -0,0 +1,81 @@ |
| 1 | const __floatuntidf = @import("floatuntidf.zig").__floatuntidf; | |
| 2 | const assert = @import("std").debug.assert; | |
| 3 | ||
| 4 | fn test__floatuntidf(a: u128, expected: f64) void { | |
| 5 | const x = __floatuntidf(a); | |
| 6 | assert(x == expected); | |
| 7 | } | |
| 8 | ||
| 9 | test "floatuntidf" { | |
| 10 | test__floatuntidf(0, 0.0); | |
| 11 | ||
| 12 | test__floatuntidf(1, 1.0); | |
| 13 | test__floatuntidf(2, 2.0); | |
| 14 | test__floatuntidf(20, 20.0); | |
| 15 | ||
| 16 | test__floatuntidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); | |
| 17 | test__floatuntidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); | |
| 18 | test__floatuntidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); | |
| 19 | test__floatuntidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); | |
| 20 | ||
| 21 | test__floatuntidf(make_ti(0x8000008000000000, 0), 0x1.000001p+127); | |
| 22 | test__floatuntidf(make_ti(0x8000000000000800, 0), 0x1.0000000000001p+127); | |
| 23 | test__floatuntidf(make_ti(0x8000010000000000, 0), 0x1.000002p+127); | |
| 24 | test__floatuntidf(make_ti(0x8000000000001000, 0), 0x1.0000000000002p+127); | |
| 25 | ||
| 26 | test__floatuntidf(make_ti(0x8000000000000000, 0), 0x1.000000p+127); | |
| 27 | test__floatuntidf(make_ti(0x8000000000000001, 0), 0x1.0000000000000002p+127); | |
| 28 | ||
| 29 | test__floatuntidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); | |
| 30 | ||
| 31 | test__floatuntidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); | |
| 32 | test__floatuntidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); | |
| 33 | test__floatuntidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); | |
| 34 | test__floatuntidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); | |
| 35 | test__floatuntidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); | |
| 36 | ||
| 37 | test__floatuntidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); | |
| 38 | test__floatuntidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); | |
| 39 | test__floatuntidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); | |
| 40 | test__floatuntidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); | |
| 41 | test__floatuntidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); | |
| 42 | ||
| 43 | test__floatuntidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); | |
| 44 | test__floatuntidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); | |
| 45 | test__floatuntidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); | |
| 46 | test__floatuntidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); | |
| 47 | test__floatuntidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); | |
| 48 | test__floatuntidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); | |
| 49 | test__floatuntidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); | |
| 50 | test__floatuntidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); | |
| 51 | test__floatuntidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); | |
| 52 | test__floatuntidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); | |
| 53 | test__floatuntidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); | |
| 54 | test__floatuntidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); | |
| 55 | test__floatuntidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); | |
| 56 | test__floatuntidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); | |
| 57 | test__floatuntidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); | |
| 58 | ||
| 59 | test__floatuntidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); | |
| 60 | test__floatuntidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); | |
| 61 | test__floatuntidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); | |
| 62 | test__floatuntidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); | |
| 63 | test__floatuntidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); | |
| 64 | test__floatuntidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); | |
| 65 | test__floatuntidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); | |
| 66 | test__floatuntidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); | |
| 67 | test__floatuntidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); | |
| 68 | test__floatuntidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); | |
| 69 | test__floatuntidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); | |
| 70 | test__floatuntidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); | |
| 71 | test__floatuntidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); | |
| 72 | test__floatuntidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); | |
| 73 | test__floatuntidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); | |
| 74 | } | |
| 75 | ||
| 76 | fn make_ti(high: u64, low: u64) u128 { | |
| 77 | var result: u128 = high; | |
| 78 | result <<= 64; | |
| 79 | result |= low; | |
| 80 | return result; | |
| 81 | } |
std/special/compiler_rt/index.zig+2| ... | ... | @@ -19,6 +19,8 @@ comptime { |
| 19 | 19 | |
| 20 | 20 | @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); |
| 21 | 21 | |
| 22 | @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); | |
| 23 | ||
| 22 | 24 | @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); |
| 23 | 25 | @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); |
| 24 | 26 | @export("__fixunssfti", @import("fixunssfti.zig").__fixunssfti, linkage); |
test/cases/cast.zig+20-1| ... | ... | @@ -340,11 +340,23 @@ fn testPeerErrorAndArray2(x: u8) error![]const u8 { |
| 340 | 340 | }; |
| 341 | 341 | } |
| 342 | 342 | |
| 343 | test "explicit cast float number literal to integer if no fraction component" { | |
| 343 | test "@floatToInt" { | |
| 344 | testFloatToInts(); | |
| 345 | comptime testFloatToInts(); | |
| 346 | } | |
| 347 | ||
| 348 | fn testFloatToInts() void { | |
| 344 | 349 | const x = i32(1e4); |
| 345 | 350 | assert(x == 10000); |
| 346 | 351 | const y = @floatToInt(i32, f32(1e4)); |
| 347 | 352 | assert(y == 10000); |
| 353 | expectFloatToInt(u8, 255.1, 255); | |
| 354 | expectFloatToInt(i8, 127.2, 127); | |
| 355 | expectFloatToInt(i8, -128.2, -128); | |
| 356 | } | |
| 357 | ||
| 358 | fn expectFloatToInt(comptime T: type, f: f32, i: T) void { | |
| 359 | assert(@floatToInt(T, f) == i); | |
| 348 | 360 | } |
| 349 | 361 | |
| 350 | 362 | test "cast u128 to f128 and back" { |
| ... | ... | @@ -426,3 +438,10 @@ test "@bytesToSlice keeps pointer alignment" { |
| 426 | 438 | const numbers = @bytesToSlice(u32, bytes[0..]); |
| 427 | 439 | comptime assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); |
| 428 | 440 | } |
| 441 | ||
| 442 | test "@intCast i32 to u7" { | |
| 443 | var x: u128 = @maxValue(u128); | |
| 444 | var y: i32 = 120; | |
| 445 | var z = x >> @intCast(u7, y); | |
| 446 | assert(z == 0xff); | |
| 447 | } |
test/compile_errors.zig+17| ... | ... | @@ -1,6 +1,23 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4 | cases.add( | |
| 5 | "@floatToInt comptime safety", | |
| 6 | \\comptime { | |
| 7 | \\ _ = @floatToInt(i8, f32(-129.1)); | |
| 8 | \\} | |
| 9 | \\comptime { | |
| 10 | \\ _ = @floatToInt(u8, f32(-1.1)); | |
| 11 | \\} | |
| 12 | \\comptime { | |
| 13 | \\ _ = @floatToInt(u8, f32(256.1)); | |
| 14 | \\} | |
| 15 | , | |
| 16 | ".tmp_source.zig:2:9: error: integer value '-129' cannot be stored in type 'i8'", | |
| 17 | ".tmp_source.zig:5:9: error: integer value '-1' cannot be stored in type 'u8'", | |
| 18 | ".tmp_source.zig:8:9: error: integer value '256' cannot be stored in type 'u8'", | |
| 19 | ); | |
| 20 | ||
| 4 | 21 | cases.add( |
| 5 | 22 | "use c_void as return type of fn ptr", |
| 6 | 23 | \\export fn entry() void { |
test/runtime_safety.zig+39| ... | ... | @@ -1,6 +1,45 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 4 | cases.addRuntimeSafety("@floatToInt cannot fit - negative to unsigned", | |
| 5 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 6 | \\ @import("std").os.exit(126); | |
| 7 | \\} | |
| 8 | \\pub fn main() void { | |
| 9 | \\ baz(bar(-1.1)); | |
| 10 | \\} | |
| 11 | \\fn bar(a: f32) u8 { | |
| 12 | \\ return @floatToInt(u8, a); | |
| 13 | \\} | |
| 14 | \\fn baz(a: u8) void { } | |
| 15 | ); | |
| 16 | ||
| 17 | cases.addRuntimeSafety("@floatToInt cannot fit - negative out of range", | |
| 18 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 19 | \\ @import("std").os.exit(126); | |
| 20 | \\} | |
| 21 | \\pub fn main() void { | |
| 22 | \\ baz(bar(-129.1)); | |
| 23 | \\} | |
| 24 | \\fn bar(a: f32) i8 { | |
| 25 | \\ return @floatToInt(i8, a); | |
| 26 | \\} | |
| 27 | \\fn baz(a: i8) void { } | |
| 28 | ); | |
| 29 | ||
| 30 | cases.addRuntimeSafety("@floatToInt cannot fit - positive out of range", | |
| 31 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 32 | \\ @import("std").os.exit(126); | |
| 33 | \\} | |
| 34 | \\pub fn main() void { | |
| 35 | \\ baz(bar(256.2)); | |
| 36 | \\} | |
| 37 | \\fn bar(a: f32) u8 { | |
| 38 | \\ return @floatToInt(u8, a); | |
| 39 | \\} | |
| 40 | \\fn baz(a: u8) void { } | |
| 41 | ); | |
| 42 | ||
| 4 | 43 | cases.addRuntimeSafety("calling panic", |
| 5 | 44 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 6 | 45 | \\ @import("std").os.exit(126); |