authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-05-06 23:59:57-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-05-06 23:59:57-04:00
log29beb603b752928184f5f5e9f7479412de1e1951
tree3d7432ccc7078fb67757ba18a407e32920db31a3
parent157af4332a7b78672ff8ad76a00120455547e2fd

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

4 files changed, 55 insertions(+), 14 deletions(-)

src/bignum.cpp-2
......@@ -259,7 +259,6 @@ bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) {
259259 if (dest->kind == BigNumKindFloat) {
260260 dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);
261261 } else {
262 assert(!op2->is_negative);
263262 dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;
264263 dest->is_negative = op1->is_negative;
265264 bignum_normalize(dest);
......@@ -274,7 +273,6 @@ bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {
274273 if (dest->kind == BigNumKindFloat) {
275274 dest->data.x_float = fmod(fmod(op1->data.x_float, op2->data.x_float) + op2->data.x_float, op2->data.x_float);
276275 } else {
277 assert(!op2->is_negative);
278276 if (op1->is_negative) {
279277 dest->data.x_uint = (op2->data.x_uint - op1->data.x_uint % op2->data.x_uint) % op2->data.x_uint;
280278 } else {
src/ir.cpp+45-11
......@@ -8209,25 +8209,59 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
82098209
82108210 bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt;
82118211 bool is_signed = ((resolved_type->id == TypeTableEntryIdInt && resolved_type->data.integral.is_signed) ||
8212 resolved_type->id == TypeTableEntryIdFloat ||
8213 (resolved_type->id == TypeTableEntryIdNumLitFloat &&
8214 (op1->value.data.x_bignum.data.x_float < 0.0 || op2->value.data.x_bignum.data.x_float < 0.0)) ||
82128215 (resolved_type->id == TypeTableEntryIdNumLitInt &&
82138216 (op1->value.data.x_bignum.is_negative || op2->value.data.x_bignum.is_negative)));
82148217 if (op_id == IrBinOpDivUnspecified) {
8215 if (is_signed) {
8216 ir_add_error(ira, &bin_op_instruction->base,
8217 buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact",
8218 buf_ptr(&op1->value.type->name),
8219 buf_ptr(&op2->value.type->name)));
8220 return ira->codegen->builtin_types.entry_invalid;
8218 if (is_int && is_signed) {
8219 bool ok = false;
8220 if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
8221 BigNum trunc_result;
8222 BigNum floor_result;
8223 if (bignum_div_trunc(&trunc_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) {
8224 zig_unreachable();
8225 }
8226 if (bignum_div_floor(&floor_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) {
8227 zig_unreachable();
8228 }
8229 if (bignum_cmp_eq(&trunc_result, &floor_result)) {
8230 ok = true;
8231 op_id = IrBinOpDivTrunc;
8232 }
8233 }
8234 if (!ok) {
8235 ir_add_error(ira, &bin_op_instruction->base,
8236 buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact",
8237 buf_ptr(&op1->value.type->name),
8238 buf_ptr(&op2->value.type->name)));
8239 return ira->codegen->builtin_types.entry_invalid;
8240 }
82218241 } else if (is_int) {
82228242 op_id = IrBinOpDivTrunc;
82238243 }
82248244 } else if (op_id == IrBinOpRemUnspecified) {
82258245 if (is_signed) {
8226 ir_add_error(ira, &bin_op_instruction->base,
8227 buf_sprintf("remainder division with '%s' and '%s': signed integers must use @rem or @mod",
8228 buf_ptr(&op1->value.type->name),
8229 buf_ptr(&op2->value.type->name)));
8230 return ira->codegen->builtin_types.entry_invalid;
8246 bool ok = false;
8247 if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
8248 BigNum rem_result;
8249 BigNum mod_result;
8250 if (bignum_rem(&rem_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) {
8251 zig_unreachable();
8252 }
8253 if (bignum_mod(&mod_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) {
8254 zig_unreachable();
8255 }
8256 ok = bignum_cmp_eq(&rem_result, &mod_result);
8257 }
8258 if (!ok) {
8259 ir_add_error(ira, &bin_op_instruction->base,
8260 buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod",
8261 buf_ptr(&op1->value.type->name),
8262 buf_ptr(&op2->value.type->name)));
8263 return ira->codegen->builtin_types.entry_invalid;
8264 }
82318265 }
82328266 op_id = IrBinOpRemRem;
82338267 }
test/cases/math.zig+9
......@@ -220,3 +220,12 @@ fn testFloatEqualityImpl(x: f64, y: f64) {
220220 const y2 = x + 1.0;
221221 assert(y == y2);
222222}
223
224test "allow signed integer division/remainder when values are comptime known and positive or exact" {
225 assert(5 / 3 == 1);
226 assert(-5 / -3 == 1);
227 assert(-6 / 3 == -2);
228
229 assert(5 % 3 == 2);
230 assert(-6 % 3 == 0);
231}
test/compile_errors.zig+1-1
......@@ -1722,5 +1722,5 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
17221722 \\ a % b
17231723 \\}
17241724 ,
1725 ".tmp_source.zig:2:7: error: remainder division with 'i32' and 'i32': signed integers must use @rem or @mod");
1725 ".tmp_source.zig:2:7: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod");
17261726}