| author | |
| committer | |
| log | 7f70c27e9d57c8234545120da81861e2cfb354b5 |
| tree | 47948dcf37e5e5767db2b428a8ef8177ef8eee86 |
| parent | a3c9bfef301e0a4675fcea57356653334e07df45 |
AIR:
* div is renamed to div_trunc.
* Add div_float, div_floor, div_exact.
- Implemented in Sema and LLVM codegen. C backend has a stub.
Improvements to std.math.big.Int:
* Add `eqZero` function to `Mutable`.
* Fix incorrect results for `divFloor`.
Compiler-rt:
* Add muloti4 to the stage2 section.24 files changed, 1026 insertions(+), 556 deletions(-)
lib/std/math/big/int.zig+13-5| ... | ... | @@ -135,6 +135,11 @@ pub const Mutable = struct { |
| 135 | 135 | }; |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | /// Returns true if `a == 0`. | |
| 139 | pub fn eqZero(self: Mutable) bool { | |
| 140 | return self.toConst().eqZero(); | |
| 141 | } | |
| 142 | ||
| 138 | 143 | /// Asserts that the allocator owns the limbs memory. If this is not the case, |
| 139 | 144 | /// use `toConst().toManaged()`. |
| 140 | 145 | pub fn toManaged(self: Mutable, allocator: *Allocator) Managed { |
| ... | ... | @@ -773,12 +778,15 @@ pub const Mutable = struct { |
| 773 | 778 | div(q, r, a, b, limbs_buffer, allocator); |
| 774 | 779 | |
| 775 | 780 | // Trunc -> Floor. |
| 776 | if (!q.positive) { | |
| 781 | if (a.positive and b.positive) return; | |
| 782 | ||
| 783 | if ((!q.positive or q.eqZero()) and !r.eqZero()) { | |
| 777 | 784 | const one: Const = .{ .limbs = &[_]Limb{1}, .positive = true }; |
| 778 | 785 | q.sub(q.toConst(), one); |
| 779 | r.add(q.toConst(), one); | |
| 780 | 786 | } |
| 781 | r.positive = b.positive; | |
| 787 | ||
| 788 | r.mulNoAlias(q.toConst(), b, allocator); | |
| 789 | r.sub(a, r.toConst()); | |
| 782 | 790 | } |
| 783 | 791 | |
| 784 | 792 | /// q = a / b (rem r) |
| ... | ... | @@ -1220,12 +1228,12 @@ pub const Mutable = struct { |
| 1220 | 1228 | |
| 1221 | 1229 | var x: Mutable = .{ |
| 1222 | 1230 | .limbs = x_limbs, |
| 1223 | .positive = a.positive, | |
| 1231 | .positive = true, | |
| 1224 | 1232 | .len = a.limbs.len - ab_zero_limb_count, |
| 1225 | 1233 | }; |
| 1226 | 1234 | var y: Mutable = .{ |
| 1227 | 1235 | .limbs = y_limbs, |
| 1228 | .positive = b.positive, | |
| 1236 | .positive = true, | |
| 1229 | 1237 | .len = b.limbs.len - ab_zero_limb_count, |
| 1230 | 1238 | }; |
| 1231 | 1239 |
lib/std/math/big/int_test.zig+57| ... | ... | @@ -1399,6 +1399,63 @@ test "big.int div floor single-single -/-" { |
| 1399 | 1399 | try testing.expect((try r.to(i32)) == er); |
| 1400 | 1400 | } |
| 1401 | 1401 | |
| 1402 | test "big.int div floor no remainder negative quotient" { | |
| 1403 | const u: i32 = -0x80000000; | |
| 1404 | const v: i32 = 1; | |
| 1405 | ||
| 1406 | var a = try Managed.initSet(testing.allocator, u); | |
| 1407 | defer a.deinit(); | |
| 1408 | var b = try Managed.initSet(testing.allocator, v); | |
| 1409 | defer b.deinit(); | |
| 1410 | ||
| 1411 | var q = try Managed.init(testing.allocator); | |
| 1412 | defer q.deinit(); | |
| 1413 | var r = try Managed.init(testing.allocator); | |
| 1414 | defer r.deinit(); | |
| 1415 | try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); | |
| 1416 | ||
| 1417 | try testing.expect((try q.to(i32)) == -0x80000000); | |
| 1418 | try testing.expect((try r.to(i32)) == 0); | |
| 1419 | } | |
| 1420 | ||
| 1421 | test "big.int div floor negative close to zero" { | |
| 1422 | const u: i32 = -2; | |
| 1423 | const v: i32 = 12; | |
| 1424 | ||
| 1425 | var a = try Managed.initSet(testing.allocator, u); | |
| 1426 | defer a.deinit(); | |
| 1427 | var b = try Managed.initSet(testing.allocator, v); | |
| 1428 | defer b.deinit(); | |
| 1429 | ||
| 1430 | var q = try Managed.init(testing.allocator); | |
| 1431 | defer q.deinit(); | |
| 1432 | var r = try Managed.init(testing.allocator); | |
| 1433 | defer r.deinit(); | |
| 1434 | try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); | |
| 1435 | ||
| 1436 | try testing.expect((try q.to(i32)) == -1); | |
| 1437 | try testing.expect((try r.to(i32)) == 10); | |
| 1438 | } | |
| 1439 | ||
| 1440 | test "big.int div floor positive close to zero" { | |
| 1441 | const u: i32 = 10; | |
| 1442 | const v: i32 = 12; | |
| 1443 | ||
| 1444 | var a = try Managed.initSet(testing.allocator, u); | |
| 1445 | defer a.deinit(); | |
| 1446 | var b = try Managed.initSet(testing.allocator, v); | |
| 1447 | defer b.deinit(); | |
| 1448 | ||
| 1449 | var q = try Managed.init(testing.allocator); | |
| 1450 | defer q.deinit(); | |
| 1451 | var r = try Managed.init(testing.allocator); | |
| 1452 | defer r.deinit(); | |
| 1453 | try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); | |
| 1454 | ||
| 1455 | try testing.expect((try q.to(i32)) == 0); | |
| 1456 | try testing.expect((try r.to(i32)) == 10); | |
| 1457 | } | |
| 1458 | ||
| 1402 | 1459 | test "big.int div multi-multi with rem" { |
| 1403 | 1460 | var a = try Managed.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999); |
| 1404 | 1461 | defer a.deinit(); |
lib/std/special/compiler_rt.zig+5-4| ... | ... | @@ -74,6 +74,11 @@ comptime { |
| 74 | 74 | @export(__getf2, .{ .name = "__gttf2", .linkage = linkage }); |
| 75 | 75 | |
| 76 | 76 | @export(__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage }); |
| 77 | ||
| 78 | const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4; | |
| 79 | @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage }); | |
| 80 | const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4; | |
| 81 | @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); | |
| 77 | 82 | } |
| 78 | 83 | |
| 79 | 84 | if (!builtin.zig_is_stage2) { |
| ... | ... | @@ -621,10 +626,6 @@ comptime { |
| 621 | 626 | const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3; |
| 622 | 627 | @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); |
| 623 | 628 | } |
| 624 | const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4; | |
| 625 | @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage }); | |
| 626 | const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4; | |
| 627 | @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); | |
| 628 | 629 | |
| 629 | 630 | _ = @import("compiler_rt/atomics.zig"); |
| 630 | 631 |
src/Air.zig+22-3| ... | ... | @@ -80,11 +80,27 @@ pub const Inst = struct { |
| 80 | 80 | /// is the same as both operands. |
| 81 | 81 | /// Uses the `bin_op` field. |
| 82 | 82 | mul_sat, |
| 83 | /// Integer or float division. For integers, wrapping is undefined behavior. | |
| 83 | /// Float division. | |
| 84 | 84 | /// Both operands are guaranteed to be the same type, and the result type |
| 85 | 85 | /// is the same as both operands. |
| 86 | 86 | /// Uses the `bin_op` field. |
| 87 | div, | |
| 87 | div_float, | |
| 88 | /// Truncating integer or float division. For integers, wrapping is undefined behavior. | |
| 89 | /// Both operands are guaranteed to be the same type, and the result type | |
| 90 | /// is the same as both operands. | |
| 91 | /// Uses the `bin_op` field. | |
| 92 | div_trunc, | |
| 93 | /// Flooring integer or float division. For integers, wrapping is undefined behavior. | |
| 94 | /// Both operands are guaranteed to be the same type, and the result type | |
| 95 | /// is the same as both operands. | |
| 96 | /// Uses the `bin_op` field. | |
| 97 | div_floor, | |
| 98 | /// Integer or float division. Guaranteed no remainder. | |
| 99 | /// For integers, wrapping is undefined behavior. | |
| 100 | /// Both operands are guaranteed to be the same type, and the result type | |
| 101 | /// is the same as both operands. | |
| 102 | /// Uses the `bin_op` field. | |
| 103 | div_exact, | |
| 88 | 104 | /// Integer or float remainder division. |
| 89 | 105 | /// Both operands are guaranteed to be the same type, and the result type |
| 90 | 106 | /// is the same as both operands. |
| ... | ... | @@ -644,7 +660,10 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 644 | 660 | .mul, |
| 645 | 661 | .mulwrap, |
| 646 | 662 | .mul_sat, |
| 647 | .div, | |
| 663 | .div_float, | |
| 664 | .div_trunc, | |
| 665 | .div_floor, | |
| 666 | .div_exact, | |
| 648 | 667 | .rem, |
| 649 | 668 | .mod, |
| 650 | 669 | .bit_and, |
src/Liveness.zig+4-1| ... | ... | @@ -233,7 +233,10 @@ fn analyzeInst( |
| 233 | 233 | .mul, |
| 234 | 234 | .mulwrap, |
| 235 | 235 | .mul_sat, |
| 236 | .div, | |
| 236 | .div_float, | |
| 237 | .div_trunc, | |
| 238 | .div_floor, | |
| 239 | .div_exact, | |
| 237 | 240 | .rem, |
| 238 | 241 | .mod, |
| 239 | 242 | .ptr_add, |
src/Sema.zig+215-38| ... | ... | @@ -641,9 +641,6 @@ pub fn analyzeBody( |
| 641 | 641 | .pop_count => try sema.zirPopCount(block, inst), |
| 642 | 642 | .byte_swap => try sema.zirByteSwap(block, inst), |
| 643 | 643 | .bit_reverse => try sema.zirBitReverse(block, inst), |
| 644 | .div_exact => try sema.zirDivExact(block, inst), | |
| 645 | .div_floor => try sema.zirDivFloor(block, inst), | |
| 646 | .div_trunc => try sema.zirDivTrunc(block, inst), | |
| 647 | 644 | .shr_exact => try sema.zirShrExact(block, inst), |
| 648 | 645 | .bit_offset_of => try sema.zirBitOffsetOf(block, inst), |
| 649 | 646 | .offset_of => try sema.zirOffsetOf(block, inst), |
| ... | ... | @@ -683,19 +680,22 @@ pub fn analyzeBody( |
| 683 | 680 | .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), |
| 684 | 681 | .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func), |
| 685 | 682 | |
| 686 | .add => try sema.zirArithmetic(block, inst, .add), | |
| 687 | .addwrap => try sema.zirArithmetic(block, inst, .addwrap), | |
| 688 | .add_sat => try sema.zirArithmetic(block, inst, .add_sat), | |
| 689 | .div => try sema.zirArithmetic(block, inst, .div), | |
| 690 | .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem), | |
| 691 | .mod => try sema.zirArithmetic(block, inst, .mod), | |
| 692 | .rem => try sema.zirArithmetic(block, inst, .rem), | |
| 693 | .mul => try sema.zirArithmetic(block, inst, .mul), | |
| 694 | .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap), | |
| 695 | .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat), | |
| 696 | .sub => try sema.zirArithmetic(block, inst, .sub), | |
| 697 | .subwrap => try sema.zirArithmetic(block, inst, .subwrap), | |
| 698 | .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat), | |
| 683 | .add => try sema.zirArithmetic(block, inst, .add), | |
| 684 | .addwrap => try sema.zirArithmetic(block, inst, .addwrap), | |
| 685 | .add_sat => try sema.zirArithmetic(block, inst, .add_sat), | |
| 686 | .div => try sema.zirArithmetic(block, inst, .div), | |
| 687 | .div_exact => try sema.zirArithmetic(block, inst, .div_exact), | |
| 688 | .div_floor => try sema.zirArithmetic(block, inst, .div_floor), | |
| 689 | .div_trunc => try sema.zirArithmetic(block, inst, .div_trunc), | |
| 690 | .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem), | |
| 691 | .mod => try sema.zirArithmetic(block, inst, .mod), | |
| 692 | .rem => try sema.zirArithmetic(block, inst, .rem), | |
| 693 | .mul => try sema.zirArithmetic(block, inst, .mul), | |
| 694 | .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap), | |
| 695 | .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat), | |
| 696 | .sub => try sema.zirArithmetic(block, inst, .sub), | |
| 697 | .subwrap => try sema.zirArithmetic(block, inst, .subwrap), | |
| 698 | .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat), | |
| 699 | 699 | |
| 700 | 700 | .maximum => try sema.zirMinMax(block, inst, .max), |
| 701 | 701 | .minimum => try sema.zirMinMax(block, inst, .min), |
| ... | ... | @@ -7350,6 +7350,9 @@ fn analyzeArithmetic( |
| 7350 | 7350 | } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; |
| 7351 | 7351 | }, |
| 7352 | 7352 | .div => { |
| 7353 | // TODO: emit compile error when .div is used on integers and there would be an | |
| 7354 | // ambiguous result between div_floor and div_trunc. | |
| 7355 | ||
| 7353 | 7356 | // For integers: |
| 7354 | 7357 | // If the lhs is zero, then zero is returned regardless of rhs. |
| 7355 | 7358 | // If the rhs is zero, compile error for division by zero. |
| ... | ... | @@ -7359,9 +7362,11 @@ fn analyzeArithmetic( |
| 7359 | 7362 | // * if lhs type is signed: |
| 7360 | 7363 | // * if rhs is comptime-known and not -1, result is undefined |
| 7361 | 7364 | // * if rhs is -1 or runtime-known, compile error because there is a |
| 7362 | // possible value (-min_int * -1) for which division would be | |
| 7365 | // possible value (-min_int / -1) for which division would be | |
| 7363 | 7366 | // illegal behavior. |
| 7364 | 7367 | // * if lhs type is unsigned, undef is returned regardless of rhs. |
| 7368 | // TODO: emit runtime safety for division by zero | |
| 7369 | // | |
| 7365 | 7370 | // For floats: |
| 7366 | 7371 | // If the rhs is zero, compile error for division by zero. |
| 7367 | 7372 | // If the rhs is undefined, compile error because there is a possible |
| ... | ... | @@ -7407,8 +7412,198 @@ fn analyzeArithmetic( |
| 7407 | 7412 | try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena), |
| 7408 | 7413 | ); |
| 7409 | 7414 | } |
| 7410 | } else break :rs .{ .src = rhs_src, .air_tag = .div }; | |
| 7411 | } else break :rs .{ .src = lhs_src, .air_tag = .div }; | |
| 7415 | } else { | |
| 7416 | if (is_int) { | |
| 7417 | break :rs .{ .src = rhs_src, .air_tag = .div_trunc }; | |
| 7418 | } else { | |
| 7419 | break :rs .{ .src = rhs_src, .air_tag = .div_float }; | |
| 7420 | } | |
| 7421 | } | |
| 7422 | } else { | |
| 7423 | if (is_int) { | |
| 7424 | break :rs .{ .src = lhs_src, .air_tag = .div_trunc }; | |
| 7425 | } else { | |
| 7426 | break :rs .{ .src = lhs_src, .air_tag = .div_float }; | |
| 7427 | } | |
| 7428 | } | |
| 7429 | }, | |
| 7430 | .div_trunc => { | |
| 7431 | // For integers: | |
| 7432 | // If the lhs is zero, then zero is returned regardless of rhs. | |
| 7433 | // If the rhs is zero, compile error for division by zero. | |
| 7434 | // If the rhs is undefined, compile error because there is a possible | |
| 7435 | // value (zero) for which the division would be illegal behavior. | |
| 7436 | // If the lhs is undefined: | |
| 7437 | // * if lhs type is signed: | |
| 7438 | // * if rhs is comptime-known and not -1, result is undefined | |
| 7439 | // * if rhs is -1 or runtime-known, compile error because there is a | |
| 7440 | // possible value (-min_int / -1) for which division would be | |
| 7441 | // illegal behavior. | |
| 7442 | // * if lhs type is unsigned, undef is returned regardless of rhs. | |
| 7443 | // TODO: emit runtime safety for division by zero | |
| 7444 | // | |
| 7445 | // For floats: | |
| 7446 | // If the rhs is zero, compile error for division by zero. | |
| 7447 | // If the rhs is undefined, compile error because there is a possible | |
| 7448 | // value (zero) for which the division would be illegal behavior. | |
| 7449 | // If the lhs is undefined, result is undefined. | |
| 7450 | if (maybe_lhs_val) |lhs_val| { | |
| 7451 | if (!lhs_val.isUndef()) { | |
| 7452 | if (lhs_val.compareWithZero(.eq)) { | |
| 7453 | return sema.addConstant(scalar_type, Value.zero); | |
| 7454 | } | |
| 7455 | } | |
| 7456 | } | |
| 7457 | if (maybe_rhs_val) |rhs_val| { | |
| 7458 | if (rhs_val.isUndef()) { | |
| 7459 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 7460 | } | |
| 7461 | if (rhs_val.compareWithZero(.eq)) { | |
| 7462 | return sema.failWithDivideByZero(block, rhs_src); | |
| 7463 | } | |
| 7464 | } | |
| 7465 | if (maybe_lhs_val) |lhs_val| { | |
| 7466 | if (lhs_val.isUndef()) { | |
| 7467 | if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { | |
| 7468 | if (maybe_rhs_val) |rhs_val| { | |
| 7469 | if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { | |
| 7470 | return sema.addConstUndef(scalar_type); | |
| 7471 | } | |
| 7472 | } | |
| 7473 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 7474 | } | |
| 7475 | return sema.addConstUndef(scalar_type); | |
| 7476 | } | |
| 7477 | ||
| 7478 | if (maybe_rhs_val) |rhs_val| { | |
| 7479 | if (is_int) { | |
| 7480 | return sema.addConstant( | |
| 7481 | scalar_type, | |
| 7482 | try lhs_val.intDiv(rhs_val, sema.arena), | |
| 7483 | ); | |
| 7484 | } else { | |
| 7485 | return sema.addConstant( | |
| 7486 | scalar_type, | |
| 7487 | try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena), | |
| 7488 | ); | |
| 7489 | } | |
| 7490 | } else break :rs .{ .src = rhs_src, .air_tag = .div_trunc }; | |
| 7491 | } else break :rs .{ .src = lhs_src, .air_tag = .div_trunc }; | |
| 7492 | }, | |
| 7493 | .div_floor => { | |
| 7494 | // For integers: | |
| 7495 | // If the lhs is zero, then zero is returned regardless of rhs. | |
| 7496 | // If the rhs is zero, compile error for division by zero. | |
| 7497 | // If the rhs is undefined, compile error because there is a possible | |
| 7498 | // value (zero) for which the division would be illegal behavior. | |
| 7499 | // If the lhs is undefined: | |
| 7500 | // * if lhs type is signed: | |
| 7501 | // * if rhs is comptime-known and not -1, result is undefined | |
| 7502 | // * if rhs is -1 or runtime-known, compile error because there is a | |
| 7503 | // possible value (-min_int / -1) for which division would be | |
| 7504 | // illegal behavior. | |
| 7505 | // * if lhs type is unsigned, undef is returned regardless of rhs. | |
| 7506 | // TODO: emit runtime safety for division by zero | |
| 7507 | // | |
| 7508 | // For floats: | |
| 7509 | // If the rhs is zero, compile error for division by zero. | |
| 7510 | // If the rhs is undefined, compile error because there is a possible | |
| 7511 | // value (zero) for which the division would be illegal behavior. | |
| 7512 | // If the lhs is undefined, result is undefined. | |
| 7513 | if (maybe_lhs_val) |lhs_val| { | |
| 7514 | if (!lhs_val.isUndef()) { | |
| 7515 | if (lhs_val.compareWithZero(.eq)) { | |
| 7516 | return sema.addConstant(scalar_type, Value.zero); | |
| 7517 | } | |
| 7518 | } | |
| 7519 | } | |
| 7520 | if (maybe_rhs_val) |rhs_val| { | |
| 7521 | if (rhs_val.isUndef()) { | |
| 7522 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 7523 | } | |
| 7524 | if (rhs_val.compareWithZero(.eq)) { | |
| 7525 | return sema.failWithDivideByZero(block, rhs_src); | |
| 7526 | } | |
| 7527 | } | |
| 7528 | if (maybe_lhs_val) |lhs_val| { | |
| 7529 | if (lhs_val.isUndef()) { | |
| 7530 | if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { | |
| 7531 | if (maybe_rhs_val) |rhs_val| { | |
| 7532 | if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { | |
| 7533 | return sema.addConstUndef(scalar_type); | |
| 7534 | } | |
| 7535 | } | |
| 7536 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 7537 | } | |
| 7538 | return sema.addConstUndef(scalar_type); | |
| 7539 | } | |
| 7540 | ||
| 7541 | if (maybe_rhs_val) |rhs_val| { | |
| 7542 | if (is_int) { | |
| 7543 | return sema.addConstant( | |
| 7544 | scalar_type, | |
| 7545 | try lhs_val.intDivFloor(rhs_val, sema.arena), | |
| 7546 | ); | |
| 7547 | } else { | |
| 7548 | return sema.addConstant( | |
| 7549 | scalar_type, | |
| 7550 | try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena), | |
| 7551 | ); | |
| 7552 | } | |
| 7553 | } else break :rs .{ .src = rhs_src, .air_tag = .div_floor }; | |
| 7554 | } else break :rs .{ .src = lhs_src, .air_tag = .div_floor }; | |
| 7555 | }, | |
| 7556 | .div_exact => { | |
| 7557 | // For integers: | |
| 7558 | // If the lhs is zero, then zero is returned regardless of rhs. | |
| 7559 | // If the rhs is zero, compile error for division by zero. | |
| 7560 | // If the rhs is undefined, compile error because there is a possible | |
| 7561 | // value (zero) for which the division would be illegal behavior. | |
| 7562 | // If the lhs is undefined, compile error because there is a possible | |
| 7563 | // value for which the division would result in a remainder. | |
| 7564 | // TODO: emit runtime safety for if there is a remainder | |
| 7565 | // TODO: emit runtime safety for division by zero | |
| 7566 | // | |
| 7567 | // For floats: | |
| 7568 | // If the rhs is zero, compile error for division by zero. | |
| 7569 | // If the rhs is undefined, compile error because there is a possible | |
| 7570 | // value (zero) for which the division would be illegal behavior. | |
| 7571 | // If the lhs is undefined, compile error because there is a possible | |
| 7572 | // value for which the division would result in a remainder. | |
| 7573 | if (maybe_lhs_val) |lhs_val| { | |
| 7574 | if (lhs_val.isUndef()) { | |
| 7575 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 7576 | } else { | |
| 7577 | if (lhs_val.compareWithZero(.eq)) { | |
| 7578 | return sema.addConstant(scalar_type, Value.zero); | |
| 7579 | } | |
| 7580 | } | |
| 7581 | } | |
| 7582 | if (maybe_rhs_val) |rhs_val| { | |
| 7583 | if (rhs_val.isUndef()) { | |
| 7584 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 7585 | } | |
| 7586 | if (rhs_val.compareWithZero(.eq)) { | |
| 7587 | return sema.failWithDivideByZero(block, rhs_src); | |
| 7588 | } | |
| 7589 | } | |
| 7590 | if (maybe_lhs_val) |lhs_val| { | |
| 7591 | if (maybe_rhs_val) |rhs_val| { | |
| 7592 | if (is_int) { | |
| 7593 | // TODO: emit compile error if there is a remainder | |
| 7594 | return sema.addConstant( | |
| 7595 | scalar_type, | |
| 7596 | try lhs_val.intDiv(rhs_val, sema.arena), | |
| 7597 | ); | |
| 7598 | } else { | |
| 7599 | // TODO: emit compile error if there is a remainder | |
| 7600 | return sema.addConstant( | |
| 7601 | scalar_type, | |
| 7602 | try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena), | |
| 7603 | ); | |
| 7604 | } | |
| 7605 | } else break :rs .{ .src = rhs_src, .air_tag = .div_exact }; | |
| 7606 | } else break :rs .{ .src = lhs_src, .air_tag = .div_exact }; | |
| 7412 | 7607 | }, |
| 7413 | 7608 | .mul => { |
| 7414 | 7609 | // For integers: |
| ... | ... | @@ -8824,7 +9019,7 @@ fn analyzeRet( |
| 8824 | 9019 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { |
| 8825 | 9020 | // extend this swich as additional operators are implemented |
| 8826 | 9021 | return switch (tag) { |
| 8827 | .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true, | |
| 9022 | .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true, | |
| 8828 | 9023 | else => false, |
| 8829 | 9024 | }; |
| 8830 | 9025 | } |
| ... | ... | @@ -9602,24 +9797,6 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 9602 | 9797 | return sema.fail(block, src, "TODO: Sema.zirBitReverse", .{}); |
| 9603 | 9798 | } |
| 9604 | 9799 | |
| 9605 | fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 9606 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 9607 | const src = inst_data.src(); | |
| 9608 | return sema.fail(block, src, "TODO: Sema.zirDivExact", .{}); | |
| 9609 | } | |
| 9610 | ||
| 9611 | fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 9612 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 9613 | const src = inst_data.src(); | |
| 9614 | return sema.fail(block, src, "TODO: Sema.zirDivFloor", .{}); | |
| 9615 | } | |
| 9616 | ||
| 9617 | fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 9618 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 9619 | const src = inst_data.src(); | |
| 9620 | return sema.fail(block, src, "TODO: Sema.zirDivTrunc", .{}); | |
| 9621 | } | |
| 9622 | ||
| 9623 | 9800 | fn zirShrExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9624 | 9801 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 9625 | 9802 | const src = inst_data.src(); |
src/arch/aarch64/CodeGen.zig+2-1| ... | ... | @@ -410,7 +410,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 410 | 410 | .mul => try self.airMul(inst), |
| 411 | 411 | .mulwrap => try self.airMulWrap(inst), |
| 412 | 412 | .mul_sat => try self.airMulSat(inst), |
| 413 | .div => try self.airDiv(inst), | |
| 414 | 413 | .rem => try self.airRem(inst), |
| 415 | 414 | .mod => try self.airMod(inst), |
| 416 | 415 | .shl, .shl_exact => try self.airShl(inst), |
| ... | ... | @@ -419,6 +418,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 419 | 418 | .max => try self.airMax(inst), |
| 420 | 419 | .slice => try self.airSlice(inst), |
| 421 | 420 | |
| 421 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), | |
| 422 | ||
| 422 | 423 | .cmp_lt => try self.airCmp(inst, .lt), |
| 423 | 424 | .cmp_lte => try self.airCmp(inst, .lte), |
| 424 | 425 | .cmp_eq => try self.airCmp(inst, .eq), |
src/codegen.zig+2-1| ... | ... | @@ -758,7 +758,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 758 | 758 | .mul => try self.airMul(inst), |
| 759 | 759 | .mulwrap => try self.airMulWrap(inst), |
| 760 | 760 | .mul_sat => try self.airMulSat(inst), |
| 761 | .div => try self.airDiv(inst), | |
| 762 | 761 | .rem => try self.airRem(inst), |
| 763 | 762 | .mod => try self.airMod(inst), |
| 764 | 763 | .shl, .shl_exact => try self.airShl(inst), |
| ... | ... | @@ -767,6 +766,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 767 | 766 | .max => try self.airMax(inst), |
| 768 | 767 | .slice => try self.airSlice(inst), |
| 769 | 768 | |
| 769 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), | |
| 770 | ||
| 770 | 771 | .cmp_lt => try self.airCmp(inst, .lt), |
| 771 | 772 | .cmp_lte => try self.airCmp(inst, .lte), |
| 772 | 773 | .cmp_eq => try self.airCmp(inst, .eq), |
src/codegen/c.zig+2-1| ... | ... | @@ -976,7 +976,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 976 | 976 | .mul => try airBinOp (f, inst, " * "), |
| 977 | 977 | // TODO use a different strategy for div that communicates to the optimizer |
| 978 | 978 | // that wrapping is UB. |
| 979 | .div => try airBinOp( f, inst, " / "), | |
| 979 | .div_float, .div_exact, .div_trunc => try airBinOp( f, inst, " / "), | |
| 980 | .div_floor => try airBinOp( f, inst, " divfloor "), | |
| 980 | 981 | .rem => try airBinOp( f, inst, " % "), |
| 981 | 982 | .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division |
| 982 | 983 |
src/codegen/llvm.zig+96-5| ... | ... | @@ -1667,7 +1667,10 @@ pub const FuncGen = struct { |
| 1667 | 1667 | .mul => try self.airMul(inst), |
| 1668 | 1668 | .mulwrap => try self.airMulWrap(inst), |
| 1669 | 1669 | .mul_sat => try self.airMulSat(inst), |
| 1670 | .div => try self.airDiv(inst), | |
| 1670 | .div_float => try self.airDivFloat(inst), | |
| 1671 | .div_trunc => try self.airDivTrunc(inst), | |
| 1672 | .div_floor => try self.airDivFloor(inst), | |
| 1673 | .div_exact => try self.airDivExact(inst), | |
| 1671 | 1674 | .rem => try self.airRem(inst), |
| 1672 | 1675 | .mod => try self.airMod(inst), |
| 1673 | 1676 | .ptr_add => try self.airPtrAdd(inst), |
| ... | ... | @@ -2830,20 +2833,81 @@ pub const FuncGen = struct { |
| 2830 | 2833 | return self.builder.buildUMulFixSat(lhs, rhs, ""); |
| 2831 | 2834 | } |
| 2832 | 2835 | |
| 2833 | fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2834 | if (self.liveness.isUnused(inst)) | |
| 2835 | return null; | |
| 2836 | fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2837 | if (self.liveness.isUnused(inst)) return null; | |
| 2838 | ||
| 2839 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2840 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2841 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2842 | ||
| 2843 | return self.builder.buildFDiv(lhs, rhs, ""); | |
| 2844 | } | |
| 2845 | ||
| 2846 | fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2847 | if (self.liveness.isUnused(inst)) return null; | |
| 2836 | 2848 | |
| 2837 | 2849 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2838 | 2850 | const lhs = try self.resolveInst(bin_op.lhs); |
| 2839 | 2851 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2840 | 2852 | const inst_ty = self.air.typeOfIndex(inst); |
| 2841 | 2853 | |
| 2842 | if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, ""); | |
| 2854 | if (inst_ty.isRuntimeFloat()) { | |
| 2855 | const result_llvm_ty = try self.dg.llvmType(inst_ty); | |
| 2856 | const zero = result_llvm_ty.constNull(); | |
| 2857 | const result = self.builder.buildFDiv(lhs, rhs, ""); | |
| 2858 | const ceiled = try self.callCeil(result, inst_ty); | |
| 2859 | const floored = try self.callFloor(result, inst_ty); | |
| 2860 | const ltz = self.builder.buildFCmp(.OLT, lhs, zero, ""); | |
| 2861 | return self.builder.buildSelect(ltz, ceiled, floored, ""); | |
| 2862 | } | |
| 2843 | 2863 | if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); |
| 2844 | 2864 | return self.builder.buildUDiv(lhs, rhs, ""); |
| 2845 | 2865 | } |
| 2846 | 2866 | |
| 2867 | fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2868 | if (self.liveness.isUnused(inst)) return null; | |
| 2869 | ||
| 2870 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2871 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2872 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2873 | const inst_ty = self.air.typeOfIndex(inst); | |
| 2874 | ||
| 2875 | if (inst_ty.isRuntimeFloat()) { | |
| 2876 | const result = self.builder.buildFDiv(lhs, rhs, ""); | |
| 2877 | return try self.callFloor(result, inst_ty); | |
| 2878 | } | |
| 2879 | if (inst_ty.isSignedInt()) { | |
| 2880 | // const d = @divTrunc(a, b); | |
| 2881 | // const r = @rem(a, b); | |
| 2882 | // return if (r == 0) d else d - ((a < 0) ^ (b < 0)); | |
| 2883 | const result_llvm_ty = try self.dg.llvmType(inst_ty); | |
| 2884 | const zero = result_llvm_ty.constNull(); | |
| 2885 | const div_trunc = self.builder.buildSDiv(lhs, rhs, ""); | |
| 2886 | const rem = self.builder.buildSRem(lhs, rhs, ""); | |
| 2887 | const rem_eq_0 = self.builder.buildICmp(.EQ, rem, zero, ""); | |
| 2888 | const a_lt_0 = self.builder.buildICmp(.SLT, lhs, zero, ""); | |
| 2889 | const b_lt_0 = self.builder.buildICmp(.SLT, rhs, zero, ""); | |
| 2890 | const a_b_xor = self.builder.buildXor(a_lt_0, b_lt_0, ""); | |
| 2891 | const a_b_xor_ext = self.builder.buildZExt(a_b_xor, div_trunc.typeOf(), ""); | |
| 2892 | const d_sub_xor = self.builder.buildSub(div_trunc, a_b_xor_ext, ""); | |
| 2893 | return self.builder.buildSelect(rem_eq_0, div_trunc, d_sub_xor, ""); | |
| 2894 | } | |
| 2895 | return self.builder.buildUDiv(lhs, rhs, ""); | |
| 2896 | } | |
| 2897 | ||
| 2898 | fn airDivExact(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2899 | if (self.liveness.isUnused(inst)) return null; | |
| 2900 | ||
| 2901 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2902 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2903 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2904 | const inst_ty = self.air.typeOfIndex(inst); | |
| 2905 | ||
| 2906 | if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, ""); | |
| 2907 | if (inst_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, ""); | |
| 2908 | return self.builder.buildExactUDiv(lhs, rhs, ""); | |
| 2909 | } | |
| 2910 | ||
| 2847 | 2911 | fn airRem(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 2848 | 2912 | if (self.liveness.isUnused(inst)) return null; |
| 2849 | 2913 | |
| ... | ... | @@ -3546,6 +3610,33 @@ pub const FuncGen = struct { |
| 3546 | 3610 | } |
| 3547 | 3611 | } |
| 3548 | 3612 | |
| 3613 | fn callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value { | |
| 3614 | return self.callFloatUnary(arg, ty, "floor"); | |
| 3615 | } | |
| 3616 | ||
| 3617 | fn callCeil(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value { | |
| 3618 | return self.callFloatUnary(arg, ty, "ceil"); | |
| 3619 | } | |
| 3620 | ||
| 3621 | fn callFloatUnary(self: *FuncGen, arg: *const llvm.Value, ty: Type, name: []const u8) !*const llvm.Value { | |
| 3622 | const target = self.dg.module.getTarget(); | |
| 3623 | ||
| 3624 | var fn_name_buf: [100]u8 = undefined; | |
| 3625 | const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.f{d}", .{ | |
| 3626 | name, ty.floatBits(target), | |
| 3627 | }) catch unreachable; | |
| 3628 | ||
| 3629 | const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { | |
| 3630 | const operand_llvm_ty = try self.dg.llvmType(ty); | |
| 3631 | const param_types = [_]*const llvm.Type{operand_llvm_ty}; | |
| 3632 | const fn_type = llvm.functionType(operand_llvm_ty, &param_types, param_types.len, .False); | |
| 3633 | break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); | |
| 3634 | }; | |
| 3635 | ||
| 3636 | const args: [1]*const llvm.Value = .{arg}; | |
| 3637 | return self.builder.buildCall(llvm_fn, &args, args.len, .C, .Auto, ""); | |
| 3638 | } | |
| 3639 | ||
| 3549 | 3640 | fn fieldPtr( |
| 3550 | 3641 | self: *FuncGen, |
| 3551 | 3642 | inst: Air.Inst.Index, |
src/codegen/llvm/bindings.zig+6| ... | ... | @@ -756,6 +756,12 @@ pub const Builder = opaque { |
| 756 | 756 | |
| 757 | 757 | pub const buildSMin = ZigLLVMBuildSMin; |
| 758 | 758 | extern fn ZigLLVMBuildSMin(builder: *const Builder, LHS: *const Value, RHS: *const Value, name: [*:0]const u8) *const Value; |
| 759 | ||
| 760 | pub const buildExactUDiv = LLVMBuildExactUDiv; | |
| 761 | extern fn LLVMBuildExactUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 762 | ||
| 763 | pub const buildExactSDiv = LLVMBuildExactSDiv; | |
| 764 | extern fn LLVMBuildExactSDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 759 | 765 | }; |
| 760 | 766 | |
| 761 | 767 | pub const IntPredicate = enum(c_uint) { |
src/codegen/spirv.zig-1| ... | ... | @@ -669,7 +669,6 @@ pub const DeclGen = struct { |
| 669 | 669 | .add, .addwrap => try self.airArithOp(inst, .{.OpFAdd, .OpIAdd, .OpIAdd}), |
| 670 | 670 | .sub, .subwrap => try self.airArithOp(inst, .{.OpFSub, .OpISub, .OpISub}), |
| 671 | 671 | .mul, .mulwrap => try self.airArithOp(inst, .{.OpFMul, .OpIMul, .OpIMul}), |
| 672 | .div => try self.airArithOp(inst, .{.OpFDiv, .OpSDiv, .OpUDiv}), | |
| 673 | 672 | |
| 674 | 673 | .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd), |
| 675 | 674 | .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr), |
src/codegen/wasm.zig+1-1| ... | ... | @@ -822,7 +822,7 @@ pub const Context = struct { |
| 822 | 822 | .subwrap => self.airWrapBinOp(inst, .sub), |
| 823 | 823 | .mul => self.airBinOp(inst, .mul), |
| 824 | 824 | .mulwrap => self.airWrapBinOp(inst, .mul), |
| 825 | .div => self.airBinOp(inst, .div), | |
| 825 | .div_trunc => self.airBinOp(inst, .div), | |
| 826 | 826 | .bit_and => self.airBinOp(inst, .@"and"), |
| 827 | 827 | .bit_or => self.airBinOp(inst, .@"or"), |
| 828 | 828 | .bool_and => self.airBinOp(inst, .@"and"), |
src/print_air.zig+4-1| ... | ... | @@ -111,7 +111,10 @@ const Writer = struct { |
| 111 | 111 | .mul, |
| 112 | 112 | .mulwrap, |
| 113 | 113 | .mul_sat, |
| 114 | .div, | |
| 114 | .div_float, | |
| 115 | .div_trunc, | |
| 116 | .div_floor, | |
| 117 | .div_exact, | |
| 115 | 118 | .rem, |
| 116 | 119 | .mod, |
| 117 | 120 | .ptr_add, |
src/value.zig+100-3| ... | ... | @@ -2200,7 +2200,8 @@ pub const Value = extern union { |
| 2200 | 2200 | const rhs_bigint = rhs.toBigInt(&rhs_space); |
| 2201 | 2201 | const limbs = try arena.alloc( |
| 2202 | 2202 | std.math.big.Limb, |
| 2203 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 2203 | // + 1 for negatives | |
| 2204 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, | |
| 2204 | 2205 | ); |
| 2205 | 2206 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; |
| 2206 | 2207 | result_bigint.bitAnd(lhs_bigint, rhs_bigint); |
| ... | ... | @@ -2264,7 +2265,8 @@ pub const Value = extern union { |
| 2264 | 2265 | const rhs_bigint = rhs.toBigInt(&rhs_space); |
| 2265 | 2266 | const limbs = try arena.alloc( |
| 2266 | 2267 | std.math.big.Limb, |
| 2267 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 2268 | // + 1 for negatives | |
| 2269 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, | |
| 2268 | 2270 | ); |
| 2269 | 2271 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; |
| 2270 | 2272 | result_bigint.bitXor(lhs_bigint, rhs_bigint); |
| ... | ... | @@ -2352,7 +2354,7 @@ pub const Value = extern union { |
| 2352 | 2354 | } |
| 2353 | 2355 | } |
| 2354 | 2356 | |
| 2355 | pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | |
| 2357 | pub fn intDivFloor(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | |
| 2356 | 2358 | // TODO is this a performance issue? maybe we should try the operation without |
| 2357 | 2359 | // resorting to BigInt first. |
| 2358 | 2360 | var lhs_space: Value.BigIntSpace = undefined; |
| ... | ... | @@ -2373,6 +2375,39 @@ pub const Value = extern union { |
| 2373 | 2375 | ); |
| 2374 | 2376 | var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; |
| 2375 | 2377 | var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; |
| 2378 | result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null); | |
| 2379 | const result_limbs = result_q.limbs[0..result_q.len]; | |
| 2380 | ||
| 2381 | if (result_q.positive) { | |
| 2382 | return Value.Tag.int_big_positive.create(allocator, result_limbs); | |
| 2383 | } else { | |
| 2384 | return Value.Tag.int_big_negative.create(allocator, result_limbs); | |
| 2385 | } | |
| 2386 | } | |
| 2387 | ||
| 2388 | pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | |
| 2389 | // TODO is this a performance issue? maybe we should try the operation without | |
| 2390 | // resorting to BigInt first. | |
| 2391 | var lhs_space: Value.BigIntSpace = undefined; | |
| 2392 | var rhs_space: Value.BigIntSpace = undefined; | |
| 2393 | const lhs_bigint = lhs.toBigInt(&lhs_space); | |
| 2394 | const rhs_bigint = rhs.toBigInt(&rhs_space); | |
| 2395 | const limbs_q = try allocator.alloc( | |
| 2396 | std.math.big.Limb, | |
| 2397 | lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1, | |
| 2398 | ); | |
| 2399 | const limbs_r = try allocator.alloc( | |
| 2400 | std.math.big.Limb, | |
| 2401 | // TODO: audit this size, and also consider reworking Sema to re-use Values rather than | |
| 2402 | // always producing new Value objects. | |
| 2403 | rhs_bigint.limbs.len + 1, | |
| 2404 | ); | |
| 2405 | const limbs_buffer = try allocator.alloc( | |
| 2406 | std.math.big.Limb, | |
| 2407 | std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 2408 | ); | |
| 2409 | var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; | |
| 2410 | var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; | |
| 2376 | 2411 | result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null); |
| 2377 | 2412 | const result_limbs = result_r.limbs[0..result_r.len]; |
| 2378 | 2413 | |
| ... | ... | @@ -2662,6 +2697,68 @@ pub const Value = extern union { |
| 2662 | 2697 | } |
| 2663 | 2698 | } |
| 2664 | 2699 | |
| 2700 | pub fn floatDivFloor( | |
| 2701 | lhs: Value, | |
| 2702 | rhs: Value, | |
| 2703 | float_type: Type, | |
| 2704 | arena: *Allocator, | |
| 2705 | ) !Value { | |
| 2706 | switch (float_type.tag()) { | |
| 2707 | .f16 => { | |
| 2708 | const lhs_val = lhs.toFloat(f16); | |
| 2709 | const rhs_val = rhs.toFloat(f16); | |
| 2710 | return Value.Tag.float_16.create(arena, @divFloor(lhs_val, rhs_val)); | |
| 2711 | }, | |
| 2712 | .f32 => { | |
| 2713 | const lhs_val = lhs.toFloat(f32); | |
| 2714 | const rhs_val = rhs.toFloat(f32); | |
| 2715 | return Value.Tag.float_32.create(arena, @divFloor(lhs_val, rhs_val)); | |
| 2716 | }, | |
| 2717 | .f64 => { | |
| 2718 | const lhs_val = lhs.toFloat(f64); | |
| 2719 | const rhs_val = rhs.toFloat(f64); | |
| 2720 | return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val)); | |
| 2721 | }, | |
| 2722 | .f128, .comptime_float, .c_longdouble => { | |
| 2723 | const lhs_val = lhs.toFloat(f128); | |
| 2724 | const rhs_val = rhs.toFloat(f128); | |
| 2725 | return Value.Tag.float_128.create(arena, @divFloor(lhs_val, rhs_val)); | |
| 2726 | }, | |
| 2727 | else => unreachable, | |
| 2728 | } | |
| 2729 | } | |
| 2730 | ||
| 2731 | pub fn floatDivTrunc( | |
| 2732 | lhs: Value, | |
| 2733 | rhs: Value, | |
| 2734 | float_type: Type, | |
| 2735 | arena: *Allocator, | |
| 2736 | ) !Value { | |
| 2737 | switch (float_type.tag()) { | |
| 2738 | .f16 => { | |
| 2739 | const lhs_val = lhs.toFloat(f16); | |
| 2740 | const rhs_val = rhs.toFloat(f16); | |
| 2741 | return Value.Tag.float_16.create(arena, @divTrunc(lhs_val, rhs_val)); | |
| 2742 | }, | |
| 2743 | .f32 => { | |
| 2744 | const lhs_val = lhs.toFloat(f32); | |
| 2745 | const rhs_val = rhs.toFloat(f32); | |
| 2746 | return Value.Tag.float_32.create(arena, @divTrunc(lhs_val, rhs_val)); | |
| 2747 | }, | |
| 2748 | .f64 => { | |
| 2749 | const lhs_val = lhs.toFloat(f64); | |
| 2750 | const rhs_val = rhs.toFloat(f64); | |
| 2751 | return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val)); | |
| 2752 | }, | |
| 2753 | .f128, .comptime_float, .c_longdouble => { | |
| 2754 | const lhs_val = lhs.toFloat(f128); | |
| 2755 | const rhs_val = rhs.toFloat(f128); | |
| 2756 | return Value.Tag.float_128.create(arena, @divTrunc(lhs_val, rhs_val)); | |
| 2757 | }, | |
| 2758 | else => unreachable, | |
| 2759 | } | |
| 2760 | } | |
| 2761 | ||
| 2665 | 2762 | pub fn floatMul( |
| 2666 | 2763 | lhs: Value, |
| 2667 | 2764 | rhs: Value, |
test/behavior.zig+3-2| ... | ... | @@ -33,6 +33,7 @@ test { |
| 33 | 33 | _ = @import("behavior/error.zig"); |
| 34 | 34 | _ = @import("behavior/eval.zig"); |
| 35 | 35 | _ = @import("behavior/floatop.zig"); |
| 36 | _ = @import("behavior/fn.zig"); | |
| 36 | 37 | _ = @import("behavior/for.zig"); |
| 37 | 38 | _ = @import("behavior/generics.zig"); |
| 38 | 39 | _ = @import("behavior/hasdecl.zig"); |
| ... | ... | @@ -126,7 +127,7 @@ test { |
| 126 | 127 | _ = @import("behavior/eval_stage1.zig"); |
| 127 | 128 | _ = @import("behavior/field_parent_ptr.zig"); |
| 128 | 129 | _ = @import("behavior/floatop_stage1.zig"); |
| 129 | _ = @import("behavior/fn.zig"); | |
| 130 | _ = @import("behavior/fn_stage1.zig"); | |
| 130 | 131 | _ = @import("behavior/fn_delegation.zig"); |
| 131 | 132 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); |
| 132 | 133 | _ = @import("behavior/for_stage1.zig"); |
| ... | ... | @@ -150,7 +151,7 @@ test { |
| 150 | 151 | _ = @import("behavior/reflection.zig"); |
| 151 | 152 | { |
| 152 | 153 | // Checklist for getting saturating_arithmetic.zig passing for stage2: |
| 153 | // * add __muloti4 to compiler-rt | |
| 154 | // * add __udivti3 to compiler-rt | |
| 154 | 155 | _ = @import("behavior/saturating_arithmetic.zig"); |
| 155 | 156 | } |
| 156 | 157 | _ = @import("behavior/select.zig"); |
test/behavior/array.zig+52| ... | ... | @@ -112,3 +112,55 @@ test "void arrays" { |
| 112 | 112 | try expect(@sizeOf(@TypeOf(array)) == 0); |
| 113 | 113 | try expect(array.len == 4); |
| 114 | 114 | } |
| 115 | ||
| 116 | test "nested arrays" { | |
| 117 | const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" }; | |
| 118 | for (array_of_strings) |s, i| { | |
| 119 | if (i == 0) try expect(mem.eql(u8, s, "hello")); | |
| 120 | if (i == 1) try expect(mem.eql(u8, s, "this")); | |
| 121 | if (i == 2) try expect(mem.eql(u8, s, "is")); | |
| 122 | if (i == 3) try expect(mem.eql(u8, s, "my")); | |
| 123 | if (i == 4) try expect(mem.eql(u8, s, "thing")); | |
| 124 | } | |
| 125 | } | |
| 126 | ||
| 127 | var s_array: [8]Sub = undefined; | |
| 128 | const Sub = struct { b: u8 }; | |
| 129 | const Str = struct { a: []Sub }; | |
| 130 | test "set global var array via slice embedded in struct" { | |
| 131 | var s = Str{ .a = s_array[0..] }; | |
| 132 | ||
| 133 | s.a[0].b = 1; | |
| 134 | s.a[1].b = 2; | |
| 135 | s.a[2].b = 3; | |
| 136 | ||
| 137 | try expect(s_array[0].b == 1); | |
| 138 | try expect(s_array[1].b == 2); | |
| 139 | try expect(s_array[2].b == 3); | |
| 140 | } | |
| 141 | ||
| 142 | test "implicit comptime in array type size" { | |
| 143 | var arr: [plusOne(10)]bool = undefined; | |
| 144 | try expect(arr.len == 11); | |
| 145 | } | |
| 146 | ||
| 147 | fn plusOne(x: u32) u32 { | |
| 148 | return x + 1; | |
| 149 | } | |
| 150 | ||
| 151 | test "read/write through global variable array of struct fields initialized via array mult" { | |
| 152 | const S = struct { | |
| 153 | fn doTheTest() !void { | |
| 154 | try expect(storage[0].term == 1); | |
| 155 | storage[0] = MyStruct{ .term = 123 }; | |
| 156 | try expect(storage[0].term == 123); | |
| 157 | } | |
| 158 | ||
| 159 | pub const MyStruct = struct { | |
| 160 | term: usize, | |
| 161 | }; | |
| 162 | ||
| 163 | var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1; | |
| 164 | }; | |
| 165 | try S.doTheTest(); | |
| 166 | } |
test/behavior/array_stage1.zig-52| ... | ... | @@ -4,32 +4,6 @@ const mem = std.mem; |
| 4 | 4 | const expect = testing.expect; |
| 5 | 5 | const expectEqual = testing.expectEqual; |
| 6 | 6 | |
| 7 | test "nested arrays" { | |
| 8 | const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" }; | |
| 9 | for (array_of_strings) |s, i| { | |
| 10 | if (i == 0) try expect(mem.eql(u8, s, "hello")); | |
| 11 | if (i == 1) try expect(mem.eql(u8, s, "this")); | |
| 12 | if (i == 2) try expect(mem.eql(u8, s, "is")); | |
| 13 | if (i == 3) try expect(mem.eql(u8, s, "my")); | |
| 14 | if (i == 4) try expect(mem.eql(u8, s, "thing")); | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | var s_array: [8]Sub = undefined; | |
| 19 | const Sub = struct { b: u8 }; | |
| 20 | const Str = struct { a: []Sub }; | |
| 21 | test "set global var array via slice embedded in struct" { | |
| 22 | var s = Str{ .a = s_array[0..] }; | |
| 23 | ||
| 24 | s.a[0].b = 1; | |
| 25 | s.a[1].b = 2; | |
| 26 | s.a[2].b = 3; | |
| 27 | ||
| 28 | try expect(s_array[0].b == 1); | |
| 29 | try expect(s_array[1].b == 2); | |
| 30 | try expect(s_array[2].b == 3); | |
| 31 | } | |
| 32 | ||
| 33 | 7 | test "single-item pointer to array indexing and slicing" { |
| 34 | 8 | try testSingleItemPtrArrayIndexSlice(); |
| 35 | 9 | comptime try testSingleItemPtrArrayIndexSlice(); |
| ... | ... | @@ -75,15 +49,6 @@ test "comptime evaluating function that takes array by value" { |
| 75 | 49 | _ = comptime testArrayByValAtComptime(arr); |
| 76 | 50 | } |
| 77 | 51 | |
| 78 | test "implicit comptime in array type size" { | |
| 79 | var arr: [plusOne(10)]bool = undefined; | |
| 80 | try expect(arr.len == 11); | |
| 81 | } | |
| 82 | ||
| 83 | fn plusOne(x: u32) u32 { | |
| 84 | return x + 1; | |
| 85 | } | |
| 86 | ||
| 87 | 52 | test "runtime initialize array elem and then implicit cast to slice" { |
| 88 | 53 | var two: i32 = 2; |
| 89 | 54 | const x: []const i32 = &[_]i32{two}; |
| ... | ... | @@ -171,23 +136,6 @@ test "double nested array to const slice cast in array literal" { |
| 171 | 136 | comptime try S.entry(2); |
| 172 | 137 | } |
| 173 | 138 | |
| 174 | test "read/write through global variable array of struct fields initialized via array mult" { | |
| 175 | const S = struct { | |
| 176 | fn doTheTest() !void { | |
| 177 | try expect(storage[0].term == 1); | |
| 178 | storage[0] = MyStruct{ .term = 123 }; | |
| 179 | try expect(storage[0].term == 123); | |
| 180 | } | |
| 181 | ||
| 182 | pub const MyStruct = struct { | |
| 183 | term: usize, | |
| 184 | }; | |
| 185 | ||
| 186 | var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1; | |
| 187 | }; | |
| 188 | try S.doTheTest(); | |
| 189 | } | |
| 190 | ||
| 191 | 139 | test "implicit cast zero sized array ptr to slice" { |
| 192 | 140 | { |
| 193 | 141 | var b = "".*; |
test/behavior/eval.zig+49| ... | ... | @@ -402,3 +402,52 @@ test "f64 at compile time is lossy" { |
| 402 | 402 | test { |
| 403 | 403 | comptime try expect(@as(f128, 1 << 113) == 10384593717069655257060992658440192); |
| 404 | 404 | } |
| 405 | ||
| 406 | fn copyWithPartialInline(s: []u32, b: []u8) void { | |
| 407 | comptime var i: usize = 0; | |
| 408 | inline while (i < 4) : (i += 1) { | |
| 409 | s[i] = 0; | |
| 410 | s[i] |= @as(u32, b[i * 4 + 0]) << 24; | |
| 411 | s[i] |= @as(u32, b[i * 4 + 1]) << 16; | |
| 412 | s[i] |= @as(u32, b[i * 4 + 2]) << 8; | |
| 413 | s[i] |= @as(u32, b[i * 4 + 3]) << 0; | |
| 414 | } | |
| 415 | } | |
| 416 | ||
| 417 | test "binary math operator in partially inlined function" { | |
| 418 | var s: [4]u32 = undefined; | |
| 419 | var b: [16]u8 = undefined; | |
| 420 | ||
| 421 | for (b) |*r, i| | |
| 422 | r.* = @intCast(u8, i + 1); | |
| 423 | ||
| 424 | copyWithPartialInline(s[0..], b[0..]); | |
| 425 | try expect(s[0] == 0x1020304); | |
| 426 | try expect(s[1] == 0x5060708); | |
| 427 | try expect(s[2] == 0x90a0b0c); | |
| 428 | try expect(s[3] == 0xd0e0f10); | |
| 429 | } | |
| 430 | ||
| 431 | test "comptime shl" { | |
| 432 | var a: u128 = 3; | |
| 433 | var b: u7 = 63; | |
| 434 | var c: u128 = 3 << 63; | |
| 435 | try expect((a << b) == c); | |
| 436 | } | |
| 437 | ||
| 438 | test "comptime bitwise operators" { | |
| 439 | comptime { | |
| 440 | try expect(3 & 1 == 1); | |
| 441 | try expect(3 & -1 == 3); | |
| 442 | try expect(-3 & -1 == -3); | |
| 443 | try expect(3 | -1 == -1); | |
| 444 | try expect(-3 | -1 == -1); | |
| 445 | try expect(3 ^ -1 == -4); | |
| 446 | try expect(-3 ^ -1 == 2); | |
| 447 | try expect(~@as(i8, -1) == 0); | |
| 448 | try expect(~@as(i128, -1) == 0); | |
| 449 | try expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611); | |
| 450 | try expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); | |
| 451 | try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff); | |
| 452 | } | |
| 453 | } |
test/behavior/eval_stage1.zig-49| ... | ... | @@ -132,31 +132,6 @@ test "string literal used as comptime slice is memoized" { |
| 132 | 132 | comptime try expect(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); |
| 133 | 133 | } |
| 134 | 134 | |
| 135 | fn copyWithPartialInline(s: []u32, b: []u8) void { | |
| 136 | comptime var i: usize = 0; | |
| 137 | inline while (i < 4) : (i += 1) { | |
| 138 | s[i] = 0; | |
| 139 | s[i] |= @as(u32, b[i * 4 + 0]) << 24; | |
| 140 | s[i] |= @as(u32, b[i * 4 + 1]) << 16; | |
| 141 | s[i] |= @as(u32, b[i * 4 + 2]) << 8; | |
| 142 | s[i] |= @as(u32, b[i * 4 + 3]) << 0; | |
| 143 | } | |
| 144 | } | |
| 145 | ||
| 146 | test "binary math operator in partially inlined function" { | |
| 147 | var s: [4]u32 = undefined; | |
| 148 | var b: [16]u8 = undefined; | |
| 149 | ||
| 150 | for (b) |*r, i| | |
| 151 | r.* = @intCast(u8, i + 1); | |
| 152 | ||
| 153 | copyWithPartialInline(s[0..], b[0..]); | |
| 154 | try expect(s[0] == 0x1020304); | |
| 155 | try expect(s[1] == 0x5060708); | |
| 156 | try expect(s[2] == 0x90a0b0c); | |
| 157 | try expect(s[3] == 0xd0e0f10); | |
| 158 | } | |
| 159 | ||
| 160 | 135 | test "comptime function with mutable pointer is not memoized" { |
| 161 | 136 | comptime { |
| 162 | 137 | var x: i32 = 1; |
| ... | ... | @@ -203,13 +178,6 @@ test "comptime shlWithOverflow" { |
| 203 | 178 | try expect(ct_shifted == rt_shifted); |
| 204 | 179 | } |
| 205 | 180 | |
| 206 | test "comptime shl" { | |
| 207 | var a: u128 = 3; | |
| 208 | var b: u7 = 63; | |
| 209 | var c: u128 = 3 << 63; | |
| 210 | try expectEqual(a << b, c); | |
| 211 | } | |
| 212 | ||
| 213 | 181 | test "runtime 128 bit integer division" { |
| 214 | 182 | var a: u128 = 152313999999999991610955792383; |
| 215 | 183 | var b: u128 = 10000000000000000000; |
| ... | ... | @@ -278,23 +246,6 @@ test "bit shift a u1" { |
| 278 | 246 | try expect(y == 1); |
| 279 | 247 | } |
| 280 | 248 | |
| 281 | test "comptime bitwise operators" { | |
| 282 | comptime { | |
| 283 | try expect(3 & 1 == 1); | |
| 284 | try expect(3 & -1 == 3); | |
| 285 | try expect(-3 & -1 == -3); | |
| 286 | try expect(3 | -1 == -1); | |
| 287 | try expect(-3 | -1 == -1); | |
| 288 | try expect(3 ^ -1 == -4); | |
| 289 | try expect(-3 ^ -1 == 2); | |
| 290 | try expect(~@as(i8, -1) == 0); | |
| 291 | try expect(~@as(i128, -1) == 0); | |
| 292 | try expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611); | |
| 293 | try expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); | |
| 294 | try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff); | |
| 295 | } | |
| 296 | } | |
| 297 | ||
| 298 | 249 | test "*align(1) u16 is the same as *align(1:0:2) u16" { |
| 299 | 250 | comptime { |
| 300 | 251 | try expect(*align(1:0:2) u16 == *align(1) u16); |
test/behavior/fn.zig-201| ... | ... | @@ -19,17 +19,6 @@ fn testLocVars(b: i32) void { |
| 19 | 19 | if (a + b != 3) unreachable; |
| 20 | 20 | } |
| 21 | 21 | |
| 22 | test "void parameters" { | |
| 23 | try voidFun(1, void{}, 2, {}); | |
| 24 | } | |
| 25 | fn voidFun(a: i32, b: void, c: i32, d: void) !void { | |
| 26 | _ = d; | |
| 27 | const v = b; | |
| 28 | const vv: void = if (a == 1) v else {}; | |
| 29 | try expect(a + c == 3); | |
| 30 | return vv; | |
| 31 | } | |
| 32 | ||
| 33 | 22 | test "mutable local variables" { |
| 34 | 23 | var zero: i32 = 0; |
| 35 | 24 | try expect(zero == 0); |
| ... | ... | @@ -54,14 +43,6 @@ test "separate block scopes" { |
| 54 | 43 | try expect(c == 10); |
| 55 | 44 | } |
| 56 | 45 | |
| 57 | test "call function with empty string" { | |
| 58 | acceptsString(""); | |
| 59 | } | |
| 60 | ||
| 61 | fn acceptsString(foo: []u8) void { | |
| 62 | _ = foo; | |
| 63 | } | |
| 64 | ||
| 65 | 46 | fn @"weird function name"() i32 { |
| 66 | 47 | return 1234; |
| 67 | 48 | } |
| ... | ... | @@ -69,51 +50,6 @@ test "weird function name" { |
| 69 | 50 | try expect(@"weird function name"() == 1234); |
| 70 | 51 | } |
| 71 | 52 | |
| 72 | test "implicit cast function unreachable return" { | |
| 73 | wantsFnWithVoid(fnWithUnreachable); | |
| 74 | } | |
| 75 | ||
| 76 | fn wantsFnWithVoid(f: fn () void) void { | |
| 77 | _ = f; | |
| 78 | } | |
| 79 | ||
| 80 | fn fnWithUnreachable() noreturn { | |
| 81 | unreachable; | |
| 82 | } | |
| 83 | ||
| 84 | test "function pointers" { | |
| 85 | const fns = [_]@TypeOf(fn1){ | |
| 86 | fn1, | |
| 87 | fn2, | |
| 88 | fn3, | |
| 89 | fn4, | |
| 90 | }; | |
| 91 | for (fns) |f, i| { | |
| 92 | try expect(f() == @intCast(u32, i) + 5); | |
| 93 | } | |
| 94 | } | |
| 95 | fn fn1() u32 { | |
| 96 | return 5; | |
| 97 | } | |
| 98 | fn fn2() u32 { | |
| 99 | return 6; | |
| 100 | } | |
| 101 | fn fn3() u32 { | |
| 102 | return 7; | |
| 103 | } | |
| 104 | fn fn4() u32 { | |
| 105 | return 8; | |
| 106 | } | |
| 107 | ||
| 108 | test "number literal as an argument" { | |
| 109 | try numberLiteralArg(3); | |
| 110 | comptime try numberLiteralArg(3); | |
| 111 | } | |
| 112 | ||
| 113 | fn numberLiteralArg(a: anytype) !void { | |
| 114 | try expect(a == 3); | |
| 115 | } | |
| 116 | ||
| 117 | 53 | test "assign inline fn to const variable" { |
| 118 | 54 | const a = inlineFn; |
| 119 | 55 | a(); |
| ... | ... | @@ -121,64 +57,6 @@ test "assign inline fn to const variable" { |
| 121 | 57 | |
| 122 | 58 | inline fn inlineFn() void {} |
| 123 | 59 | |
| 124 | test "pass by non-copying value" { | |
| 125 | try expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); | |
| 126 | } | |
| 127 | ||
| 128 | const Point = struct { | |
| 129 | x: i32, | |
| 130 | y: i32, | |
| 131 | }; | |
| 132 | ||
| 133 | fn addPointCoords(pt: Point) i32 { | |
| 134 | return pt.x + pt.y; | |
| 135 | } | |
| 136 | ||
| 137 | test "pass by non-copying value through var arg" { | |
| 138 | try expect((try addPointCoordsVar(Point{ .x = 1, .y = 2 })) == 3); | |
| 139 | } | |
| 140 | ||
| 141 | fn addPointCoordsVar(pt: anytype) !i32 { | |
| 142 | comptime try expect(@TypeOf(pt) == Point); | |
| 143 | return pt.x + pt.y; | |
| 144 | } | |
| 145 | ||
| 146 | test "pass by non-copying value as method" { | |
| 147 | var pt = Point2{ .x = 1, .y = 2 }; | |
| 148 | try expect(pt.addPointCoords() == 3); | |
| 149 | } | |
| 150 | ||
| 151 | const Point2 = struct { | |
| 152 | x: i32, | |
| 153 | y: i32, | |
| 154 | ||
| 155 | fn addPointCoords(self: Point2) i32 { | |
| 156 | return self.x + self.y; | |
| 157 | } | |
| 158 | }; | |
| 159 | ||
| 160 | test "pass by non-copying value as method, which is generic" { | |
| 161 | var pt = Point3{ .x = 1, .y = 2 }; | |
| 162 | try expect(pt.addPointCoords(i32) == 3); | |
| 163 | } | |
| 164 | ||
| 165 | const Point3 = struct { | |
| 166 | x: i32, | |
| 167 | y: i32, | |
| 168 | ||
| 169 | fn addPointCoords(self: Point3, comptime T: type) i32 { | |
| 170 | _ = T; | |
| 171 | return self.x + self.y; | |
| 172 | } | |
| 173 | }; | |
| 174 | ||
| 175 | test "pass by non-copying value as method, at comptime" { | |
| 176 | comptime { | |
| 177 | var pt = Point2{ .x = 1, .y = 2 }; | |
| 178 | try expect(pt.addPointCoords() == 3); | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 182 | 60 | fn outer(y: u32) fn (u32) u32 { |
| 183 | 61 | const Y = @TypeOf(y); |
| 184 | 62 | const st = struct { |
| ... | ... | @@ -194,43 +72,6 @@ test "return inner function which references comptime variable of outer function |
| 194 | 72 | try expect(func(3) == 7); |
| 195 | 73 | } |
| 196 | 74 | |
| 197 | test "extern struct with stdcallcc fn pointer" { | |
| 198 | const S = extern struct { | |
| 199 | ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32, | |
| 200 | ||
| 201 | fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 { | |
| 202 | return 1234; | |
| 203 | } | |
| 204 | }; | |
| 205 | ||
| 206 | var s: S = undefined; | |
| 207 | s.ptr = S.foo; | |
| 208 | try expect(s.ptr() == 1234); | |
| 209 | } | |
| 210 | ||
| 211 | test "implicit cast fn call result to optional in field result" { | |
| 212 | const S = struct { | |
| 213 | fn entry() !void { | |
| 214 | var x = Foo{ | |
| 215 | .field = optionalPtr(), | |
| 216 | }; | |
| 217 | try expect(x.field.?.* == 999); | |
| 218 | } | |
| 219 | ||
| 220 | const glob: i32 = 999; | |
| 221 | ||
| 222 | fn optionalPtr() *const i32 { | |
| 223 | return &glob; | |
| 224 | } | |
| 225 | ||
| 226 | const Foo = struct { | |
| 227 | field: ?*const i32, | |
| 228 | }; | |
| 229 | }; | |
| 230 | try S.entry(); | |
| 231 | comptime try S.entry(); | |
| 232 | } | |
| 233 | ||
| 234 | 75 | test "discard the result of a function that returns a struct" { |
| 235 | 76 | const S = struct { |
| 236 | 77 | fn entry() void { |
| ... | ... | @@ -249,45 +90,3 @@ test "discard the result of a function that returns a struct" { |
| 249 | 90 | S.entry(); |
| 250 | 91 | comptime S.entry(); |
| 251 | 92 | } |
| 252 | ||
| 253 | test "function call with anon list literal" { | |
| 254 | const S = struct { | |
| 255 | fn doTheTest() !void { | |
| 256 | try consumeVec(.{ 9, 8, 7 }); | |
| 257 | } | |
| 258 | ||
| 259 | fn consumeVec(vec: [3]f32) !void { | |
| 260 | try expect(vec[0] == 9); | |
| 261 | try expect(vec[1] == 8); | |
| 262 | try expect(vec[2] == 7); | |
| 263 | } | |
| 264 | }; | |
| 265 | try S.doTheTest(); | |
| 266 | comptime try S.doTheTest(); | |
| 267 | } | |
| 268 | ||
| 269 | test "ability to give comptime types and non comptime types to same parameter" { | |
| 270 | const S = struct { | |
| 271 | fn doTheTest() !void { | |
| 272 | var x: i32 = 1; | |
| 273 | try expect(foo(x) == 10); | |
| 274 | try expect(foo(i32) == 20); | |
| 275 | } | |
| 276 | ||
| 277 | fn foo(arg: anytype) i32 { | |
| 278 | if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20; | |
| 279 | return 9 + arg; | |
| 280 | } | |
| 281 | }; | |
| 282 | try S.doTheTest(); | |
| 283 | comptime try S.doTheTest(); | |
| 284 | } | |
| 285 | ||
| 286 | test "function with inferred error set but returning no error" { | |
| 287 | const S = struct { | |
| 288 | fn foo() !void {} | |
| 289 | }; | |
| 290 | ||
| 291 | const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?; | |
| 292 | try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len); | |
| 293 | } |
test/behavior/fn_stage1.zig created+206| ... | ... | @@ -0,0 +1,206 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const testing = std.testing; | |
| 4 | const expect = testing.expect; | |
| 5 | const expectEqual = testing.expectEqual; | |
| 6 | ||
| 7 | test "void parameters" { | |
| 8 | try voidFun(1, void{}, 2, {}); | |
| 9 | } | |
| 10 | fn voidFun(a: i32, b: void, c: i32, d: void) !void { | |
| 11 | _ = d; | |
| 12 | const v = b; | |
| 13 | const vv: void = if (a == 1) v else {}; | |
| 14 | try expect(a + c == 3); | |
| 15 | return vv; | |
| 16 | } | |
| 17 | ||
| 18 | test "call function with empty string" { | |
| 19 | acceptsString(""); | |
| 20 | } | |
| 21 | ||
| 22 | fn acceptsString(foo: []u8) void { | |
| 23 | _ = foo; | |
| 24 | } | |
| 25 | ||
| 26 | test "implicit cast function unreachable return" { | |
| 27 | wantsFnWithVoid(fnWithUnreachable); | |
| 28 | } | |
| 29 | ||
| 30 | fn wantsFnWithVoid(f: fn () void) void { | |
| 31 | _ = f; | |
| 32 | } | |
| 33 | ||
| 34 | fn fnWithUnreachable() noreturn { | |
| 35 | unreachable; | |
| 36 | } | |
| 37 | ||
| 38 | test "function pointers" { | |
| 39 | const fns = [_]@TypeOf(fn1){ | |
| 40 | fn1, | |
| 41 | fn2, | |
| 42 | fn3, | |
| 43 | fn4, | |
| 44 | }; | |
| 45 | for (fns) |f, i| { | |
| 46 | try expect(f() == @intCast(u32, i) + 5); | |
| 47 | } | |
| 48 | } | |
| 49 | fn fn1() u32 { | |
| 50 | return 5; | |
| 51 | } | |
| 52 | fn fn2() u32 { | |
| 53 | return 6; | |
| 54 | } | |
| 55 | fn fn3() u32 { | |
| 56 | return 7; | |
| 57 | } | |
| 58 | fn fn4() u32 { | |
| 59 | return 8; | |
| 60 | } | |
| 61 | ||
| 62 | test "number literal as an argument" { | |
| 63 | try numberLiteralArg(3); | |
| 64 | comptime try numberLiteralArg(3); | |
| 65 | } | |
| 66 | ||
| 67 | fn numberLiteralArg(a: anytype) !void { | |
| 68 | try expect(a == 3); | |
| 69 | } | |
| 70 | ||
| 71 | test "pass by non-copying value" { | |
| 72 | try expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); | |
| 73 | } | |
| 74 | ||
| 75 | const Point = struct { | |
| 76 | x: i32, | |
| 77 | y: i32, | |
| 78 | }; | |
| 79 | ||
| 80 | fn addPointCoords(pt: Point) i32 { | |
| 81 | return pt.x + pt.y; | |
| 82 | } | |
| 83 | ||
| 84 | test "pass by non-copying value through var arg" { | |
| 85 | try expect((try addPointCoordsVar(Point{ .x = 1, .y = 2 })) == 3); | |
| 86 | } | |
| 87 | ||
| 88 | fn addPointCoordsVar(pt: anytype) !i32 { | |
| 89 | comptime try expect(@TypeOf(pt) == Point); | |
| 90 | return pt.x + pt.y; | |
| 91 | } | |
| 92 | ||
| 93 | test "pass by non-copying value as method" { | |
| 94 | var pt = Point2{ .x = 1, .y = 2 }; | |
| 95 | try expect(pt.addPointCoords() == 3); | |
| 96 | } | |
| 97 | ||
| 98 | const Point2 = struct { | |
| 99 | x: i32, | |
| 100 | y: i32, | |
| 101 | ||
| 102 | fn addPointCoords(self: Point2) i32 { | |
| 103 | return self.x + self.y; | |
| 104 | } | |
| 105 | }; | |
| 106 | ||
| 107 | test "pass by non-copying value as method, which is generic" { | |
| 108 | var pt = Point3{ .x = 1, .y = 2 }; | |
| 109 | try expect(pt.addPointCoords(i32) == 3); | |
| 110 | } | |
| 111 | ||
| 112 | const Point3 = struct { | |
| 113 | x: i32, | |
| 114 | y: i32, | |
| 115 | ||
| 116 | fn addPointCoords(self: Point3, comptime T: type) i32 { | |
| 117 | _ = T; | |
| 118 | return self.x + self.y; | |
| 119 | } | |
| 120 | }; | |
| 121 | ||
| 122 | test "pass by non-copying value as method, at comptime" { | |
| 123 | comptime { | |
| 124 | var pt = Point2{ .x = 1, .y = 2 }; | |
| 125 | try expect(pt.addPointCoords() == 3); | |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | test "extern struct with stdcallcc fn pointer" { | |
| 130 | const S = extern struct { | |
| 131 | ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32, | |
| 132 | ||
| 133 | fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 { | |
| 134 | return 1234; | |
| 135 | } | |
| 136 | }; | |
| 137 | ||
| 138 | var s: S = undefined; | |
| 139 | s.ptr = S.foo; | |
| 140 | try expect(s.ptr() == 1234); | |
| 141 | } | |
| 142 | ||
| 143 | test "implicit cast fn call result to optional in field result" { | |
| 144 | const S = struct { | |
| 145 | fn entry() !void { | |
| 146 | var x = Foo{ | |
| 147 | .field = optionalPtr(), | |
| 148 | }; | |
| 149 | try expect(x.field.?.* == 999); | |
| 150 | } | |
| 151 | ||
| 152 | const glob: i32 = 999; | |
| 153 | ||
| 154 | fn optionalPtr() *const i32 { | |
| 155 | return &glob; | |
| 156 | } | |
| 157 | ||
| 158 | const Foo = struct { | |
| 159 | field: ?*const i32, | |
| 160 | }; | |
| 161 | }; | |
| 162 | try S.entry(); | |
| 163 | comptime try S.entry(); | |
| 164 | } | |
| 165 | ||
| 166 | test "function call with anon list literal" { | |
| 167 | const S = struct { | |
| 168 | fn doTheTest() !void { | |
| 169 | try consumeVec(.{ 9, 8, 7 }); | |
| 170 | } | |
| 171 | ||
| 172 | fn consumeVec(vec: [3]f32) !void { | |
| 173 | try expect(vec[0] == 9); | |
| 174 | try expect(vec[1] == 8); | |
| 175 | try expect(vec[2] == 7); | |
| 176 | } | |
| 177 | }; | |
| 178 | try S.doTheTest(); | |
| 179 | comptime try S.doTheTest(); | |
| 180 | } | |
| 181 | ||
| 182 | test "ability to give comptime types and non comptime types to same parameter" { | |
| 183 | const S = struct { | |
| 184 | fn doTheTest() !void { | |
| 185 | var x: i32 = 1; | |
| 186 | try expect(foo(x) == 10); | |
| 187 | try expect(foo(i32) == 20); | |
| 188 | } | |
| 189 | ||
| 190 | fn foo(arg: anytype) i32 { | |
| 191 | if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20; | |
| 192 | return 9 + arg; | |
| 193 | } | |
| 194 | }; | |
| 195 | try S.doTheTest(); | |
| 196 | comptime try S.doTheTest(); | |
| 197 | } | |
| 198 | ||
| 199 | test "function with inferred error set but returning no error" { | |
| 200 | const S = struct { | |
| 201 | fn foo() !void {} | |
| 202 | }; | |
| 203 | ||
| 204 | const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?; | |
| 205 | try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len); | |
| 206 | } |
test/behavior/math.zig+187| ... | ... | @@ -249,3 +249,190 @@ test "binary not" { |
| 249 | 249 | fn testBinaryNot(x: u16) !void { |
| 250 | 250 | try expect(~x == 0b0101010101010101); |
| 251 | 251 | } |
| 252 | ||
| 253 | test "division" { | |
| 254 | try testDivision(); | |
| 255 | comptime try testDivision(); | |
| 256 | } | |
| 257 | fn testDivision() !void { | |
| 258 | try expect(div(u32, 13, 3) == 4); | |
| 259 | try expect(div(f16, 1.0, 2.0) == 0.5); | |
| 260 | try expect(div(f32, 1.0, 2.0) == 0.5); | |
| 261 | ||
| 262 | try expect(divExact(u32, 55, 11) == 5); | |
| 263 | try expect(divExact(i32, -55, 11) == -5); | |
| 264 | try expect(divExact(f16, 55.0, 11.0) == 5.0); | |
| 265 | try expect(divExact(f16, -55.0, 11.0) == -5.0); | |
| 266 | try expect(divExact(f32, 55.0, 11.0) == 5.0); | |
| 267 | try expect(divExact(f32, -55.0, 11.0) == -5.0); | |
| 268 | ||
| 269 | try expect(divFloor(i32, 5, 3) == 1); | |
| 270 | try expect(divFloor(i32, -5, 3) == -2); | |
| 271 | try expect(divFloor(f16, 5.0, 3.0) == 1.0); | |
| 272 | try expect(divFloor(f16, -5.0, 3.0) == -2.0); | |
| 273 | try expect(divFloor(f32, 5.0, 3.0) == 1.0); | |
| 274 | try expect(divFloor(f32, -5.0, 3.0) == -2.0); | |
| 275 | try expect(divFloor(i32, -0x80000000, -2) == 0x40000000); | |
| 276 | try expect(divFloor(i32, 0, -0x80000000) == 0); | |
| 277 | try expect(divFloor(i32, -0x40000001, 0x40000000) == -2); | |
| 278 | try expect(divFloor(i32, -0x80000000, 1) == -0x80000000); | |
| 279 | try expect(divFloor(i32, 10, 12) == 0); | |
| 280 | try expect(divFloor(i32, -14, 12) == -2); | |
| 281 | try expect(divFloor(i32, -2, 12) == -1); | |
| 282 | ||
| 283 | try expect(divTrunc(i32, 5, 3) == 1); | |
| 284 | try expect(divTrunc(i32, -5, 3) == -1); | |
| 285 | try expect(divTrunc(f16, 5.0, 3.0) == 1.0); | |
| 286 | try expect(divTrunc(f16, -5.0, 3.0) == -1.0); | |
| 287 | try expect(divTrunc(f32, 5.0, 3.0) == 1.0); | |
| 288 | try expect(divTrunc(f32, -5.0, 3.0) == -1.0); | |
| 289 | try expect(divTrunc(f64, 5.0, 3.0) == 1.0); | |
| 290 | try expect(divTrunc(f64, -5.0, 3.0) == -1.0); | |
| 291 | try expect(divTrunc(i32, 10, 12) == 0); | |
| 292 | try expect(divTrunc(i32, -14, 12) == -1); | |
| 293 | try expect(divTrunc(i32, -2, 12) == 0); | |
| 294 | ||
| 295 | try expect(mod(i32, 10, 12) == 10); | |
| 296 | try expect(mod(i32, -14, 12) == 10); | |
| 297 | try expect(mod(i32, -2, 12) == 10); | |
| 298 | ||
| 299 | comptime { | |
| 300 | try expect( | |
| 301 | 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, | |
| 302 | ); | |
| 303 | try expect( | |
| 304 | @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, | |
| 305 | ); | |
| 306 | try expect( | |
| 307 | 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, | |
| 308 | ); | |
| 309 | try expect( | |
| 310 | @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, | |
| 311 | ); | |
| 312 | try expect( | |
| 313 | @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, | |
| 314 | ); | |
| 315 | try expect( | |
| 316 | @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, | |
| 317 | ); | |
| 318 | try expect( | |
| 319 | 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, | |
| 320 | ); | |
| 321 | } | |
| 322 | } | |
| 323 | fn div(comptime T: type, a: T, b: T) T { | |
| 324 | return a / b; | |
| 325 | } | |
| 326 | fn divExact(comptime T: type, a: T, b: T) T { | |
| 327 | return @divExact(a, b); | |
| 328 | } | |
| 329 | fn divFloor(comptime T: type, a: T, b: T) T { | |
| 330 | return @divFloor(a, b); | |
| 331 | } | |
| 332 | fn divTrunc(comptime T: type, a: T, b: T) T { | |
| 333 | return @divTrunc(a, b); | |
| 334 | } | |
| 335 | fn mod(comptime T: type, a: T, b: T) T { | |
| 336 | return @mod(a, b); | |
| 337 | } | |
| 338 | ||
| 339 | test "unsigned wrapping" { | |
| 340 | try testUnsignedWrappingEval(maxInt(u32)); | |
| 341 | comptime try testUnsignedWrappingEval(maxInt(u32)); | |
| 342 | } | |
| 343 | fn testUnsignedWrappingEval(x: u32) !void { | |
| 344 | const zero = x +% 1; | |
| 345 | try expect(zero == 0); | |
| 346 | const orig = zero -% 1; | |
| 347 | try expect(orig == maxInt(u32)); | |
| 348 | } | |
| 349 | ||
| 350 | test "signed wrapping" { | |
| 351 | try testSignedWrappingEval(maxInt(i32)); | |
| 352 | comptime try testSignedWrappingEval(maxInt(i32)); | |
| 353 | } | |
| 354 | fn testSignedWrappingEval(x: i32) !void { | |
| 355 | const min_val = x +% 1; | |
| 356 | try expect(min_val == minInt(i32)); | |
| 357 | const max_val = min_val -% 1; | |
| 358 | try expect(max_val == maxInt(i32)); | |
| 359 | } | |
| 360 | ||
| 361 | test "signed negation wrapping" { | |
| 362 | try testSignedNegationWrappingEval(minInt(i16)); | |
| 363 | comptime try testSignedNegationWrappingEval(minInt(i16)); | |
| 364 | } | |
| 365 | fn testSignedNegationWrappingEval(x: i16) !void { | |
| 366 | try expect(x == -32768); | |
| 367 | const neg = -%x; | |
| 368 | try expect(neg == -32768); | |
| 369 | } | |
| 370 | ||
| 371 | test "unsigned negation wrapping" { | |
| 372 | try testUnsignedNegationWrappingEval(1); | |
| 373 | comptime try testUnsignedNegationWrappingEval(1); | |
| 374 | } | |
| 375 | fn testUnsignedNegationWrappingEval(x: u16) !void { | |
| 376 | try expect(x == 1); | |
| 377 | const neg = -%x; | |
| 378 | try expect(neg == maxInt(u16)); | |
| 379 | } | |
| 380 | ||
| 381 | test "unsigned 64-bit division" { | |
| 382 | try test_u64_div(); | |
| 383 | comptime try test_u64_div(); | |
| 384 | } | |
| 385 | fn test_u64_div() !void { | |
| 386 | const result = divWithResult(1152921504606846976, 34359738365); | |
| 387 | try expect(result.quotient == 33554432); | |
| 388 | try expect(result.remainder == 100663296); | |
| 389 | } | |
| 390 | fn divWithResult(a: u64, b: u64) DivResult { | |
| 391 | return DivResult{ | |
| 392 | .quotient = a / b, | |
| 393 | .remainder = a % b, | |
| 394 | }; | |
| 395 | } | |
| 396 | const DivResult = struct { | |
| 397 | quotient: u64, | |
| 398 | remainder: u64, | |
| 399 | }; | |
| 400 | ||
| 401 | test "truncating shift right" { | |
| 402 | try testShrTrunc(maxInt(u16)); | |
| 403 | comptime try testShrTrunc(maxInt(u16)); | |
| 404 | } | |
| 405 | fn testShrTrunc(x: u16) !void { | |
| 406 | const shifted = x >> 1; | |
| 407 | try expect(shifted == 32767); | |
| 408 | } | |
| 409 | ||
| 410 | test "f128" { | |
| 411 | try test_f128(); | |
| 412 | comptime try test_f128(); | |
| 413 | } | |
| 414 | ||
| 415 | fn make_f128(x: f128) f128 { | |
| 416 | return x; | |
| 417 | } | |
| 418 | ||
| 419 | fn test_f128() !void { | |
| 420 | try expect(@sizeOf(f128) == 16); | |
| 421 | try expect(make_f128(1.0) == 1.0); | |
| 422 | try expect(make_f128(1.0) != 1.1); | |
| 423 | try expect(make_f128(1.0) > 0.9); | |
| 424 | try expect(make_f128(1.0) >= 0.9); | |
| 425 | try expect(make_f128(1.0) >= 1.0); | |
| 426 | try should_not_be_zero(1.0); | |
| 427 | } | |
| 428 | ||
| 429 | fn should_not_be_zero(x: f128) !void { | |
| 430 | try expect(x != 0.0); | |
| 431 | } | |
| 432 | ||
| 433 | test "128-bit multiplication" { | |
| 434 | var a: i128 = 3; | |
| 435 | var b: i128 = 2; | |
| 436 | var c = a * b; | |
| 437 | try expect(c == 6); | |
| 438 | } |
test/behavior/math_stage1.zig-187| ... | ... | @@ -6,92 +6,6 @@ const maxInt = std.math.maxInt; |
| 6 | 6 | const minInt = std.math.minInt; |
| 7 | 7 | const mem = std.mem; |
| 8 | 8 | |
| 9 | test "division" { | |
| 10 | try testDivision(); | |
| 11 | comptime try testDivision(); | |
| 12 | } | |
| 13 | fn testDivision() !void { | |
| 14 | try expect(div(u32, 13, 3) == 4); | |
| 15 | try expect(div(f16, 1.0, 2.0) == 0.5); | |
| 16 | try expect(div(f32, 1.0, 2.0) == 0.5); | |
| 17 | ||
| 18 | try expect(divExact(u32, 55, 11) == 5); | |
| 19 | try expect(divExact(i32, -55, 11) == -5); | |
| 20 | try expect(divExact(f16, 55.0, 11.0) == 5.0); | |
| 21 | try expect(divExact(f16, -55.0, 11.0) == -5.0); | |
| 22 | try expect(divExact(f32, 55.0, 11.0) == 5.0); | |
| 23 | try expect(divExact(f32, -55.0, 11.0) == -5.0); | |
| 24 | ||
| 25 | try expect(divFloor(i32, 5, 3) == 1); | |
| 26 | try expect(divFloor(i32, -5, 3) == -2); | |
| 27 | try expect(divFloor(f16, 5.0, 3.0) == 1.0); | |
| 28 | try expect(divFloor(f16, -5.0, 3.0) == -2.0); | |
| 29 | try expect(divFloor(f32, 5.0, 3.0) == 1.0); | |
| 30 | try expect(divFloor(f32, -5.0, 3.0) == -2.0); | |
| 31 | try expect(divFloor(i32, -0x80000000, -2) == 0x40000000); | |
| 32 | try expect(divFloor(i32, 0, -0x80000000) == 0); | |
| 33 | try expect(divFloor(i32, -0x40000001, 0x40000000) == -2); | |
| 34 | try expect(divFloor(i32, -0x80000000, 1) == -0x80000000); | |
| 35 | try expect(divFloor(i32, 10, 12) == 0); | |
| 36 | try expect(divFloor(i32, -14, 12) == -2); | |
| 37 | try expect(divFloor(i32, -2, 12) == -1); | |
| 38 | ||
| 39 | try expect(divTrunc(i32, 5, 3) == 1); | |
| 40 | try expect(divTrunc(i32, -5, 3) == -1); | |
| 41 | try expect(divTrunc(f16, 5.0, 3.0) == 1.0); | |
| 42 | try expect(divTrunc(f16, -5.0, 3.0) == -1.0); | |
| 43 | try expect(divTrunc(f32, 5.0, 3.0) == 1.0); | |
| 44 | try expect(divTrunc(f32, -5.0, 3.0) == -1.0); | |
| 45 | try expect(divTrunc(f64, 5.0, 3.0) == 1.0); | |
| 46 | try expect(divTrunc(f64, -5.0, 3.0) == -1.0); | |
| 47 | try expect(divTrunc(i32, 10, 12) == 0); | |
| 48 | try expect(divTrunc(i32, -14, 12) == -1); | |
| 49 | try expect(divTrunc(i32, -2, 12) == 0); | |
| 50 | ||
| 51 | try expect(mod(i32, 10, 12) == 10); | |
| 52 | try expect(mod(i32, -14, 12) == 10); | |
| 53 | try expect(mod(i32, -2, 12) == 10); | |
| 54 | ||
| 55 | comptime { | |
| 56 | try expect( | |
| 57 | 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, | |
| 58 | ); | |
| 59 | try expect( | |
| 60 | @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, | |
| 61 | ); | |
| 62 | try expect( | |
| 63 | 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, | |
| 64 | ); | |
| 65 | try expect( | |
| 66 | @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, | |
| 67 | ); | |
| 68 | try expect( | |
| 69 | @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, | |
| 70 | ); | |
| 71 | try expect( | |
| 72 | @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, | |
| 73 | ); | |
| 74 | try expect( | |
| 75 | 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, | |
| 76 | ); | |
| 77 | } | |
| 78 | } | |
| 79 | fn div(comptime T: type, a: T, b: T) T { | |
| 80 | return a / b; | |
| 81 | } | |
| 82 | fn divExact(comptime T: type, a: T, b: T) T { | |
| 83 | return @divExact(a, b); | |
| 84 | } | |
| 85 | fn divFloor(comptime T: type, a: T, b: T) T { | |
| 86 | return @divFloor(a, b); | |
| 87 | } | |
| 88 | fn divTrunc(comptime T: type, a: T, b: T) T { | |
| 89 | return @divTrunc(a, b); | |
| 90 | } | |
| 91 | fn mod(comptime T: type, a: T, b: T) T { | |
| 92 | return @mod(a, b); | |
| 93 | } | |
| 94 | ||
| 95 | 9 | test "@addWithOverflow" { |
| 96 | 10 | var result: u8 = undefined; |
| 97 | 11 | try expect(@addWithOverflow(u8, 250, 100, &result)); |
| ... | ... | @@ -157,68 +71,6 @@ fn testCtzVectors() !void { |
| 157 | 71 | try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16))); |
| 158 | 72 | } |
| 159 | 73 | |
| 160 | test "unsigned wrapping" { | |
| 161 | try testUnsignedWrappingEval(maxInt(u32)); | |
| 162 | comptime try testUnsignedWrappingEval(maxInt(u32)); | |
| 163 | } | |
| 164 | fn testUnsignedWrappingEval(x: u32) !void { | |
| 165 | const zero = x +% 1; | |
| 166 | try expect(zero == 0); | |
| 167 | const orig = zero -% 1; | |
| 168 | try expect(orig == maxInt(u32)); | |
| 169 | } | |
| 170 | ||
| 171 | test "signed wrapping" { | |
| 172 | try testSignedWrappingEval(maxInt(i32)); | |
| 173 | comptime try testSignedWrappingEval(maxInt(i32)); | |
| 174 | } | |
| 175 | fn testSignedWrappingEval(x: i32) !void { | |
| 176 | const min_val = x +% 1; | |
| 177 | try expect(min_val == minInt(i32)); | |
| 178 | const max_val = min_val -% 1; | |
| 179 | try expect(max_val == maxInt(i32)); | |
| 180 | } | |
| 181 | ||
| 182 | test "signed negation wrapping" { | |
| 183 | try testSignedNegationWrappingEval(minInt(i16)); | |
| 184 | comptime try testSignedNegationWrappingEval(minInt(i16)); | |
| 185 | } | |
| 186 | fn testSignedNegationWrappingEval(x: i16) !void { | |
| 187 | try expect(x == -32768); | |
| 188 | const neg = -%x; | |
| 189 | try expect(neg == -32768); | |
| 190 | } | |
| 191 | ||
| 192 | test "unsigned negation wrapping" { | |
| 193 | try testUnsignedNegationWrappingEval(1); | |
| 194 | comptime try testUnsignedNegationWrappingEval(1); | |
| 195 | } | |
| 196 | fn testUnsignedNegationWrappingEval(x: u16) !void { | |
| 197 | try expect(x == 1); | |
| 198 | const neg = -%x; | |
| 199 | try expect(neg == maxInt(u16)); | |
| 200 | } | |
| 201 | ||
| 202 | test "unsigned 64-bit division" { | |
| 203 | try test_u64_div(); | |
| 204 | comptime try test_u64_div(); | |
| 205 | } | |
| 206 | fn test_u64_div() !void { | |
| 207 | const result = divWithResult(1152921504606846976, 34359738365); | |
| 208 | try expect(result.quotient == 33554432); | |
| 209 | try expect(result.remainder == 100663296); | |
| 210 | } | |
| 211 | fn divWithResult(a: u64, b: u64) DivResult { | |
| 212 | return DivResult{ | |
| 213 | .quotient = a / b, | |
| 214 | .remainder = a % b, | |
| 215 | }; | |
| 216 | } | |
| 217 | const DivResult = struct { | |
| 218 | quotient: u64, | |
| 219 | remainder: u64, | |
| 220 | }; | |
| 221 | ||
| 222 | 74 | test "small int addition" { |
| 223 | 75 | var x: u2 = 0; |
| 224 | 76 | try expect(x == 0); |
| ... | ... | @@ -346,15 +198,6 @@ fn testShlTrunc(x: u16) !void { |
| 346 | 198 | try expect(shifted == 65534); |
| 347 | 199 | } |
| 348 | 200 | |
| 349 | test "truncating shift right" { | |
| 350 | try testShrTrunc(maxInt(u16)); | |
| 351 | comptime try testShrTrunc(maxInt(u16)); | |
| 352 | } | |
| 353 | fn testShrTrunc(x: u16) !void { | |
| 354 | const shifted = x >> 1; | |
| 355 | try expect(shifted == 32767); | |
| 356 | } | |
| 357 | ||
| 358 | 201 | test "exact shift left" { |
| 359 | 202 | try testShlExact(0b00110101); |
| 360 | 203 | comptime try testShlExact(0b00110101); |
| ... | ... | @@ -392,29 +235,6 @@ test "shift left/right on u0 operand" { |
| 392 | 235 | comptime try S.doTheTest(); |
| 393 | 236 | } |
| 394 | 237 | |
| 395 | test "f128" { | |
| 396 | try test_f128(); | |
| 397 | comptime try test_f128(); | |
| 398 | } | |
| 399 | ||
| 400 | fn make_f128(x: f128) f128 { | |
| 401 | return x; | |
| 402 | } | |
| 403 | ||
| 404 | fn test_f128() !void { | |
| 405 | try expect(@sizeOf(f128) == 16); | |
| 406 | try expect(make_f128(1.0) == 1.0); | |
| 407 | try expect(make_f128(1.0) != 1.1); | |
| 408 | try expect(make_f128(1.0) > 0.9); | |
| 409 | try expect(make_f128(1.0) >= 0.9); | |
| 410 | try expect(make_f128(1.0) >= 1.0); | |
| 411 | try should_not_be_zero(1.0); | |
| 412 | } | |
| 413 | ||
| 414 | fn should_not_be_zero(x: f128) !void { | |
| 415 | try expect(x != 0.0); | |
| 416 | } | |
| 417 | ||
| 418 | 238 | test "comptime float rem int" { |
| 419 | 239 | comptime { |
| 420 | 240 | var x = @as(f32, 1) % 2; |
| ... | ... | @@ -614,13 +434,6 @@ fn testNanEqNan(comptime F: type) !void { |
| 614 | 434 | try expect(!(nan1 <= nan2)); |
| 615 | 435 | } |
| 616 | 436 | |
| 617 | test "128-bit multiplication" { | |
| 618 | var a: i128 = 3; | |
| 619 | var b: i128 = 2; | |
| 620 | var c = a * b; | |
| 621 | try expect(c == 6); | |
| 622 | } | |
| 623 | ||
| 624 | 437 | test "vector comparison" { |
| 625 | 438 | const S = struct { |
| 626 | 439 | fn doTheTest() !void { |