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
master
Andrew Kelley 2017-05-06 23:59:57 -04:00
parent 157af4332a
commit 29beb603b7
4 changed files with 55 additions and 14 deletions

View File

@ -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 {

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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");
}