| author | |
| committer | |
| log | 924eb08b613e3333419512f4de02b167aba9336d |
| tree | 6366aa385ba28c3193541fcc3d9f6874aaf52d13 |
| parent | f8e418c47d13189674bdf5f131c19fd811964579 |
| parent | 3c53667db8d44777c2fbceaf3c6d3a22a6c9caad |
Closes #103936 files changed, 216 insertions(+), 49 deletions(-)
src/Sema.zig+23-13| ... | ... | @@ -7474,7 +7474,10 @@ fn zirShl( |
| 7474 | 7474 | } |
| 7475 | 7475 | const val = switch (air_tag) { |
| 7476 | 7476 | .shl_exact => return sema.fail(block, lhs_src, "TODO implement Sema for comptime shl_exact", .{}), |
| 7477 | .shl_sat => try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, sema.mod.getTarget()), | |
| 7477 | .shl_sat => if (lhs_ty.zigTypeTag() == .ComptimeInt) | |
| 7478 | try lhs_val.shl(rhs_val, sema.arena) | |
| 7479 | else | |
| 7480 | try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, sema.mod.getTarget()), | |
| 7478 | 7481 | .shl => try lhs_val.shl(rhs_val, sema.arena), |
| 7479 | 7482 | else => unreachable, |
| 7480 | 7483 | }; |
| ... | ... | @@ -8189,10 +8192,12 @@ fn analyzeArithmetic( |
| 8189 | 8192 | return casted_lhs; |
| 8190 | 8193 | } |
| 8191 | 8194 | if (maybe_lhs_val) |lhs_val| { |
| 8192 | return sema.addConstant( | |
| 8193 | scalar_type, | |
| 8194 | try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target), | |
| 8195 | ); | |
| 8195 | const val = if (scalar_tag == .ComptimeInt) | |
| 8196 | try lhs_val.intAdd(rhs_val, sema.arena) | |
| 8197 | else | |
| 8198 | try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target); | |
| 8199 | ||
| 8200 | return sema.addConstant(scalar_type, val); | |
| 8196 | 8201 | } else break :rs .{ .src = lhs_src, .air_tag = .add_sat }; |
| 8197 | 8202 | } else break :rs .{ .src = rhs_src, .air_tag = .add_sat }; |
| 8198 | 8203 | }, |
| ... | ... | @@ -8280,10 +8285,12 @@ fn analyzeArithmetic( |
| 8280 | 8285 | return sema.addConstUndef(scalar_type); |
| 8281 | 8286 | } |
| 8282 | 8287 | if (maybe_rhs_val) |rhs_val| { |
| 8283 | return sema.addConstant( | |
| 8284 | scalar_type, | |
| 8285 | try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target), | |
| 8286 | ); | |
| 8288 | const val = if (scalar_tag == .ComptimeInt) | |
| 8289 | try lhs_val.intSub(rhs_val, sema.arena) | |
| 8290 | else | |
| 8291 | try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target); | |
| 8292 | ||
| 8293 | return sema.addConstant(scalar_type, val); | |
| 8287 | 8294 | } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat }; |
| 8288 | 8295 | } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; |
| 8289 | 8296 | }, |
| ... | ... | @@ -8663,10 +8670,13 @@ fn analyzeArithmetic( |
| 8663 | 8670 | if (lhs_val.isUndef()) { |
| 8664 | 8671 | return sema.addConstUndef(scalar_type); |
| 8665 | 8672 | } |
| 8666 | return sema.addConstant( | |
| 8667 | scalar_type, | |
| 8668 | try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target), | |
| 8669 | ); | |
| 8673 | ||
| 8674 | const val = if (scalar_tag == .ComptimeInt) | |
| 8675 | try lhs_val.intMul(rhs_val, sema.arena) | |
| 8676 | else | |
| 8677 | try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target); | |
| 8678 | ||
| 8679 | return sema.addConstant(scalar_type, val); | |
| 8670 | 8680 | } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat }; |
| 8671 | 8681 | } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat }; |
| 8672 | 8682 | }, |
src/stage1/bigint.cpp+22-21| ... | ... | @@ -60,6 +60,9 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) |
| 60 | 60 | bigint_init_unsigned(dest, 0); |
| 61 | 61 | return; |
| 62 | 62 | } |
| 63 | ||
| 64 | BigInt pos_op = {0}; | |
| 65 | ||
| 63 | 66 | if (op->is_negative) { |
| 64 | 67 | BigInt negated = {0}; |
| 65 | 68 | bigint_negate(&negated, op); |
| ... | ... | @@ -70,13 +73,14 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) |
| 70 | 73 | BigInt one = {0}; |
| 71 | 74 | bigint_init_unsigned(&one, 1); |
| 72 | 75 | |
| 73 | bigint_add(dest, &inverted, &one); | |
| 74 | return; | |
| 76 | bigint_add(&pos_op, &inverted, &one); | |
| 77 | } else { | |
| 78 | bigint_init_bigint(&pos_op, op); | |
| 75 | 79 | } |
| 76 | 80 | |
| 77 | 81 | dest->is_negative = false; |
| 78 | const uint64_t *op_digits = bigint_ptr(op); | |
| 79 | if (op->digit_count == 1) { | |
| 82 | const uint64_t *op_digits = bigint_ptr(&pos_op); | |
| 83 | if (pos_op.digit_count == 1) { | |
| 80 | 84 | dest->data.digit = op_digits[0]; |
| 81 | 85 | if (bit_count < 64) { |
| 82 | 86 | dest->data.digit &= (1ULL << bit_count) - 1; |
| ... | ... | @@ -98,11 +102,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) |
| 98 | 102 | } |
| 99 | 103 | dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count); |
| 100 | 104 | for (size_t i = 0; i < digits_to_copy; i += 1) { |
| 101 | uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0; | |
| 105 | uint64_t digit = (i < pos_op.digit_count) ? op_digits[i] : 0; | |
| 102 | 106 | dest->data.digits[i] = digit; |
| 103 | 107 | } |
| 104 | 108 | if (leftover_bits != 0) { |
| 105 | uint64_t digit = (digits_to_copy < op->digit_count) ? op_digits[digits_to_copy] : 0; | |
| 109 | uint64_t digit = (digits_to_copy < pos_op.digit_count) ? op_digits[digits_to_copy] : 0; | |
| 106 | 110 | dest->data.digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1); |
| 107 | 111 | } |
| 108 | 112 | bigint_normalize(dest); |
| ... | ... | @@ -469,18 +473,18 @@ void bigint_min(BigInt* dest, const BigInt *op1, const BigInt *op2) { |
| 469 | 473 | } |
| 470 | 474 | |
| 471 | 475 | /// clamps op within bit_count/signedness boundaries |
| 472 | /// signed bounds are [-2^(bit_count-1)..2^(bit_count-1)-1] | |
| 473 | /// unsigned bounds are [0..2^bit_count-1] | |
| 476 | /// signed bounds are [-2^(bit_count-1)..2^(bit_count-1)-1] | |
| 477 | /// unsigned bounds are [0..2^bit_count-1] | |
| 474 | 478 | void bigint_clamp_by_bitcount(BigInt* dest, uint32_t bit_count, bool is_signed) { |
| 475 | // compute the number of bits required to store the value, and use that | |
| 479 | // compute the number of bits required to store the value, and use that | |
| 476 | 480 | // to decide whether to clamp the result |
| 477 | 481 | bool is_negative = dest->is_negative; |
| 478 | // to workaround the fact this bits_needed calculation would yield 65 or more for | |
| 479 | // all negative numbers, set is_negative to false. this is a cheap way to find | |
| 480 | // bits_needed(abs(dest)). | |
| 482 | // to workaround the fact this bits_needed calculation would yield 65 or more for | |
| 483 | // all negative numbers, set is_negative to false. this is a cheap way to find | |
| 484 | // bits_needed(abs(dest)). | |
| 481 | 485 | dest->is_negative = false; |
| 482 | 486 | // because we've set is_negative to false, we have to account for the extra bit here |
| 483 | // by adding 1 additional bit_needed when (is_negative && !is_signed). | |
| 487 | // by adding 1 additional bit_needed when (is_negative && !is_signed). | |
| 484 | 488 | size_t full_bits = dest->digit_count * 64; |
| 485 | 489 | size_t leading_zero_count = bigint_clz(dest, full_bits); |
| 486 | 490 | size_t bits_needed = full_bits - leading_zero_count + (is_negative && !is_signed); |
| ... | ... | @@ -491,7 +495,7 @@ void bigint_clamp_by_bitcount(BigInt* dest, uint32_t bit_count, bool is_signed) |
| 491 | 495 | bigint_init_unsigned(&one, 1); |
| 492 | 496 | BigInt bit_count_big; |
| 493 | 497 | bigint_init_unsigned(&bit_count_big, bit_count); |
| 494 | ||
| 498 | ||
| 495 | 499 | if(is_signed) { |
| 496 | 500 | if(is_negative) { |
| 497 | 501 | BigInt bound; |
| ... | ... | @@ -639,25 +643,22 @@ void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) { |
| 639 | 643 | size_t i = 1; |
| 640 | 644 | |
| 641 | 645 | for (;;) { |
| 642 | bool found_digit = false; | |
| 643 | 646 | uint64_t x = bigger_op_digits[i]; |
| 644 | 647 | uint64_t prev_overflow = overflow; |
| 645 | 648 | overflow = 0; |
| 646 | 649 | |
| 647 | 650 | if (i < smaller_op->digit_count) { |
| 648 | found_digit = true; | |
| 649 | 651 | uint64_t digit = smaller_op_digits[i]; |
| 650 | 652 | overflow += sub_u64_overflow(x, digit, &x); |
| 651 | 653 | } |
| 652 | if (sub_u64_overflow(x, prev_overflow, &x)) { | |
| 653 | found_digit = true; | |
| 654 | overflow += 1; | |
| 655 | } | |
| 654 | ||
| 655 | overflow += sub_u64_overflow(x, prev_overflow, &x); | |
| 656 | 656 | dest->data.digits[i] = x; |
| 657 | 657 | i += 1; |
| 658 | 658 | |
| 659 | if (!found_digit || i >= bigger_op->digit_count) | |
| 659 | if (i >= bigger_op->digit_count) { | |
| 660 | 660 | break; |
| 661 | } | |
| 661 | 662 | } |
| 662 | 663 | assert(overflow == 0); |
| 663 | 664 | dest->digit_count = i; |
src/stage1/ir.cpp+23-15| ... | ... | @@ -10230,13 +10230,7 @@ static Stage1AirInst *ir_analyze_bit_shift(IrAnalyze *ira, Stage1ZirInstBinOp *b |
| 10230 | 10230 | // comptime_int has no finite bit width |
| 10231 | 10231 | casted_op2 = op2; |
| 10232 | 10232 | |
| 10233 | if (op_id == IrBinOpShlSat) { | |
| 10234 | ir_add_error_node(ira, bin_op_instruction->base.source_node, | |
| 10235 | buf_sprintf("saturating shift on a comptime_int which has unlimited bits")); | |
| 10236 | return ira->codegen->invalid_inst_gen; | |
| 10237 | } | |
| 10238 | ||
| 10239 | if (op_id == IrBinOpBitShiftLeftLossy) { | |
| 10233 | if (op_id == IrBinOpBitShiftLeftLossy || op_id == IrBinOpShlSat) { | |
| 10240 | 10234 | op_id = IrBinOpBitShiftLeftExact; |
| 10241 | 10235 | } |
| 10242 | 10236 | |
| ... | ... | @@ -10398,6 +10392,25 @@ static bool ok_float_op(IrBinOp op) { |
| 10398 | 10392 | zig_unreachable(); |
| 10399 | 10393 | } |
| 10400 | 10394 | |
| 10395 | static IrBinOp map_comptime_arithmetic_op(IrBinOp op) { | |
| 10396 | switch (op) { | |
| 10397 | case IrBinOpAddWrap: | |
| 10398 | case IrBinOpAddSat: | |
| 10399 | return IrBinOpAdd; | |
| 10400 | ||
| 10401 | case IrBinOpSubWrap: | |
| 10402 | case IrBinOpSubSat: | |
| 10403 | return IrBinOpSub; | |
| 10404 | ||
| 10405 | case IrBinOpMultWrap: | |
| 10406 | case IrBinOpMultSat: | |
| 10407 | return IrBinOpMult; | |
| 10408 | ||
| 10409 | default: | |
| 10410 | return op; | |
| 10411 | } | |
| 10412 | } | |
| 10413 | ||
| 10401 | 10414 | static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { |
| 10402 | 10415 | switch (op) { |
| 10403 | 10416 | case IrBinOpAdd: |
| ... | ... | @@ -10620,15 +10633,10 @@ static Stage1AirInst *ir_analyze_bin_op_math(IrAnalyze *ira, Stage1ZirInstBinOp |
| 10620 | 10633 | if (type_is_invalid(casted_op2->value->type)) |
| 10621 | 10634 | return ira->codegen->invalid_inst_gen; |
| 10622 | 10635 | |
| 10623 | // Comptime integers have no fixed size | |
| 10636 | // Comptime integers have no fixed size, so wrapping or saturating operations should be mapped | |
| 10637 | // to their non wrapping or saturating equivalents | |
| 10624 | 10638 | if (scalar_type->id == ZigTypeIdComptimeInt) { |
| 10625 | if (op_id == IrBinOpAddWrap) { | |
| 10626 | op_id = IrBinOpAdd; | |
| 10627 | } else if (op_id == IrBinOpSubWrap) { | |
| 10628 | op_id = IrBinOpSub; | |
| 10629 | } else if (op_id == IrBinOpMultWrap) { | |
| 10630 | op_id = IrBinOpMult; | |
| 10631 | } | |
| 10639 | op_id = map_comptime_arithmetic_op(op_id); | |
| 10632 | 10640 | } |
| 10633 | 10641 | |
| 10634 | 10642 | if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { |
src/value.zig+12| ... | ... | @@ -2275,6 +2275,10 @@ pub const Value = extern union { |
| 2275 | 2275 | ) !Value { |
| 2276 | 2276 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); |
| 2277 | 2277 | |
| 2278 | if (ty.zigTypeTag() == .ComptimeInt) { | |
| 2279 | return intAdd(lhs, rhs, arena); | |
| 2280 | } | |
| 2281 | ||
| 2278 | 2282 | if (ty.isAnyFloat()) { |
| 2279 | 2283 | return floatAdd(lhs, rhs, ty, arena); |
| 2280 | 2284 | } |
| ... | ... | @@ -2361,6 +2365,10 @@ pub const Value = extern union { |
| 2361 | 2365 | ) !Value { |
| 2362 | 2366 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); |
| 2363 | 2367 | |
| 2368 | if (ty.zigTypeTag() == .ComptimeInt) { | |
| 2369 | return intSub(lhs, rhs, arena); | |
| 2370 | } | |
| 2371 | ||
| 2364 | 2372 | if (ty.isAnyFloat()) { |
| 2365 | 2373 | return floatSub(lhs, rhs, ty, arena); |
| 2366 | 2374 | } |
| ... | ... | @@ -2440,6 +2448,10 @@ pub const Value = extern union { |
| 2440 | 2448 | ) !Value { |
| 2441 | 2449 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); |
| 2442 | 2450 | |
| 2451 | if (ty.zigTypeTag() == .ComptimeInt) { | |
| 2452 | return intMul(lhs, rhs, arena); | |
| 2453 | } | |
| 2454 | ||
| 2443 | 2455 | if (ty.isAnyFloat()) { |
| 2444 | 2456 | return floatMul(lhs, rhs, ty, arena); |
| 2445 | 2457 | } |
test/behavior/saturating_arithmetic.zig+26| ... | ... | @@ -29,8 +29,14 @@ test "saturating add" { |
| 29 | 29 | try expect(x == expected); |
| 30 | 30 | } |
| 31 | 31 | }; |
| 32 | ||
| 32 | 33 | try S.doTheTest(); |
| 33 | 34 | comptime try S.doTheTest(); |
| 35 | ||
| 36 | comptime try S.testSatAdd(comptime_int, 0, 0, 0); | |
| 37 | comptime try S.testSatAdd(comptime_int, 3, 2, 5); | |
| 38 | comptime try S.testSatAdd(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 1119305249183743626545271163355074748512); | |
| 39 | comptime try S.testSatAdd(comptime_int, 7, -593423721213448152027139550640105366508, -593423721213448152027139550640105366501); | |
| 34 | 40 | } |
| 35 | 41 | |
| 36 | 42 | test "saturating subtraction" { |
| ... | ... | @@ -56,8 +62,14 @@ test "saturating subtraction" { |
| 56 | 62 | try expect(x == expected); |
| 57 | 63 | } |
| 58 | 64 | }; |
| 65 | ||
| 59 | 66 | try S.doTheTest(); |
| 60 | 67 | comptime try S.doTheTest(); |
| 68 | ||
| 69 | comptime try S.testSatSub(comptime_int, 0, 0, 0); | |
| 70 | comptime try S.testSatSub(comptime_int, 3, 2, 1); | |
| 71 | comptime try S.testSatSub(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 182846383813587550256162760261375991602); | |
| 72 | comptime try S.testSatSub(comptime_int, 7, -593423721213448152027139550640105366508, 593423721213448152027139550640105366515); | |
| 61 | 73 | } |
| 62 | 74 | |
| 63 | 75 | test "saturating multiplication" { |
| ... | ... | @@ -90,6 +102,11 @@ test "saturating multiplication" { |
| 90 | 102 | |
| 91 | 103 | try S.doTheTest(); |
| 92 | 104 | comptime try S.doTheTest(); |
| 105 | ||
| 106 | comptime try S.testSatMul(comptime_int, 0, 0, 0); | |
| 107 | comptime try S.testSatMul(comptime_int, 3, 2, 6); | |
| 108 | comptime try S.testSatMul(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 304852860194144160265083087140337419215516305999637969803722975979232817921935); | |
| 109 | comptime try S.testSatMul(comptime_int, 7, -593423721213448152027139550640105366508, -4153966048494137064189976854480737565556); | |
| 93 | 110 | } |
| 94 | 111 | |
| 95 | 112 | test "saturating shift-left" { |
| ... | ... | @@ -107,6 +124,7 @@ test "saturating shift-left" { |
| 107 | 124 | try testSatShl(u8, 1, 2, 4); |
| 108 | 125 | try testSatShl(u8, 255, 1, 255); |
| 109 | 126 | } |
| 127 | ||
| 110 | 128 | fn testSatShl(comptime T: type, lhs: T, rhs: T, expected: T) !void { |
| 111 | 129 | try expect((lhs <<| rhs) == expected); |
| 112 | 130 | |
| ... | ... | @@ -115,8 +133,14 @@ test "saturating shift-left" { |
| 115 | 133 | try expect(x == expected); |
| 116 | 134 | } |
| 117 | 135 | }; |
| 136 | ||
| 118 | 137 | try S.doTheTest(); |
| 119 | 138 | comptime try S.doTheTest(); |
| 139 | ||
| 140 | comptime try S.testSatShl(comptime_int, 0, 0, 0); | |
| 141 | comptime try S.testSatShl(comptime_int, 1, 2, 4); | |
| 142 | comptime try S.testSatShl(comptime_int, 13, 150, 18554220005177478453757717602843436772975706112); | |
| 143 | comptime try S.testSatShl(comptime_int, -582769, 180, -893090893854873184096635538665358532628308979495815656505344); | |
| 120 | 144 | } |
| 121 | 145 | |
| 122 | 146 | test "saturating shl uses the LHS type" { |
| ... | ... | @@ -139,4 +163,6 @@ test "saturating shl uses the LHS type" { |
| 139 | 163 | try expect((@as(u8, 1) <<| 8) == 255); |
| 140 | 164 | try expect((@as(u8, 1) <<| rhs_const) == 255); |
| 141 | 165 | try expect((@as(u8, 1) <<| rhs_var) == 255); |
| 166 | ||
| 167 | try expect((1 <<| @as(u8, 200)) == 1606938044258990275541962092341162602522202993782792835301376); | |
| 142 | 168 | } |
test/behavior/wrapping_arithmetic.zig created+110| ... | ... | @@ -0,0 +1,110 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const minInt = std.math.minInt; | |
| 4 | const maxInt = std.math.maxInt; | |
| 5 | const expect = std.testing.expect; | |
| 6 | ||
| 7 | test "wrapping add" { | |
| 8 | const S = struct { | |
| 9 | fn doTheTest() !void { | |
| 10 | try testWrapAdd(i8, -3, 10, 7); | |
| 11 | try testWrapAdd(i8, -128, -128, 0); | |
| 12 | try testWrapAdd(i2, 1, 1, -2); | |
| 13 | try testWrapAdd(i64, maxInt(i64), 1, minInt(i64)); | |
| 14 | try testWrapAdd(i128, maxInt(i128), -maxInt(i128), 0); | |
| 15 | try testWrapAdd(i128, minInt(i128), maxInt(i128), -1); | |
| 16 | try testWrapAdd(i8, 127, 127, -2); | |
| 17 | try testWrapAdd(u8, 3, 10, 13); | |
| 18 | try testWrapAdd(u8, 255, 255, 254); | |
| 19 | try testWrapAdd(u2, 3, 2, 1); | |
| 20 | try testWrapAdd(u3, 7, 1, 0); | |
| 21 | try testWrapAdd(u128, maxInt(u128), 1, minInt(u128)); | |
| 22 | } | |
| 23 | ||
| 24 | fn testWrapAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void { | |
| 25 | try expect((lhs +% rhs) == expected); | |
| 26 | ||
| 27 | var x = lhs; | |
| 28 | x +%= rhs; | |
| 29 | try expect(x == expected); | |
| 30 | } | |
| 31 | }; | |
| 32 | ||
| 33 | try S.doTheTest(); | |
| 34 | comptime try S.doTheTest(); | |
| 35 | ||
| 36 | comptime try S.testWrapAdd(comptime_int, 0, 0, 0); | |
| 37 | comptime try S.testWrapAdd(comptime_int, 3, 2, 5); | |
| 38 | comptime try S.testWrapAdd(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 1119305249183743626545271163355074748512); | |
| 39 | comptime try S.testWrapAdd(comptime_int, 7, -593423721213448152027139550640105366508, -593423721213448152027139550640105366501); | |
| 40 | } | |
| 41 | ||
| 42 | test "wrapping subtraction" { | |
| 43 | const S = struct { | |
| 44 | fn doTheTest() !void { | |
| 45 | try testWrapSub(i8, -3, 10, -13); | |
| 46 | try testWrapSub(i8, -128, -128, 0); | |
| 47 | try testWrapSub(i8, -1, 127, -128); | |
| 48 | try testWrapSub(i64, minInt(i64), 1, maxInt(i64)); | |
| 49 | try testWrapSub(i128, maxInt(i128), -1, minInt(i128)); | |
| 50 | try testWrapSub(i128, minInt(i128), -maxInt(i128), -1); | |
| 51 | try testWrapSub(u8, 10, 3, 7); | |
| 52 | try testWrapSub(u8, 0, 255, 1); | |
| 53 | try testWrapSub(u5, 0, 31, 1); | |
| 54 | try testWrapSub(u128, 0, maxInt(u128), 1); | |
| 55 | } | |
| 56 | ||
| 57 | fn testWrapSub(comptime T: type, lhs: T, rhs: T, expected: T) !void { | |
| 58 | try expect((lhs -% rhs) == expected); | |
| 59 | ||
| 60 | var x = lhs; | |
| 61 | x -%= rhs; | |
| 62 | try expect(x == expected); | |
| 63 | } | |
| 64 | }; | |
| 65 | ||
| 66 | try S.doTheTest(); | |
| 67 | comptime try S.doTheTest(); | |
| 68 | ||
| 69 | comptime try S.testWrapSub(comptime_int, 0, 0, 0); | |
| 70 | comptime try S.testWrapSub(comptime_int, 3, 2, 1); | |
| 71 | comptime try S.testWrapSub(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 182846383813587550256162760261375991602); | |
| 72 | comptime try S.testWrapSub(comptime_int, 7, -593423721213448152027139550640105366508, 593423721213448152027139550640105366515); | |
| 73 | } | |
| 74 | ||
| 75 | test "wrapping multiplication" { | |
| 76 | // TODO: once #9660 has been solved, remove this line | |
| 77 | if (builtin.cpu.arch == .wasm32) return error.SkipZigTest; | |
| 78 | ||
| 79 | const S = struct { | |
| 80 | fn doTheTest() !void { | |
| 81 | try testWrapMul(i8, -3, 10, -30); | |
| 82 | try testWrapMul(i4, 2, 4, -8); | |
| 83 | try testWrapMul(i8, 2, 127, -2); | |
| 84 | try testWrapMul(i8, -128, -128, 0); | |
| 85 | try testWrapMul(i8, maxInt(i8), maxInt(i8), 1); | |
| 86 | try testWrapMul(i16, maxInt(i16), -1, minInt(i16) + 1); | |
| 87 | try testWrapMul(i128, maxInt(i128), -1, minInt(i128) + 1); | |
| 88 | try testWrapMul(i128, minInt(i128), -1, minInt(i128)); | |
| 89 | try testWrapMul(u8, 10, 3, 30); | |
| 90 | try testWrapMul(u8, 2, 255, 254); | |
| 91 | try testWrapMul(u128, maxInt(u128), maxInt(u128), 1); | |
| 92 | } | |
| 93 | ||
| 94 | fn testWrapMul(comptime T: type, lhs: T, rhs: T, expected: T) !void { | |
| 95 | try expect((lhs *% rhs) == expected); | |
| 96 | ||
| 97 | var x = lhs; | |
| 98 | x *%= rhs; | |
| 99 | try expect(x == expected); | |
| 100 | } | |
| 101 | }; | |
| 102 | ||
| 103 | try S.doTheTest(); | |
| 104 | comptime try S.doTheTest(); | |
| 105 | ||
| 106 | comptime try S.testWrapMul(comptime_int, 0, 0, 0); | |
| 107 | comptime try S.testWrapMul(comptime_int, 3, 2, 6); | |
| 108 | comptime try S.testWrapMul(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 304852860194144160265083087140337419215516305999637969803722975979232817921935); | |
| 109 | comptime try S.testWrapMul(comptime_int, 7, -593423721213448152027139550640105366508, -4153966048494137064189976854480737565556); | |
| 110 | } |