diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 73e5cab2b..8b61f4feb 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -582,8 +582,6 @@ class GeneralFloatCases(unittest.TestCase): self.assertEqual(hash(float('inf')), sys.hash_info.inf) self.assertEqual(hash(float('-inf')), -sys.hash_info.inf) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_hash_nan(self): value = float('nan') self.assertEqual(hash(value), object.__hash__(value)) diff --git a/common/src/hash.rs b/common/src/hash.rs index ba3d6048a..708a633cf 100644 --- a/common/src/hash.rs +++ b/common/src/hash.rs @@ -1,11 +1,7 @@ use num_bigint::BigInt; -use num_complex::Complex64; use num_traits::ToPrimitive; use siphasher::sip::SipHasher24; -use std::{ - hash::{BuildHasher, Hash, Hasher}, - num::Wrapping, -}; +use std::hash::{BuildHasher, Hash, Hasher}; pub type PyHash = i64; pub type PyUHash = u64; @@ -94,17 +90,14 @@ impl HashSecret { } } -pub fn hash_float(value: f64) -> PyHash { +#[inline] +pub fn hash_float(value: f64) -> Option { // cpython _Py_HashDouble if !value.is_finite() { return if value.is_infinite() { - if value > 0.0 { - INF - } else { - -INF - } + Some(if value > 0.0 { INF } else { -INF }) } else { - NAN + None }; } @@ -136,14 +129,7 @@ pub fn hash_float(value: f64) -> PyHash { }; x = ((x << e) & MODULUS) | x >> (BITS32 - e); - fix_sentinel(x as PyHash * value.signum() as PyHash) -} - -pub fn hash_complex(value: &Complex64) -> PyHash { - let re_hash = hash_float(value.re); - let im_hash = hash_float(value.im); - let Wrapping(ret) = Wrapping(re_hash) + Wrapping(im_hash) * Wrapping(IMAG); - fix_sentinel(ret) + Some(fix_sentinel(x as PyHash * value.signum() as PyHash)) } pub fn hash_iter_unordered<'a, T: 'a, I, F, E>(iter: I, hashf: F) -> Result @@ -192,3 +178,19 @@ pub fn lcg_urandom(mut x: u32, buf: &mut [u8]) { *b = ((x >> 16) & 0xff) as u8; } } + +#[inline] +pub fn hash_object_id_raw(p: usize) -> PyHash { + // TODO: Use commented logic when below issue resolved. + // Ref: https://github.com/RustPython/RustPython/pull/3951#issuecomment-1193108966 + + /* bottom 3 or 4 bits are likely to be 0; rotate y by 4 to avoid + excessive hash collisions for dicts and sets */ + // p.rotate_right(4) as PyHash + p as PyHash +} + +#[inline] +pub fn hash_object_id(p: usize) -> PyHash { + fix_sentinel(hash_object_id_raw(p)) +} diff --git a/vm/src/builtins/complex.rs b/vm/src/builtins/complex.rs index 333269e4d..97b31bcf1 100644 --- a/vm/src/builtins/complex.rs +++ b/vm/src/builtins/complex.rs @@ -16,6 +16,7 @@ use crate::{ use num_complex::Complex64; use num_traits::Zero; use rustpython_common::{float_ops, hash}; +use std::num::Wrapping; /// Create a complex number from a real part and an optional imaginary part. /// @@ -417,7 +418,16 @@ impl Comparable for PyComplex { impl Hashable for PyComplex { #[inline] fn hash(zelf: &crate::Py, _vm: &VirtualMachine) -> PyResult { - Ok(hash::hash_complex(&zelf.value)) + let value = zelf.value; + + let re_hash = + hash::hash_float(value.re).unwrap_or_else(|| hash::hash_object_id(zelf.get_id())); + + let im_hash = + hash::hash_float(value.im).unwrap_or_else(|| hash::hash_object_id(zelf.get_id())); + + let Wrapping(ret) = Wrapping(re_hash) + Wrapping(im_hash) * Wrapping(hash::IMAG); + Ok(hash::fix_sentinel(ret)) } } diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs index ee4837090..2584b5209 100644 --- a/vm/src/builtins/float.rs +++ b/vm/src/builtins/float.rs @@ -539,7 +539,7 @@ impl Comparable for PyFloat { impl Hashable for PyFloat { #[inline] fn hash(zelf: &crate::Py, _vm: &VirtualMachine) -> PyResult { - Ok(hash::hash_float(zelf.to_f64())) + Ok(hash::hash_float(zelf.to_f64()).unwrap_or_else(|| hash::hash_object_id(zelf.get_id()))) } }