authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-01-30 11:40:03+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-01-30 11:40:03+02:00
log924eb08b613e3333419512f4de02b167aba9336d
tree6366aa385ba28c3193541fcc3d9f6874aaf52d13
parentf8e418c47d13189674bdf5f131c19fd811964579
parent3c53667db8d44777c2fbceaf3c6d3a22a6c9caad

Merge branch 'fixcomptimesat'

Closes #10393

6 files changed, 216 insertions(+), 49 deletions(-)

src/Sema.zig+23-13
...@@ -7474,7 +7474,10 @@ fn zirShl(...@@ -7474,7 +7474,10 @@ fn zirShl(
7474 }7474 }
7475 const val = switch (air_tag) {7475 const val = switch (air_tag) {
7476 .shl_exact => return sema.fail(block, lhs_src, "TODO implement Sema for comptime shl_exact", .{}),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 .shl => try lhs_val.shl(rhs_val, sema.arena),7481 .shl => try lhs_val.shl(rhs_val, sema.arena),
7479 else => unreachable,7482 else => unreachable,
7480 };7483 };
...@@ -8189,10 +8192,12 @@ fn analyzeArithmetic(...@@ -8189,10 +8192,12 @@ fn analyzeArithmetic(
8189 return casted_lhs;8192 return casted_lhs;
8190 }8193 }
8191 if (maybe_lhs_val) |lhs_val| {8194 if (maybe_lhs_val) |lhs_val| {
8192 return sema.addConstant(8195 const val = if (scalar_tag == .ComptimeInt)
8193 scalar_type,8196 try lhs_val.intAdd(rhs_val, sema.arena)
8194 try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target),8197 else
8195 );8198 try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target);
8199
8200 return sema.addConstant(scalar_type, val);
8196 } else break :rs .{ .src = lhs_src, .air_tag = .add_sat };8201 } else break :rs .{ .src = lhs_src, .air_tag = .add_sat };
8197 } else break :rs .{ .src = rhs_src, .air_tag = .add_sat };8202 } else break :rs .{ .src = rhs_src, .air_tag = .add_sat };
8198 },8203 },
...@@ -8280,10 +8285,12 @@ fn analyzeArithmetic(...@@ -8280,10 +8285,12 @@ fn analyzeArithmetic(
8280 return sema.addConstUndef(scalar_type);8285 return sema.addConstUndef(scalar_type);
8281 }8286 }
8282 if (maybe_rhs_val) |rhs_val| {8287 if (maybe_rhs_val) |rhs_val| {
8283 return sema.addConstant(8288 const val = if (scalar_tag == .ComptimeInt)
8284 scalar_type,8289 try lhs_val.intSub(rhs_val, sema.arena)
8285 try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target),8290 else
8286 );8291 try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target);
8292
8293 return sema.addConstant(scalar_type, val);
8287 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };8294 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };
8288 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };8295 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
8289 },8296 },
...@@ -8663,10 +8670,13 @@ fn analyzeArithmetic(...@@ -8663,10 +8670,13 @@ fn analyzeArithmetic(
8663 if (lhs_val.isUndef()) {8670 if (lhs_val.isUndef()) {
8664 return sema.addConstUndef(scalar_type);8671 return sema.addConstUndef(scalar_type);
8665 }8672 }
8666 return sema.addConstant(8673
8667 scalar_type,8674 const val = if (scalar_tag == .ComptimeInt)
8668 try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target),8675 try lhs_val.intMul(rhs_val, sema.arena)
8669 );8676 else
8677 try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target);
8678
8679 return sema.addConstant(scalar_type, val);
8670 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };8680 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };
8671 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };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,6 +60,9 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
60 bigint_init_unsigned(dest, 0);60 bigint_init_unsigned(dest, 0);
61 return;61 return;
62 }62 }
63
64 BigInt pos_op = {0};
65
63 if (op->is_negative) {66 if (op->is_negative) {
64 BigInt negated = {0};67 BigInt negated = {0};
65 bigint_negate(&negated, op);68 bigint_negate(&negated, op);
...@@ -70,13 +73,14 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)...@@ -70,13 +73,14 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
70 BigInt one = {0};73 BigInt one = {0};
71 bigint_init_unsigned(&one, 1);74 bigint_init_unsigned(&one, 1);
7275
73 bigint_add(dest, &inverted, &one);76 bigint_add(&pos_op, &inverted, &one);
74 return;77 } else {
78 bigint_init_bigint(&pos_op, op);
75 }79 }
7680
77 dest->is_negative = false;81 dest->is_negative = false;
78 const uint64_t *op_digits = bigint_ptr(op);82 const uint64_t *op_digits = bigint_ptr(&pos_op);
79 if (op->digit_count == 1) {83 if (pos_op.digit_count == 1) {
80 dest->data.digit = op_digits[0];84 dest->data.digit = op_digits[0];
81 if (bit_count < 64) {85 if (bit_count < 64) {
82 dest->data.digit &= (1ULL << bit_count) - 1;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,11 +102,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
98 }102 }
99 dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);103 dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);
100 for (size_t i = 0; i < digits_to_copy; i += 1) {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 dest->data.digits[i] = digit;106 dest->data.digits[i] = digit;
103 }107 }
104 if (leftover_bits != 0) {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 dest->data.digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1);110 dest->data.digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1);
107 }111 }
108 bigint_normalize(dest);112 bigint_normalize(dest);
...@@ -469,18 +473,18 @@ void bigint_min(BigInt* dest, const BigInt *op1, const BigInt *op2) {...@@ -469,18 +473,18 @@ void bigint_min(BigInt* dest, const BigInt *op1, const BigInt *op2) {
469}473}
470474
471/// clamps op within bit_count/signedness boundaries475/// clamps op within bit_count/signedness boundaries
472/// signed bounds are [-2^(bit_count-1)..2^(bit_count-1)-1] 476/// signed bounds are [-2^(bit_count-1)..2^(bit_count-1)-1]
473/// unsigned bounds are [0..2^bit_count-1] 477/// unsigned bounds are [0..2^bit_count-1]
474void bigint_clamp_by_bitcount(BigInt* dest, uint32_t bit_count, bool is_signed) {478void 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 // to decide whether to clamp the result480 // to decide whether to clamp the result
477 bool is_negative = dest->is_negative;481 bool is_negative = dest->is_negative;
478 // to workaround the fact this bits_needed calculation would yield 65 or more for 482 // 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 483 // all negative numbers, set is_negative to false. this is a cheap way to find
480 // bits_needed(abs(dest)). 484 // bits_needed(abs(dest)).
481 dest->is_negative = false;485 dest->is_negative = false;
482 // because we've set is_negative to false, we have to account for the extra bit here486 // 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 size_t full_bits = dest->digit_count * 64;488 size_t full_bits = dest->digit_count * 64;
485 size_t leading_zero_count = bigint_clz(dest, full_bits);489 size_t leading_zero_count = bigint_clz(dest, full_bits);
486 size_t bits_needed = full_bits - leading_zero_count + (is_negative && !is_signed);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,7 +495,7 @@ void bigint_clamp_by_bitcount(BigInt* dest, uint32_t bit_count, bool is_signed)
491 bigint_init_unsigned(&one, 1);495 bigint_init_unsigned(&one, 1);
492 BigInt bit_count_big;496 BigInt bit_count_big;
493 bigint_init_unsigned(&bit_count_big, bit_count);497 bigint_init_unsigned(&bit_count_big, bit_count);
494 498
495 if(is_signed) {499 if(is_signed) {
496 if(is_negative) {500 if(is_negative) {
497 BigInt bound;501 BigInt bound;
...@@ -639,25 +643,22 @@ void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {...@@ -639,25 +643,22 @@ void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
639 size_t i = 1;643 size_t i = 1;
640644
641 for (;;) {645 for (;;) {
642 bool found_digit = false;
643 uint64_t x = bigger_op_digits[i];646 uint64_t x = bigger_op_digits[i];
644 uint64_t prev_overflow = overflow;647 uint64_t prev_overflow = overflow;
645 overflow = 0;648 overflow = 0;
646649
647 if (i < smaller_op->digit_count) {650 if (i < smaller_op->digit_count) {
648 found_digit = true;
649 uint64_t digit = smaller_op_digits[i];651 uint64_t digit = smaller_op_digits[i];
650 overflow += sub_u64_overflow(x, digit, &x);652 overflow += sub_u64_overflow(x, digit, &x);
651 }653 }
652 if (sub_u64_overflow(x, prev_overflow, &x)) {654
653 found_digit = true;655 overflow += sub_u64_overflow(x, prev_overflow, &x);
654 overflow += 1;
655 }
656 dest->data.digits[i] = x;656 dest->data.digits[i] = x;
657 i += 1;657 i += 1;
658658
659 if (!found_digit || i >= bigger_op->digit_count)659 if (i >= bigger_op->digit_count) {
660 break;660 break;
661 }
661 }662 }
662 assert(overflow == 0);663 assert(overflow == 0);
663 dest->digit_count = i;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,13 +10230,7 @@ static Stage1AirInst *ir_analyze_bit_shift(IrAnalyze *ira, Stage1ZirInstBinOp *b
10230 // comptime_int has no finite bit width10230 // comptime_int has no finite bit width
10231 casted_op2 = op2;10231 casted_op2 = op2;
1023210232
10233 if (op_id == IrBinOpShlSat) {10233 if (op_id == IrBinOpBitShiftLeftLossy || 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) {
10240 op_id = IrBinOpBitShiftLeftExact;10234 op_id = IrBinOpBitShiftLeftExact;
10241 }10235 }
1024210236
...@@ -10398,6 +10392,25 @@ static bool ok_float_op(IrBinOp op) {...@@ -10398,6 +10392,25 @@ static bool ok_float_op(IrBinOp op) {
10398 zig_unreachable();10392 zig_unreachable();
10399}10393}
1040010394
10395static 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
10401static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {10414static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
10402 switch (op) {10415 switch (op) {
10403 case IrBinOpAdd:10416 case IrBinOpAdd:
...@@ -10620,15 +10633,10 @@ static Stage1AirInst *ir_analyze_bin_op_math(IrAnalyze *ira, Stage1ZirInstBinOp...@@ -10620,15 +10633,10 @@ static Stage1AirInst *ir_analyze_bin_op_math(IrAnalyze *ira, Stage1ZirInstBinOp
10620 if (type_is_invalid(casted_op2->value->type))10633 if (type_is_invalid(casted_op2->value->type))
10621 return ira->codegen->invalid_inst_gen;10634 return ira->codegen->invalid_inst_gen;
1062210635
10623 // Comptime integers have no fixed size10636 // Comptime integers have no fixed size, so wrapping or saturating operations should be mapped
10637 // to their non wrapping or saturating equivalents
10624 if (scalar_type->id == ZigTypeIdComptimeInt) {10638 if (scalar_type->id == ZigTypeIdComptimeInt) {
10625 if (op_id == IrBinOpAddWrap) {10639 op_id = map_comptime_arithmetic_op(op_id);
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 }
10632 }10640 }
1063310641
10634 if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {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,6 +2275,10 @@ pub const Value = extern union {
2275 ) !Value {2275 ) !Value {
2276 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);2276 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
22772277
2278 if (ty.zigTypeTag() == .ComptimeInt) {
2279 return intAdd(lhs, rhs, arena);
2280 }
2281
2278 if (ty.isAnyFloat()) {2282 if (ty.isAnyFloat()) {
2279 return floatAdd(lhs, rhs, ty, arena);2283 return floatAdd(lhs, rhs, ty, arena);
2280 }2284 }
...@@ -2361,6 +2365,10 @@ pub const Value = extern union {...@@ -2361,6 +2365,10 @@ pub const Value = extern union {
2361 ) !Value {2365 ) !Value {
2362 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);2366 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
23632367
2368 if (ty.zigTypeTag() == .ComptimeInt) {
2369 return intSub(lhs, rhs, arena);
2370 }
2371
2364 if (ty.isAnyFloat()) {2372 if (ty.isAnyFloat()) {
2365 return floatSub(lhs, rhs, ty, arena);2373 return floatSub(lhs, rhs, ty, arena);
2366 }2374 }
...@@ -2440,6 +2448,10 @@ pub const Value = extern union {...@@ -2440,6 +2448,10 @@ pub const Value = extern union {
2440 ) !Value {2448 ) !Value {
2441 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);2449 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
24422450
2451 if (ty.zigTypeTag() == .ComptimeInt) {
2452 return intMul(lhs, rhs, arena);
2453 }
2454
2443 if (ty.isAnyFloat()) {2455 if (ty.isAnyFloat()) {
2444 return floatMul(lhs, rhs, ty, arena);2456 return floatMul(lhs, rhs, ty, arena);
2445 }2457 }
test/behavior/saturating_arithmetic.zig+26
...@@ -29,8 +29,14 @@ test "saturating add" {...@@ -29,8 +29,14 @@ test "saturating add" {
29 try expect(x == expected);29 try expect(x == expected);
30 }30 }
31 };31 };
32
32 try S.doTheTest();33 try S.doTheTest();
33 comptime try S.doTheTest();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}
3541
36test "saturating subtraction" {42test "saturating subtraction" {
...@@ -56,8 +62,14 @@ test "saturating subtraction" {...@@ -56,8 +62,14 @@ test "saturating subtraction" {
56 try expect(x == expected);62 try expect(x == expected);
57 }63 }
58 };64 };
65
59 try S.doTheTest();66 try S.doTheTest();
60 comptime try S.doTheTest();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}
6274
63test "saturating multiplication" {75test "saturating multiplication" {
...@@ -90,6 +102,11 @@ test "saturating multiplication" {...@@ -90,6 +102,11 @@ test "saturating multiplication" {
90102
91 try S.doTheTest();103 try S.doTheTest();
92 comptime try S.doTheTest();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}
94111
95test "saturating shift-left" {112test "saturating shift-left" {
...@@ -107,6 +124,7 @@ test "saturating shift-left" {...@@ -107,6 +124,7 @@ test "saturating shift-left" {
107 try testSatShl(u8, 1, 2, 4);124 try testSatShl(u8, 1, 2, 4);
108 try testSatShl(u8, 255, 1, 255);125 try testSatShl(u8, 255, 1, 255);
109 }126 }
127
110 fn testSatShl(comptime T: type, lhs: T, rhs: T, expected: T) !void {128 fn testSatShl(comptime T: type, lhs: T, rhs: T, expected: T) !void {
111 try expect((lhs <<| rhs) == expected);129 try expect((lhs <<| rhs) == expected);
112130
...@@ -115,8 +133,14 @@ test "saturating shift-left" {...@@ -115,8 +133,14 @@ test "saturating shift-left" {
115 try expect(x == expected);133 try expect(x == expected);
116 }134 }
117 };135 };
136
118 try S.doTheTest();137 try S.doTheTest();
119 comptime try S.doTheTest();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}
121145
122test "saturating shl uses the LHS type" {146test "saturating shl uses the LHS type" {
...@@ -139,4 +163,6 @@ test "saturating shl uses the LHS type" {...@@ -139,4 +163,6 @@ test "saturating shl uses the LHS type" {
139 try expect((@as(u8, 1) <<| 8) == 255);163 try expect((@as(u8, 1) <<| 8) == 255);
140 try expect((@as(u8, 1) <<| rhs_const) == 255);164 try expect((@as(u8, 1) <<| rhs_const) == 255);
141 try expect((@as(u8, 1) <<| rhs_var) == 255);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 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const minInt = std.math.minInt;
4const maxInt = std.math.maxInt;
5const expect = std.testing.expect;
6
7test "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
42test "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
75test "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}