Skip to content

Commit 2c74716

Browse files
authored
Merge pull request RustPython#926 from youknowone/number-hashes
Fix number hashes for small numbers
2 parents 0e56bb4 + 7a64f3e commit 2c74716

18 files changed

+148
-45
lines changed

tests/snippets/dict.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,5 @@ def __eq__(self, other):
198198

199199
w = {1: 1, **x, 2: 2, **y, 3: 3, **z, 4: 4}
200200
assert w == {1: 1, 'a': 1, 'b': 2, 'c': 3, 2: 2, 'd': 3, 3: 3, 'e': 3, 4: 4}
201+
202+
assert str({True: True, 1.0: 1.0}) == str({True: 1.0})

tests/snippets/floats.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,21 @@
9898
assert_raises(ValueError, lambda: float('foo'))
9999
assert_raises(OverflowError, lambda: float(2**10000))
100100

101+
# check eq and hash for small numbers
102+
103+
assert 1.0 == 1
104+
assert 1.0 == True
105+
assert 0.0 == 0
106+
assert 0.0 == False
107+
assert hash(1.0) == hash(1)
108+
assert hash(1.0) == hash(True)
109+
assert hash(0.0) == hash(0)
110+
assert hash(0.0) == hash(False)
111+
assert hash(1.0) != hash(1.0000000001)
112+
113+
assert 5.0 in {3, 4, 5}
114+
assert {-1: 2}[-1.0] == 2
115+
101116
# check that magic methods are implemented for ints and floats
102117

103118
assert 1.0.__add__(1.0) == 2.0

vm/src/builtins.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,7 @@ fn builtin_hasattr(obj: PyObjectRef, attr: PyStringRef, vm: &VirtualMachine) ->
303303

304304
fn builtin_hash(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
305305
arg_check!(vm, args, required = [(obj, None)]);
306-
307-
vm.call_method(obj, "__hash__", vec![])
306+
vm._hash(obj).and_then(|v| Ok(vm.new_int(v)))
308307
}
309308

310309
// builtin_help

vm/src/dictdatatype.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use crate::obj::objbool;
2-
use crate::obj::objint;
32
use crate::pyobject::{IdProtocol, PyObjectRef, PyResult};
43
use crate::vm::VirtualMachine;
5-
use num_traits::ToPrimitive;
64
/// Ordered dictionary implementation.
75
/// Inspired by: https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html
86
/// And: https://www.youtube.com/watch?v=p33CVV29OG8
@@ -218,8 +216,7 @@ enum LookupResult {
218216
}
219217

220218
fn calc_hash(vm: &VirtualMachine, key: &PyObjectRef) -> PyResult<usize> {
221-
let hash = vm.call_method(key, "__hash__", vec![])?;
222-
Ok(objint::get_value(&hash).to_usize().unwrap())
219+
Ok(vm._hash(key)? as usize)
223220
}
224221

225222
/// Invoke __eq__ on two keys

vm/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub mod frame;
5555
pub mod function;
5656
pub mod import;
5757
pub mod obj;
58+
mod pyhash;
5859
pub mod pyobject;
5960
pub mod stdlib;
6061
mod symboltable;

vm/src/obj/objbytearray.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ impl PyByteArrayRef {
135135
}
136136

137137
#[pymethod(name = "__hash__")]
138-
fn hash(self, vm: &VirtualMachine) -> PyResult {
138+
fn hash(self, vm: &VirtualMachine) -> PyResult<()> {
139139
Err(vm.new_type_error("unhashable type: bytearray".to_string()))
140140
}
141141

vm/src/obj/objbyteinner.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::obj::objint::PyIntRef;
22
use crate::obj::objnone::PyNoneRef;
33
use crate::obj::objslice::PySliceRef;
44
use crate::obj::objtuple::PyTupleRef;
5+
use crate::pyhash;
56
use crate::pyobject::Either;
67
use crate::pyobject::PyRef;
78
use crate::pyobject::PyValue;
@@ -12,17 +13,12 @@ use core::ops::Range;
1213
use num_bigint::BigInt;
1314

1415
use crate::function::OptionalArg;
15-
16-
use crate::vm::VirtualMachine;
17-
1816
use crate::pyobject::{PyResult, TypeProtocol};
19-
20-
use crate::obj::objstr::{PyString, PyStringRef};
21-
use std::collections::hash_map::DefaultHasher;
22-
use std::hash::{Hash, Hasher};
17+
use crate::vm::VirtualMachine;
2318

2419
use super::objint;
2520
use super::objsequence::{is_valid_slice_arg, PySliceableSequence};
21+
use super::objstr::{PyString, PyStringRef};
2622

2723
use crate::obj::objint::PyInt;
2824
use num_integer::Integer;
@@ -379,10 +375,8 @@ impl PyByteInner {
379375
}
380376
}
381377

382-
pub fn hash(&self) -> usize {
383-
let mut hasher = DefaultHasher::new();
384-
self.elements.hash(&mut hasher);
385-
hasher.finish() as usize
378+
pub fn hash(&self) -> pyhash::PyHash {
379+
pyhash::hash_value(&self.elements)
386380
}
387381

388382
pub fn add(&self, other: PyByteInner) -> Vec<u8> {

vm/src/obj/objbytes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::obj::objint::PyIntRef;
2-
32
use crate::obj::objslice::PySliceRef;
43
use crate::obj::objstr::PyStringRef;
54
use crate::obj::objtuple::PyTupleRef;
5+
use crate::pyhash;
66

77
use crate::pyobject::Either;
88
use crate::vm::VirtualMachine;
@@ -125,7 +125,7 @@ impl PyBytesRef {
125125
}
126126

127127
#[pymethod(name = "__hash__")]
128-
fn hash(self, _vm: &VirtualMachine) -> usize {
128+
fn hash(self, _vm: &VirtualMachine) -> pyhash::PyHash {
129129
self.inner.hash()
130130
}
131131

vm/src/obj/objdict.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ impl PyDictRef {
298298
Ok(PyDict { entries }.into_ref(vm))
299299
}
300300

301-
fn hash(self, vm: &VirtualMachine) -> PyResult {
301+
fn hash(self, vm: &VirtualMachine) -> PyResult<()> {
302302
Err(vm.new_type_error("unhashable type".to_string()))
303303
}
304304

vm/src/obj/objfloat.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::objstr;
44
use super::objtype;
55
use crate::function::OptionalArg;
66
use crate::obj::objtype::PyClassRef;
7+
use crate::pyhash;
78
use crate::pyobject::{
89
IdProtocol, IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
910
TypeProtocol,
@@ -429,6 +430,11 @@ impl PyFloat {
429430
zelf
430431
}
431432

433+
#[pymethod(name = "__hash__")]
434+
fn hash(&self, _vm: &VirtualMachine) -> pyhash::PyHash {
435+
pyhash::hash_float(self.value)
436+
}
437+
432438
#[pyproperty(name = "real")]
433439
fn real(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyFloatRef {
434440
zelf

0 commit comments

Comments
 (0)