Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion graalpython/com.oracle.graal.python.test/src/tests/test_descr.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -58,3 +58,22 @@ def test_overwrite___weakref__():
class C:
__weakref__ = 1
assert C.__weakref__ == 1


def test___hash___in___slots__():
class ObjWithoutHash:
def __eq__(self, other):
return True

assert ObjWithoutHash.__hash__ is None

class ObjWithHashSlot:
__slots__ = ("__hash__",)

def __eq__(self, other):
return True

assert ObjWithHashSlot.__hash__ is not None
o = ObjWithHashSlot()
o.__hash__ = lambda: 1
assert hash(o) == 1
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.ReadPointerNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.WritePointerNode;
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem;
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
Expand Down Expand Up @@ -1435,12 +1434,12 @@ private static void toNative(Object prtToWrite, TpSlotMeta def, Object slotNativ

@TruffleBoundary
public static void inherit(PythonClass klass, PDict namespace, MroSequenceStorage mro, boolean allocateAllGroups) {
Builder klassSlots = buildInherited(klass, null, mro, allocateAllGroups);
Builder klassSlots = buildInherited(klass, mro, allocateAllGroups);
klass.setTpSlots(klassSlots.build());
}

@TruffleBoundary
public static TpSlots.Builder buildInherited(PythonClass klass, PDict namespace, MroSequenceStorage mro, boolean allocateAllGroups) {
public static TpSlots.Builder buildInherited(PythonClass klass, MroSequenceStorage mro, boolean allocateAllGroups) {
// partially implements CPython:type_ready_inherit
// slots of native classes are initialized in GraalPyPrivate_AddInheritedSlots, they are
// just a mirror of the native slots initialized and inherited on the native side
Expand All @@ -1458,7 +1457,7 @@ public static TpSlots.Builder buildInherited(PythonClass klass, PDict namespace,
TpSlots slots = GetTpSlotsNode.executeUncached(type);
assert slots != null || type == klass;
if (slots != null) {
klassSlots.inherit(klass, namespace, slots);
klassSlots.inherit(klass, slots);
}
}
return klassSlots;
Expand Down Expand Up @@ -1814,7 +1813,7 @@ public Builder overrideIgnoreGroups(TpSlots other) {
return this;
}

private Builder inherit(PythonClass klass, PDict namespace, TpSlots base) {
private Builder inherit(PythonClass klass, TpSlots base) {
// similar to CPython:inherit_slots
// indirect slots (from tp_as_number etc.) are not inherited if the group is not
// allocated explicitly. Note: native heap types and managed types have always all
Expand Down Expand Up @@ -1843,22 +1842,19 @@ private Builder inherit(PythonClass klass, PDict namespace, TpSlots base) {
set(TpSlotMeta.TP_SETATTRO, base.tp_setattro());
}
if (get(TpSlotMeta.TP_RICHCOMPARE) == null && get(TpSlotMeta.TP_HASH) == null) {
if (!overridesHash(namespace)) {
if (!overridesHash(klass)) {
set(TpSlotMeta.TP_RICHCOMPARE, base.tp_richcmp());
set(TpSlotMeta.TP_HASH, base.tp_hash());
}
}
return this;
}

private static boolean overridesHash(PDict namespace) {
if (namespace == null) {
return false;
}
Object eq = HashingStorageGetItem.executeUncached(namespace.getDictStorage(), T___EQ__);
if (eq == null) {
Object hash = HashingStorageGetItem.executeUncached(namespace.getDictStorage(), T___HASH__);
return hash != null;
private static boolean overridesHash(PythonClass klass) {
Object eq = klass.getAttribute(T___EQ__);
if (eq == PNone.NO_VALUE) {
Object hash = klass.getAttribute(T___HASH__);
return hash != PNone.NO_VALUE;
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2010,11 +2010,11 @@ protected PythonClass makeType(VirtualFrame frame, PDict namespaceOrig, TruffleS
//
// - fixup_slot_dispatchers to set the slots according to magic methods.

Builder inheritedSlots = TpSlots.buildInherited(newType, namespace, getMroStorageNode.execute(inliningTarget, newType), true);
Builder inheritedSlots = TpSlots.buildInherited(newType, getMroStorageNode.execute(inliningTarget, newType), true);
// type_ready_set_hash
if (inheritedSlots.get(TpSlotMeta.TP_HASH) == null) {
Object dunderHash = getItemNamespace.execute(inliningTarget, namespace.getDictStorage(), T___HASH__);
if (dunderHash == null) {
Object dunderHash = newType.getAttribute(T___HASH__);
if (dunderHash == NO_VALUE) {
inheritedSlots.set(TpSlotMeta.TP_HASH, TpSlotHashFun.HASH_NOT_IMPLEMENTED);
newType.setAttribute(T___HASH__, PNone.NONE);
}
Expand Down