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) {...@@ -259,7 +259,6 @@ bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) {
259 if (dest->kind == BigNumKindFloat) {259 if (dest->kind == BigNumKindFloat) {
260 dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);260 dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);
261 } else {261 } else {
262 assert(!op2->is_negative);
263 dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;262 dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;
264 dest->is_negative = op1->is_negative;263 dest->is_negative = op1->is_negative;
265 bignum_normalize(dest);264 bignum_normalize(dest);
...@@ -274,7 +273,6 @@ bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {...@@ -274,7 +273,6 @@ bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {
274 if (dest->kind == BigNumKindFloat) {273 if (dest->kind == BigNumKindFloat) {
275 dest->data.x_float = fmod(fmod(op1->data.x_float, op2->data.x_float) + op2->data.x_float, op2->data.x_float);274 dest->data.x_float = fmod(fmod(op1->data.x_float, op2->data.x_float) + op2->data.x_float, op2->data.x_float);
276 } else {275 } else {
277 assert(!op2->is_negative);
278 if (op1->is_negative) {276 if (op1->is_negative) {
279 dest->data.x_uint = (op2->data.x_uint - op1->data.x_uint % op2->data.x_uint) % op2->data.x_uint;277 dest->data.x_uint = (op2->data.x_uint - op1->data.x_uint % op2->data.x_uint) % op2->data.x_uint;
280 } else {278 } else {
src/ir.cpp+45-11
...@@ -8209,25 +8209,59 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp...@@ -8209,25 +8209,59 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
82098209
8210 bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt;8210 bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt;
8211 bool is_signed = ((resolved_type->id == TypeTableEntryIdInt && resolved_type->data.integral.is_signed) ||8211 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)) ||
8212 (resolved_type->id == TypeTableEntryIdNumLitInt &&8215 (resolved_type->id == TypeTableEntryIdNumLitInt &&
8213 (op1->value.data.x_bignum.is_negative || op2->value.data.x_bignum.is_negative)));8216 (op1->value.data.x_bignum.is_negative || op2->value.data.x_bignum.is_negative)));
8214 if (op_id == IrBinOpDivUnspecified) {8217 if (op_id == IrBinOpDivUnspecified) {
8215 if (is_signed) {8218 if (is_int && is_signed) {
8216 ir_add_error(ira, &bin_op_instruction->base,8219 bool ok = false;
8217 buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact",8220 if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
8218 buf_ptr(&op1->value.type->name),8221 BigNum trunc_result;
8219 buf_ptr(&op2->value.type->name)));8222 BigNum floor_result;
8220 return ira->codegen->builtin_types.entry_invalid;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 }
8221 } else if (is_int) {8241 } else if (is_int) {
8222 op_id = IrBinOpDivTrunc;8242 op_id = IrBinOpDivTrunc;
8223 }8243 }
8224 } else if (op_id == IrBinOpRemUnspecified) {8244 } else if (op_id == IrBinOpRemUnspecified) {
8225 if (is_signed) {8245 if (is_signed) {
8226 ir_add_error(ira, &bin_op_instruction->base,8246 bool ok = false;
8227 buf_sprintf("remainder division with '%s' and '%s': signed integers must use @rem or @mod",8247 if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
8228 buf_ptr(&op1->value.type->name),8248 BigNum rem_result;
8229 buf_ptr(&op2->value.type->name)));8249 BigNum mod_result;
8230 return ira->codegen->builtin_types.entry_invalid;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 }
8231 }8265 }
8232 op_id = IrBinOpRemRem;8266 op_id = IrBinOpRemRem;
8233 }8267 }
test/cases/math.zig+9
...@@ -220,3 +220,12 @@ fn testFloatEqualityImpl(x: f64, y: f64) {...@@ -220,3 +220,12 @@ fn testFloatEqualityImpl(x: f64, y: f64) {
220 const y2 = x + 1.0;220 const y2 = x + 1.0;
221 assert(y == y2);221 assert(y == y2);
222}222}
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) {...@@ -1722,5 +1722,5 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
1722 \\ a % b1722 \\ a % b
1723 \\}1723 \\}
1724 ,1724 ,
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");
1726}1726}