diff --git a/tests/snippets/builtin_divmod.py b/tests/snippets/builtin_divmod.py index 7bab71c99..43b77a452 100644 --- a/tests/snippets/builtin_divmod.py +++ b/tests/snippets/builtin_divmod.py @@ -1,3 +1,17 @@ assert divmod(11, 3) == (3, 2) assert divmod(8,11) == (0, 8) assert divmod(0.873, 0.252) == (3.0, 0.11699999999999999) + +try: + divmod(5, 0) +except ZeroDivisionError: + pass +else: + assert False, "Expected divmod by zero to throw ZeroDivisionError" + +try: + divmod(5.0, 0.0) +except ZeroDivisionError: + pass +else: + assert False, "Expected divmod by zero to throw ZeroDivisionError" diff --git a/tests/snippets/division_by_zero.py b/tests/snippets/division_by_zero.py index 7cb68cd76..d92419d79 100644 --- a/tests/snippets/division_by_zero.py +++ b/tests/snippets/division_by_zero.py @@ -26,6 +26,27 @@ except ZeroDivisionError: else: assert False, 'Expected ZeroDivisionError' +try: + 5 // 0 +except ZeroDivisionError: + pass +else: + assert False, 'Expected ZeroDivisionError' + +try: + 5.3 // (-0.0) +except ZeroDivisionError: + pass +else: + assert False, 'Expected ZeroDivisionError' + +try: + divmod(5, 0) +except ZeroDivisionError: + pass +else: + assert False, 'Expected ZeroDivisionError' + try: raise ZeroDivisionError('Is an ArithmeticError subclass?') except ArithmeticError: diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 6d8c2ff73..94f9f19b7 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -172,9 +172,9 @@ fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]); if objtype::isinstance(i2, &vm.ctx.float_type()) || objtype::isinstance(i2, &vm.ctx.int_type()) { - let r1 = float_floordiv(vm, args.clone()); - let r2 = float_mod(vm, args.clone()); - Ok(vm.ctx.new_tuple(vec![r1.unwrap(), r2.unwrap()])) + let r1 = float_floordiv(vm, args.clone())?; + let r2 = float_mod(vm, args.clone())?; + Ok(vm.ctx.new_tuple(vec![r1, r2])) } else { Err(vm.new_type_error(format!( "Cannot divmod power {} and {}", @@ -190,18 +190,26 @@ fn float_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); - if objtype::isinstance(i2, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float((get_value(i) / get_value(i2)).floor())) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float((get_value(i) / objint::get_value(i2).to_f64().unwrap()).floor())) + + let v1 = get_value(i); + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + get_value(i2) + } else if objtype::isinstance(i2, &vm.ctx.int_type) { + objint::get_value(i2) + .to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - Err(vm.new_type_error(format!( + return Err(vm.new_type_error(format!( "Cannot floordiv {} and {}", i.borrow(), i2.borrow() - ))) + ))); + }; + + if v2 != 0.0 { + Ok(vm.ctx.new_float((v1 / v2).floor())) + } else { + Err(vm.new_zero_division_error("float floordiv by zero".to_string())) } } @@ -229,14 +237,22 @@ fn float_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); - if objtype::isinstance(i2, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float(get_value(i) % get_value(i2))) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float(get_value(i) % objint::get_value(i2).to_f64().unwrap())) + + let v1 = get_value(i); + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + get_value(i2) + } else if objtype::isinstance(i2, &vm.ctx.int_type) { + objint::get_value(i2) + .to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow()))) + return Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow()))); + }; + + if v2 != 0.0 { + Ok(vm.ctx.new_float(v1 % v2)) + } else { + Err(vm.new_zero_division_error("float mod by zero".to_string())) } } @@ -272,15 +288,22 @@ fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); + let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.float_type) { - Ok(vm.ctx.new_float(v1 / get_value(i2))) + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + get_value(i2) } else if objtype::isinstance(i2, &vm.ctx.int_type) { - Ok(vm - .ctx - .new_float(v1 / objint::get_value(i2).to_f64().unwrap())) + objint::get_value(i2) + .to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))); + }; + + if v2 != 0.0 { + Ok(vm.ctx.new_float(v1 / v2)) + } else { + Err(vm.new_zero_division_error("float division by zero".to_string())) } } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 73267a83f..443fa552e 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -8,6 +8,7 @@ use super::objfloat; use super::objstr; use super::objtype; use num_bigint::{BigInt, ToBigInt}; +use num_integer::Integer; use num_traits::{Pow, Signed, ToPrimitive, Zero}; use std::hash::{Hash, Hasher}; @@ -289,7 +290,13 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(get_value(i) / get_value(i2))) + let (v1, v2) = (get_value(i), get_value(i2)); + + if v2 != BigInt::zero() { + Ok(vm.ctx.new_int(v1 / v2)) + } else { + Err(vm.new_zero_division_error("integer floordiv by zero".to_string())) + } } else { Err(vm.new_type_error(format!( "Cannot floordiv {} and {}", @@ -462,11 +469,20 @@ fn int_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]); + if objtype::isinstance(i2, &vm.ctx.int_type()) { - let r1 = int_floordiv(vm, args.clone()); - let r2 = int_mod(vm, args.clone()); - Ok(vm.ctx.new_tuple(vec![r1.unwrap(), r2.unwrap()])) + let v1 = get_value(i); + let v2 = get_value(i2); + + if v2 != BigInt::zero() { + let (r1, r2) = v1.div_rem(&v2); + + Ok(vm + .ctx + .new_tuple(vec![vm.ctx.new_int(r1), vm.ctx.new_int(r2)])) + } else { + Err(vm.new_zero_division_error("integer divmod by zero".to_string())) + } } else { Err(vm.new_type_error(format!( "Cannot divmod power {} and {}",