Commit 29beb603b7
Changed files (4)
test
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 {
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;
}
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);
+}
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");
}