From 29beb603b752928184f5f5e9f7479412de1e1951 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 6 May 2017 23:59:57 -0400 Subject: [PATCH] allow division and remainder operators sometimes when the values are comptime known and the result would be the same, allow `/` and `%` for signed integers and floats. closes #365 --- src/bignum.cpp | 2 -- src/ir.cpp | 56 +++++++++++++++++++++++++++++++++-------- test/cases/math.zig | 9 +++++++ test/compile_errors.zig | 2 +- 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/bignum.cpp b/src/bignum.cpp index ee79a3477c17c163e5ff82fc4ddfc9e7c8b7779b..bbd1a6c92dc231fae49638eb9b9efebff91de30e 100644 --- a/src/bignum.cpp +++ b/src/bignum.cpp @@ -259,7 +259,6 @@ bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) { if (dest->kind == BigNumKindFloat) { dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float); } else { - assert(!op2->is_negative); dest->data.x_uint = op1->data.x_uint % op2->data.x_uint; dest->is_negative = op1->is_negative; bignum_normalize(dest); @@ -274,7 +273,6 @@ bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) { if (dest->kind == BigNumKindFloat) { dest->data.x_float = fmod(fmod(op1->data.x_float, op2->data.x_float) + op2->data.x_float, op2->data.x_float); } else { - assert(!op2->is_negative); if (op1->is_negative) { dest->data.x_uint = (op2->data.x_uint - op1->data.x_uint % op2->data.x_uint) % op2->data.x_uint; } else { diff --git a/src/ir.cpp b/src/ir.cpp index 9462d05e99d2be7cd1df41f8d79496766dd91801..b24fe3b9eae5805857d898d2bb9b7319f03453bb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8209,25 +8209,59 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt; bool is_signed = ((resolved_type->id == TypeTableEntryIdInt && resolved_type->data.integral.is_signed) || + resolved_type->id == TypeTableEntryIdFloat || + (resolved_type->id == TypeTableEntryIdNumLitFloat && + (op1->value.data.x_bignum.data.x_float < 0.0 || op2->value.data.x_bignum.data.x_float < 0.0)) || (resolved_type->id == TypeTableEntryIdNumLitInt && (op1->value.data.x_bignum.is_negative || op2->value.data.x_bignum.is_negative))); if (op_id == IrBinOpDivUnspecified) { - if (is_signed) { - ir_add_error(ira, &bin_op_instruction->base, - buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", - buf_ptr(&op1->value.type->name), - buf_ptr(&op2->value.type->name))); - return ira->codegen->builtin_types.entry_invalid; + if (is_int && is_signed) { + bool ok = false; + if (instr_is_comptime(op1) && instr_is_comptime(op2)) { + BigNum trunc_result; + BigNum floor_result; + if (bignum_div_trunc(&trunc_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) { + zig_unreachable(); + } + if (bignum_div_floor(&floor_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) { + zig_unreachable(); + } + if (bignum_cmp_eq(&trunc_result, &floor_result)) { + ok = true; + op_id = IrBinOpDivTrunc; + } + } + if (!ok) { + ir_add_error(ira, &bin_op_instruction->base, + buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", + buf_ptr(&op1->value.type->name), + buf_ptr(&op2->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } } else if (is_int) { op_id = IrBinOpDivTrunc; } } else if (op_id == IrBinOpRemUnspecified) { if (is_signed) { - ir_add_error(ira, &bin_op_instruction->base, - buf_sprintf("remainder division with '%s' and '%s': signed integers must use @rem or @mod", - buf_ptr(&op1->value.type->name), - buf_ptr(&op2->value.type->name))); - return ira->codegen->builtin_types.entry_invalid; + bool ok = false; + if (instr_is_comptime(op1) && instr_is_comptime(op2)) { + BigNum rem_result; + BigNum mod_result; + if (bignum_rem(&rem_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) { + zig_unreachable(); + } + if (bignum_mod(&mod_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) { + zig_unreachable(); + } + ok = bignum_cmp_eq(&rem_result, &mod_result); + } + if (!ok) { + ir_add_error(ira, &bin_op_instruction->base, + buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", + buf_ptr(&op1->value.type->name), + buf_ptr(&op2->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } } op_id = IrBinOpRemRem; } diff --git a/test/cases/math.zig b/test/cases/math.zig index b3deb5af09fa09f4a6c8371791a4dc837841f972..bd70736c595ceaaa8667f8e4acc6fa9b1e225f98 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -220,3 +220,12 @@ fn testFloatEqualityImpl(x: f64, y: f64) { const y2 = x + 1.0; assert(y == y2); } + +test "allow signed integer division/remainder when values are comptime known and positive or exact" { + assert(5 / 3 == 1); + assert(-5 / -3 == 1); + assert(-6 / 3 == -2); + + assert(5 % 3 == 2); + assert(-6 % 3 == 0); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ff4003819a5d6cb9ab3a45b262c2c8d3b8e30c24..4d6877267dce105ee8e8ec797d4508148acaf40f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1722,5 +1722,5 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\ a % b \\} , - ".tmp_source.zig:2:7: error: remainder division with 'i32' and 'i32': signed integers must use @rem or @mod"); + ".tmp_source.zig:2:7: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod"); } -- 2.54.0