Skip to content

Commit 0016e3e

Browse files
committed
[GR-71020] Fix missing descriptor for __hash__ in __slots__
PullRequest: graalpython/4074
2 parents 09752de + d911a94 commit 0016e3e

File tree

3 files changed

+33
-18
lines changed

3 files changed

+33
-18
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_descr.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -58,3 +58,22 @@ def test_overwrite___weakref__():
5858
class C:
5959
__weakref__ = 1
6060
assert C.__weakref__ == 1
61+
62+
63+
def test___hash___in___slots__():
64+
class ObjWithoutHash:
65+
def __eq__(self, other):
66+
return True
67+
68+
assert ObjWithoutHash.__hash__ is None
69+
70+
class ObjWithHashSlot:
71+
__slots__ = ("__hash__",)
72+
73+
def __eq__(self, other):
74+
return True
75+
76+
assert ObjWithHashSlot.__hash__ is not None
77+
o = ObjWithHashSlot()
78+
o.__hash__ = lambda: 1
79+
assert hash(o) == 1

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@
167167
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
168168
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.ReadPointerNode;
169169
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.WritePointerNode;
170-
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem;
171170
import com.oracle.graal.python.builtins.objects.dict.PDict;
172171
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
173172
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
@@ -1435,12 +1434,12 @@ private static void toNative(Object prtToWrite, TpSlotMeta def, Object slotNativ
14351434

14361435
@TruffleBoundary
14371436
public static void inherit(PythonClass klass, PDict namespace, MroSequenceStorage mro, boolean allocateAllGroups) {
1438-
Builder klassSlots = buildInherited(klass, null, mro, allocateAllGroups);
1437+
Builder klassSlots = buildInherited(klass, mro, allocateAllGroups);
14391438
klass.setTpSlots(klassSlots.build());
14401439
}
14411440

14421441
@TruffleBoundary
1443-
public static TpSlots.Builder buildInherited(PythonClass klass, PDict namespace, MroSequenceStorage mro, boolean allocateAllGroups) {
1442+
public static TpSlots.Builder buildInherited(PythonClass klass, MroSequenceStorage mro, boolean allocateAllGroups) {
14441443
// partially implements CPython:type_ready_inherit
14451444
// slots of native classes are initialized in GraalPyPrivate_AddInheritedSlots, they are
14461445
// just a mirror of the native slots initialized and inherited on the native side
@@ -1458,7 +1457,7 @@ public static TpSlots.Builder buildInherited(PythonClass klass, PDict namespace,
14581457
TpSlots slots = GetTpSlotsNode.executeUncached(type);
14591458
assert slots != null || type == klass;
14601459
if (slots != null) {
1461-
klassSlots.inherit(klass, namespace, slots);
1460+
klassSlots.inherit(klass, slots);
14621461
}
14631462
}
14641463
return klassSlots;
@@ -1814,7 +1813,7 @@ public Builder overrideIgnoreGroups(TpSlots other) {
18141813
return this;
18151814
}
18161815

1817-
private Builder inherit(PythonClass klass, PDict namespace, TpSlots base) {
1816+
private Builder inherit(PythonClass klass, TpSlots base) {
18181817
// similar to CPython:inherit_slots
18191818
// indirect slots (from tp_as_number etc.) are not inherited if the group is not
18201819
// allocated explicitly. Note: native heap types and managed types have always all
@@ -1843,22 +1842,19 @@ private Builder inherit(PythonClass klass, PDict namespace, TpSlots base) {
18431842
set(TpSlotMeta.TP_SETATTRO, base.tp_setattro());
18441843
}
18451844
if (get(TpSlotMeta.TP_RICHCOMPARE) == null && get(TpSlotMeta.TP_HASH) == null) {
1846-
if (!overridesHash(namespace)) {
1845+
if (!overridesHash(klass)) {
18471846
set(TpSlotMeta.TP_RICHCOMPARE, base.tp_richcmp());
18481847
set(TpSlotMeta.TP_HASH, base.tp_hash());
18491848
}
18501849
}
18511850
return this;
18521851
}
18531852

1854-
private static boolean overridesHash(PDict namespace) {
1855-
if (namespace == null) {
1856-
return false;
1857-
}
1858-
Object eq = HashingStorageGetItem.executeUncached(namespace.getDictStorage(), T___EQ__);
1859-
if (eq == null) {
1860-
Object hash = HashingStorageGetItem.executeUncached(namespace.getDictStorage(), T___HASH__);
1861-
return hash != null;
1853+
private static boolean overridesHash(PythonClass klass) {
1854+
Object eq = klass.getAttribute(T___EQ__);
1855+
if (eq == PNone.NO_VALUE) {
1856+
Object hash = klass.getAttribute(T___HASH__);
1857+
return hash != PNone.NO_VALUE;
18621858
}
18631859
return true;
18641860
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,11 +2010,11 @@ protected PythonClass makeType(VirtualFrame frame, PDict namespaceOrig, TruffleS
20102010
//
20112011
// - fixup_slot_dispatchers to set the slots according to magic methods.
20122012

2013-
Builder inheritedSlots = TpSlots.buildInherited(newType, namespace, getMroStorageNode.execute(inliningTarget, newType), true);
2013+
Builder inheritedSlots = TpSlots.buildInherited(newType, getMroStorageNode.execute(inliningTarget, newType), true);
20142014
// type_ready_set_hash
20152015
if (inheritedSlots.get(TpSlotMeta.TP_HASH) == null) {
2016-
Object dunderHash = getItemNamespace.execute(inliningTarget, namespace.getDictStorage(), T___HASH__);
2017-
if (dunderHash == null) {
2016+
Object dunderHash = newType.getAttribute(T___HASH__);
2017+
if (dunderHash == NO_VALUE) {
20182018
inheritedSlots.set(TpSlotMeta.TP_HASH, TpSlotHashFun.HASH_NOT_IMPLEMENTED);
20192019
newType.setAttribute(T___HASH__, PNone.NONE);
20202020
}

0 commit comments

Comments
 (0)