From 48909327dbbada0b4918df8a24a4bf8044e9d22e Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Sun, 6 Sep 2020 12:04:36 +0200 Subject: [PATCH] Fix array compare oparation --- Lib/test/test_array.py | 2 - tests/snippets/stdlib_array.py | 12 ++++ vm/src/stdlib/array.rs | 114 ++++++++++++++------------------- 3 files changed, 59 insertions(+), 69 deletions(-) diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 38ab32280..d00ad6707 100644 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -509,8 +509,6 @@ class BaseTest: a = array.array(self.typecode, 2*self.example) str(a) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_cmp(self): a = array.array(self.typecode, self.example) self.assertIs(a == 42, False) diff --git a/tests/snippets/stdlib_array.py b/tests/snippets/stdlib_array.py index 7df1c0acf..bcd2eec96 100644 --- a/tests/snippets/stdlib_array.py +++ b/tests/snippets/stdlib_array.py @@ -54,3 +54,15 @@ del a[0:0:9999999999] assert a == array(T, [1, 2, 3, 4, 5, 6, 7, 8]) del a[0:0:-9999999999] assert a == array(T, [1, 2, 3, 4, 5, 6, 7, 8]) + +def test_float_with_nan(): + f = float('nan') + a = array('f') + a.append(f) + assert not (a == a) + assert a != a + assert not (a < a) + assert not (a <= a) + assert not (a > a) + assert not (a >= a) +test_float_with_nan() diff --git a/vm/src/stdlib/array.rs b/vm/src/stdlib/array.rs index 55d9493cb..cc7aabae6 100644 --- a/vm/src/stdlib/array.rs +++ b/vm/src/stdlib/array.rs @@ -5,11 +5,11 @@ use crate::common::cell::{ use crate::function::OptionalArg; use crate::obj::objbytes::PyBytesRef; use crate::obj::objfloat::try_float; +use crate::obj::objiter; use crate::obj::objsequence::{PySliceableSequence, PySliceableSequenceMut}; use crate::obj::objslice::PySliceRef; use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::obj::{objbool, objiter}; use crate::pyobject::{ BorrowValue, Either, IdProtocol, IntoPyObject, PyArithmaticValue, PyClassImpl, PyComparisonValue, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, @@ -19,6 +19,7 @@ use crate::VirtualMachine; use crossbeam_utils::atomic::AtomicCell; use itertools::Itertools; use std::fmt; +use PyArithmaticValue::Implemented; struct ArrayTypeSpecifierError { _priv: (), @@ -362,7 +363,7 @@ trait ArrayElement: Sized { } macro_rules! adapt_try_into_from_object { - ($(($t:ty, $f: path),)*) => {$( + ($(($t:ty, $f:path),)*) => {$( impl ArrayElement for $t { fn try_into_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { $f(vm, obj) @@ -651,22 +652,45 @@ impl PyArray { zelf.borrow_value().repr(vm) } + fn cmp( + &self, + other: PyArrayRef, + len_cmp: L, + obj_cmp: O, + vm: &VirtualMachine, + ) -> PyResult + where + L: Fn(usize, usize) -> bool, + O: Fn(PyObjectRef, PyObjectRef) -> PyResult>, + { + let array_a = self.borrow_value(); + let array_b = other.borrow_value(); + let iter = Iterator::zip(array_a.iter(vm), array_b.iter(vm)); + for (a, b) in iter { + if let Some(v) = obj_cmp(a, b)? { + return Ok(Implemented(v)); + } + } + Ok(Implemented(len_cmp(self.len(), other.len()))) + } + #[pymethod(name = "__eq__")] fn eq(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let lhs = self.borrow_value(); - let rhs = class_or_notimplemented!(vm, Self, other); - let rhs = rhs.borrow_value(); - if lhs.len() != rhs.len() { - Ok(PyArithmaticValue::Implemented(false)) - } else { - for (a, b) in lhs.iter(vm).zip(rhs.iter(vm)) { - let ne = objbool::boolval(vm, vm._ne(a, b)?)?; - if ne { - return Ok(PyArithmaticValue::Implemented(false)); - } - } - Ok(PyArithmaticValue::Implemented(true)) + // we cannot use zelf.is(other) for shortcut because if we contenting a + // float value NaN we always return False even they are the same object. + let other = class_or_notimplemented!(vm, Self, other); + if self.len() != other.len() { + return Ok(Implemented(false)); } + let array_a = self.borrow_value(); + let array_b = other.borrow_value(); + let iter = Iterator::zip(array_a.iter(vm), array_b.iter(vm)); + for (a, b) in iter { + if !vm.bool_eq(a, b)? { + return Ok(Implemented(false)); + } + } + Ok(Implemented(true)) } #[pymethod(name = "__ne__")] @@ -676,70 +700,26 @@ impl PyArray { #[pymethod(name = "__lt__")] fn lt(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let lhs = self.borrow_value(); - let rhs = class_or_notimplemented!(vm, Self, other); - let rhs = rhs.borrow_value(); - - for (a, b) in lhs.iter(vm).zip(rhs.iter(vm)) { - let lt = objbool::boolval(vm, vm._lt(a, b)?)?; - - if lt { - return Ok(PyArithmaticValue::Implemented(true)); - } - } - - Ok(PyArithmaticValue::Implemented(lhs.len() < rhs.len())) + let other = class_or_notimplemented!(vm, Self, other); + self.cmp(other, |a, b| a < b, |a, b| vm.bool_seq_lt(a, b), vm) } #[pymethod(name = "__le__")] fn le(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let lhs = self.borrow_value(); - let rhs = class_or_notimplemented!(vm, Self, other); - let rhs = rhs.borrow_value(); - - for (a, b) in lhs.iter(vm).zip(rhs.iter(vm)) { - let le = objbool::boolval(vm, vm._le(a, b)?)?; - - if le { - return Ok(PyArithmaticValue::Implemented(true)); - } - } - - Ok(PyArithmaticValue::Implemented(lhs.len() <= rhs.len())) + let other = class_or_notimplemented!(vm, Self, other); + self.cmp(other, |a, b| a <= b, |a, b| vm.bool_seq_lt(a, b), vm) } #[pymethod(name = "__gt__")] fn gt(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let lhs = self.borrow_value(); - let rhs = class_or_notimplemented!(vm, Self, other); - let rhs = rhs.borrow_value(); - - for (a, b) in lhs.iter(vm).zip(rhs.iter(vm)) { - let gt = objbool::boolval(vm, vm._gt(a, b)?)?; - - if gt { - return Ok(PyArithmaticValue::Implemented(true)); - } - } - - Ok(PyArithmaticValue::Implemented(lhs.len() > rhs.len())) + let other = class_or_notimplemented!(vm, Self, other); + self.cmp(other, |a, b| a > b, |a, b| vm.bool_seq_gt(a, b), vm) } #[pymethod(name = "__ge__")] fn ge(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let lhs = self.borrow_value(); - let rhs = class_or_notimplemented!(vm, Self, other); - let rhs = rhs.borrow_value(); - - for (a, b) in lhs.iter(vm).zip(rhs.iter(vm)) { - let ge = objbool::boolval(vm, vm._ge(a, b)?)?; - - if ge { - return Ok(PyArithmaticValue::Implemented(true)); - } - } - - Ok(PyArithmaticValue::Implemented(lhs.len() >= rhs.len())) + let other = class_or_notimplemented!(vm, Self, other); + self.cmp(other, |a, b| a >= b, |a, b| vm.bool_seq_gt(a, b), vm) } #[pymethod(name = "__len__")]