Skip to content

Update to pass test for unhashable collections#4640

Merged
youknowone merged 11 commits into
RustPython:mainfrom
seungha-kim:feature/unhashable
Mar 7, 2023
Merged

Update to pass test for unhashable collections#4640
youknowone merged 11 commits into
RustPython:mainfrom
seungha-kim:feature/unhashable

Conversation

@seungha-kim

Copy link
Copy Markdown
Contributor

In CPython:

  • If a type is marked as unhashable on the native side (i.e. PyObject_HashNotImplemented is assigned to hash slot), then __hash__ attribute of the type dict is set to None so that the type is considered as unhashable on Python side too.
  • If a type is created at runtime and "__hash__": None is given as the type dict (like type('C', (object,), {'__hash__': None})), then the hash slot is filled with PyObject_HashNotImplemented.

In RustPython, Unhashable trait exists to mark a type as unhashble. But there is no way to check if a type implements Unhashable trait at runtime, so it is hard to follow the CPython's mechanism.

So I've added unhashable_wrapper function similar to PyObject_HashNotImplemented, and used that as a marker for unhashable on the native side.


Thanks for the advice from @youknowone !

Comment thread vm/src/builtins/list.rs Outdated
Comment on lines +605 to +610
context
.types
.list_type
.slots
.hash
.store(Some(unhashable_wrapper));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there is a more elegant way to do this...?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I missed adding unhashable_wrapper thing for deque, so test_deque fails. Deque implementation resides in collections.rs, but there is no type initialization function similar to this init function in collections.rs. Where is a good place to put this code?

@youknowone

Copy link
Copy Markdown
Member

I will look in the details.
The single failing test is this one:

======================================================================
FAIL: test_hash (test.test_deque.TestBasic.test_hash)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/work/RustPython/RustPython/pylib/Lib/test/test_deque.py", line 537, in test_hash
    self.assertRaises(TypeError, hash, deque('abc'))
AssertionError: TypeError not raised by hash

Comment thread vm/src/builtins/bytearray.rs Outdated
Comment on lines +94 to +99
context
.types
.bytearray_type
.slots
.hash
.store(Some(unhashable_wrapper));

@youknowone youknowone Mar 5, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a suggestion about the location of this initialization.

We put these information to PyClassDef for builtin types.
adding HASHABLE const with default value false will be good.

Then we use the information during making class. that's PyClassImpl::make_slots.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll try.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe default value is false, not true. hashable is common in builtin type world, but not outside.

Comment thread vm/src/class.rs

@youknowone youknowone left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a few more style suggestions.

Comment thread vm/src/class.rs Outdated
Comment on lines +120 to +121
.map(|h| h as usize == unhashable_wrapper as usize)
.unwrap_or(false)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.map(|h| h as usize == unhashable_wrapper as usize)
.unwrap_or(false)
.map_or(0, |h| h as usize) == unhashable_wrapper as usize

the cost of map_or(0, |h| h as usize) is zero here due to its layout.

Comment thread vm/src/class.rs Outdated
class.set_attr(identifier!(ctx, __new__), bound);
}

if class

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to check if this is related to #3460 later

Comment thread vm/src/types/slot.rs Outdated
Comment on lines +426 to +427
.map(|a| a.is(&ctx.none()))
.unwrap_or(false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.map(|a| a.is(&ctx.none()))
.unwrap_or(false);
.map_or(false, |a| a.is(&ctx.none));

ctx.none() increase reference count of None. We don't need to do.

Comment thread vm/src/types/slot.rs Outdated
Comment on lines +428 to +432
if is_unhashable {
toggle_slot!(hash, unhashable_wrapper);
} else {
toggle_slot!(hash, hash_wrapper);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if is_unhashable {
toggle_slot!(hash, unhashable_wrapper);
} else {
toggle_slot!(hash, hash_wrapper);
}
let wrapper = if is_unhashable {
unhashable_wrapper
} else {
hash_wrapper
};
toggle_slot!(wrapper)

Comment thread vm/src/types/slot.rs Outdated
}

/// Marks a type as unhashable. Similar to PyObject_HashNotImplemented in CPython
pub fn unhashable_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn unhashable_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
pub fn hash_unhashable(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {

Other functions are called wrapper because they are wrapping a python call.
This function is not a wrapper of python call, so giving a different name would be better.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, probably hash_not_implemented by following cpython naming

@youknowone youknowone left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! it now passes deque too.

Comment thread vm/src/class.rs Outdated
const TP_NAME: &'static str;
const DOC: Option<&'static str> = None;
const BASICSIZE: usize;
const UNHASHABLE: bool;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const UNHASHABLE: bool;
const UNHASHABLE: bool = false;

default value can be placed here

Comment thread vm/src/class.rs Outdated
const TP_NAME: &'static str = T::TP_NAME;
const DOC: Option<&'static str> = T::DOC;
const BASICSIZE: usize = T::BASICSIZE;
const UNHASHABLE: bool = false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PyRef<T> must follows T

Suggested change
const UNHASHABLE: bool = false;
const UNHASHABLE: bool = T::UNHASHABLE;

@youknowone youknowone added the z-ls-2023 Tag to track Line OSS Sprint 2023 label Mar 7, 2023

@youknowone youknowone left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thank you for digging in this tricky issue.

Comment thread vm/src/builtins/slice.rs Outdated
@youknowone youknowone merged commit 223fe14 into RustPython:main Mar 7, 2023
@seungha-kim seungha-kim deleted the feature/unhashable branch March 7, 2023 14:53
minhrongcon2000 pushed a commit to minhrongcon2000/RustPython that referenced this pull request Mar 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

z-ls-2023 Tag to track Line OSS Sprint 2023

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants