diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index ef644b4da0..e8eebbe8e8 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -8,14 +8,18 @@ assert complex(1, -1) == complex(1, -1) assert complex(1, 0) == 1 -assert not complex(1, 1) == 1 +assert 1 == complex(1, 0) +assert complex(1, 1) != 1 +assert 1 != complex(1, 1) assert complex(1, 0) == 1.0 -assert not complex(1, 1) == 1.0 -assert not complex(1, 0) == 1.5 +assert 1.0 == complex(1, 0) +assert complex(1, 1) != 1.0 +assert 1.0 != complex(1, 1) +assert complex(1, 0) != 1.5 +assert not 1.0 != complex(1, 0) assert bool(complex(1, 0)) -assert not complex(1, 2) == complex(1, 1) -# Currently broken - see issue #419 -# assert complex(1, 2) != 'foo' +assert complex(1, 2) != complex(1, 1) +assert complex(1, 2) != 'foo' assert complex(1, 2).__eq__('foo') == NotImplemented # __neg__ diff --git a/tests/snippets/object.py b/tests/snippets/object.py new file mode 100644 index 0000000000..55f0a3355a --- /dev/null +++ b/tests/snippets/object.py @@ -0,0 +1,11 @@ +class MyObject: + pass + +assert not MyObject() == MyObject() +assert MyObject() != MyObject() +myobj = MyObject() +assert myobj == myobj +assert not myobj != myobj + +assert MyObject().__eq__(MyObject()) == NotImplemented +assert MyObject().__ne__(MyObject()) == NotImplemented diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 91d7338e11..cf30c6b98f 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -988,8 +988,8 @@ impl Frame { let b = self.pop_value(); let a = self.pop_value(); let value = match *op { - bytecode::ComparisonOperator::Equal => vm._eq(&a, b)?, - bytecode::ComparisonOperator::NotEqual => vm._ne(&a, b)?, + bytecode::ComparisonOperator::Equal => vm._eq(a, b)?, + bytecode::ComparisonOperator::NotEqual => vm._ne(a, b)?, bytecode::ComparisonOperator::Less => vm._lt(&a, b)?, bytecode::ComparisonOperator::LessOrEqual => vm._le(&a, b)?, bytecode::ComparisonOperator::Greater => vm._gt(&a, b)?, diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 6ffd8d1d25..a822f7208f 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -80,7 +80,7 @@ fn float_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { false } } else { - false + return Ok(vm.ctx.not_implemented()); }; Ok(vm.ctx.new_bool(result)) } @@ -181,7 +181,7 @@ fn float_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_float(v1 + objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -198,11 +198,7 @@ fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 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 {}", - i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -221,11 +217,7 @@ fn float_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - return Err(vm.new_type_error(format!( - "Cannot floordiv {} and {}", - i.borrow(), - i2.borrow() - ))); + return Ok(vm.ctx.not_implemented()); }; if v2 != 0.0 { @@ -249,7 +241,7 @@ fn float_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_float(v1 - objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -268,7 +260,7 @@ fn float_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - return Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow()))); + return Ok(vm.ctx.not_implemented()); }; if v2 != 0.0 { @@ -300,7 +292,7 @@ fn float_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let result = v1.powf(objint::get_value(i2).to_f64().unwrap()); Ok(vm.ctx.new_float(result)) } else { - Err(vm.new_type_error(format!("Cannot add {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index d1ab693400..387b4bdf75 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -125,7 +125,7 @@ fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { false } } else { - false + return Ok(vm.ctx.not_implemented()); }; Ok(vm.ctx.new_bool(result)) } @@ -303,11 +303,7 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_zero_division_error("integer floordiv by zero".to_string())) } } else { - Err(vm.new_type_error(format!( - "Cannot floordiv {} and {}", - i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -358,11 +354,7 @@ fn int_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_float(i.to_f64().unwrap() - objfloat::get_value(i2))) } else { - Err(vm.new_not_implemented_error(format!( - "Cannot substract {} and {}", - _i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -379,11 +371,7 @@ fn int_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_float(get_value(i).to_f64().unwrap() * objfloat::get_value(i2))) } else { - Err(vm.new_type_error(format!( - "Cannot multiply {} and {}", - i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -405,7 +393,7 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else if objtype::isinstance(i2, &vm.ctx.float_type()) { objfloat::get_value(i2) } else { - return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))); + return Ok(vm.ctx.not_implemented()); }; if v2 == 0.0 { @@ -431,7 +419,7 @@ fn int_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_zero_division_error("integer modulo by zero".to_string())) } } else { - Err(vm.new_type_error(format!("Cannot modulo {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -460,11 +448,7 @@ fn int_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v2 = objfloat::get_value(i2); Ok(vm.ctx.new_float((v1.to_f64().unwrap()).powf(v2))) } else { - Err(vm.new_type_error(format!( - "Cannot raise power {} and {}", - i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -489,11 +473,7 @@ fn int_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(vm.new_zero_division_error("integer divmod by zero".to_string())) } } else { - Err(vm.new_type_error(format!( - "Cannot divmod power {} and {}", - i.borrow(), - i2.borrow() - ))) + Ok(vm.ctx.not_implemented()) } } @@ -508,7 +488,7 @@ fn int_xor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 ^ v2)) } else { - Err(vm.new_type_error(format!("Cannot xor {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -525,7 +505,7 @@ fn int_rxor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(left_val ^ right_val)) } else { - Err(vm.new_type_error(format!("Cannot rxor {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -540,7 +520,7 @@ fn int_or(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 | v2)) } else { - Err(vm.new_type_error(format!("Cannot or {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } @@ -555,7 +535,7 @@ fn int_and(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v2 = get_value(i2); Ok(vm.ctx.new_int(v1 & v2)) } else { - Err(vm.new_type_error(format!("Cannot and {} and {}", i.borrow(), i2.borrow()))) + Ok(vm.ctx.not_implemented()) } } diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index be30b672ed..24dcf68ec3 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -228,7 +228,7 @@ fn list_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let elements = get_elements(zelf); let mut count: usize = 0; for element in elements.iter() { - let is_eq = vm._eq(element, value.clone())?; + let is_eq = vm._eq(element.clone(), value.clone())?; if objbool::boolval(vm, is_eq)? { count += 1; } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index cb5065b20f..8bc4d834ed 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -3,7 +3,6 @@ use super::super::pyobject::{ TypeProtocol, }; use super::super::vm::VirtualMachine; -use super::objbool; use super::objstr; use super::objtype; use std::cell::RefCell; @@ -29,19 +28,19 @@ fn object_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(zelf, Some(vm.ctx.object())), (other, None)] + required = [(_zelf, Some(vm.ctx.object())), (_other, None)] ); - Ok(vm.ctx.new_bool(zelf.is(other))) + Ok(vm.ctx.not_implemented()) } fn object_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, - required = [(zelf, Some(vm.ctx.object())), (other, None)] + required = [(_zelf, Some(vm.ctx.object())), (_other, None)] ); - let eq = vm.call_method(zelf, "__eq__", vec![other.clone()])?; - objbool::not(vm, &eq) + + Ok(vm.ctx.not_implemented()) } fn object_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 6fda0ad092..413a0285f4 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -166,7 +166,7 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(set, Some(vm.ctx.set_type())), (needle, None)] ); for element in get_elements(set).iter() { - match vm.call_method(needle, "__eq__", vec![element.1.clone()]) { + match vm._eq(needle.clone(), element.1.clone()) { Ok(value) => { if objbool::get_value(&value) { return Ok(vm.new_bool(true)); diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 3b76466749..f9338fb113 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -129,7 +129,7 @@ fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let elements = get_elements(zelf); let mut count: usize = 0; for element in elements.iter() { - let is_eq = vm._eq(element, value.clone())?; + let is_eq = vm._eq(element.clone(), value.clone())?; if objbool::boolval(vm, is_eq)? { count += 1; } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 5c432ecc02..cfab253c82 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -13,6 +13,7 @@ use std::sync::{Mutex, MutexGuard}; use super::builtins; use super::bytecode; use super::frame::Frame; +use super::obj::objbool; use super::obj::objcode; use super::obj::objgenerator; use super::obj::objiter; @@ -101,6 +102,20 @@ impl VirtualMachine { self.new_exception(type_error, msg) } + pub fn new_unsupported_operand_error( + &mut self, + a: PyObjectRef, + b: PyObjectRef, + op: &str, + ) -> PyObjectRef { + let a_type_name = objtype::get_type_name(&a.typ()); + let b_type_name = objtype::get_type_name(&b.typ()); + self.new_type_error(format!( + "Unsupported operand types for '{}': '{}' and '{}'", + op, a_type_name, b_type_name + )) + } + pub fn new_os_error(&mut self, msg: String) -> PyObjectRef { let os_error = self.ctx.exceptions.os_error.clone(); self.new_exception(os_error, msg) @@ -497,9 +512,7 @@ impl VirtualMachine { /// Given the above example, it will /// 1. Try to call `__and__` with `a` and `b` /// 2. If above fails try to call `__rand__` with `a` and `b` - /// 3. If above fails throw an exception: - /// `TypeError: Unsupported operand types for '&': 'float' and 'int'` - /// if `a` is of type float and `b` of type int + /// 3. If above in not implemented, call unsupported(a, b) for result. /// pub fn call_or_unsupported( &mut self, @@ -507,97 +520,106 @@ impl VirtualMachine { b: PyObjectRef, d: &str, r: &str, - op: &str, + unsupported: fn(&mut VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult, ) -> PyResult { // Try to call the first method if let Ok(method) = self.get_method(a.clone(), d) { - match self.invoke( + let result = self.invoke( method, PyFuncArgs { args: vec![b.clone()], kwargs: vec![], }, - ) { - Ok(value) => return Ok(value), - Err(err) => { - if !objtype::isinstance(&err, &self.ctx.exceptions.not_implemented_error) { - return Err(err); - } - } + )?; + + if !result.is(&self.ctx.not_implemented()) { + return Ok(result); } } // 2. Try to call reverse method if let Ok(method) = self.get_method(b.clone(), r) { - match self.invoke( + let result = self.invoke( method, PyFuncArgs { args: vec![a.clone()], kwargs: vec![], }, - ) { - Ok(value) => return Ok(value), - Err(err) => { - if !objtype::isinstance(&err, &self.ctx.exceptions.not_implemented_error) { - return Err(err); - } - } + )?; + + if !result.is(&self.ctx.not_implemented()) { + return Ok(result); } } - // 3. Both failed, throw an exception - // TODO: Move this chunk somewhere else, it should be - // called in other methods as well (for example objint.rs) - let a_type_name = objtype::get_type_name(&a.typ()); - let b_type_name = objtype::get_type_name(&b.typ()); - Err(self.new_type_error(format!( - "Unsupported operand types for '{}': '{}' and '{}'", - op, a_type_name, b_type_name - ))) + unsupported(self, a, b) } pub fn _sub(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__sub__", "__rsub__", "-") + self.call_or_unsupported(a, b, "__sub__", "__rsub__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "-")) + }) } pub fn _add(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__add__", "__radd__", "+") + self.call_or_unsupported(a, b, "__add__", "__radd__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "+")) + }) } pub fn _mul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__mul__", "__rmul__", "*") + self.call_or_unsupported(a, b, "__mul__", "__rmul__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "*")) + }) } pub fn _div(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__truediv__", "__rtruediv__", "/") + self.call_or_unsupported(a, b, "__truediv__", "__rtruediv__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "/")) + }) } pub fn _pow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__pow__", "__rpow__", "**") + self.call_or_unsupported(a, b, "__pow__", "__rpow__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "**")) + }) } pub fn _modulo(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__mod__", "__rmod__", "%") + self.call_or_unsupported(a, b, "__mod__", "__rmod__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "%")) + }) } pub fn _xor(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__xor__", "__rxor__", "^") + self.call_or_unsupported(a, b, "__xor__", "__rxor__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "^")) + }) } pub fn _or(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__or__", "__ror__", "|") + self.call_or_unsupported(a, b, "__or__", "__ror__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "|")) + }) } pub fn _and(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__and__", "__rand__", "&") + self.call_or_unsupported(a, b, "__and__", "__rand__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "^")) + }) } - pub fn _eq(&mut self, a: &PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(a, "__eq__", vec![b]) + pub fn _eq(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__eq__", "__eq__", |vm, a, b| { + Ok(vm.new_bool(a.is(&b))) + }) } - pub fn _ne(&mut self, a: &PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(a, "__ne__", vec![b]) + pub fn _ne(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__ne__", "__ne__", |vm, a, b| { + let eq = vm._eq(a, b)?; + objbool::not(vm, &eq) + }) } pub fn _lt(&mut self, a: &PyObjectRef, b: PyObjectRef) -> PyResult {