Skip to content

Commit e208d90

Browse files
committed
[GR-53092] Fix super descriptor binding for subclasses.
PullRequest: graalpython/4530
2 parents 0a96435 + bed6a32 commit e208d90

2 files changed

Lines changed: 66 additions & 3 deletions

File tree

  • graalpython

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

Lines changed: 42 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, 2026, 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
@@ -47,3 +47,44 @@ def m(self): return ["B"] + self.my_super.m()
4747
B.my_super = B.my_super
4848

4949
assert B().m() == ["B", "A"]
50+
51+
52+
def test_super_subclass_descr_get_invokes_subclass_type():
53+
class MySuper(super):
54+
news = []
55+
calls = []
56+
57+
def __new__(cls, *args):
58+
cls.news.append(args)
59+
return super().__new__(cls)
60+
61+
def __init__(self, *args):
62+
type(self).calls.append(args)
63+
super().__init__(*args)
64+
65+
class A:
66+
def f(self):
67+
return "A.f"
68+
69+
class B(A):
70+
pass
71+
72+
raw = MySuper(B)
73+
MySuper.news.clear()
74+
MySuper.calls.clear()
75+
obj = B()
76+
bound = raw.__get__(obj, B)
77+
78+
assert type(bound) is MySuper
79+
assert MySuper.news == [(B, obj)]
80+
assert MySuper.calls == [(B, obj)]
81+
assert bound.f() == "A.f"
82+
83+
raw = MySuper.__new__(MySuper)
84+
MySuper.news.clear()
85+
MySuper.calls.clear()
86+
bound = raw.__get__(obj, B)
87+
88+
assert type(bound) is MySuper
89+
assert MySuper.news == [()]
90+
assert MySuper.calls == [()]

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,25 @@
9090
import com.oracle.graal.python.nodes.bytecode.FrameInfo;
9191
import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode;
9292
import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode;
93+
import com.oracle.graal.python.nodes.call.CallNode;
9394
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
9495
import com.oracle.graal.python.nodes.frame.ReadFrameNode;
9596
import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode;
9697
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
9798
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
9899
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
99100
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
101+
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile;
100102
import com.oracle.graal.python.nodes.object.GetClassNode;
103+
import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode;
101104
import com.oracle.graal.python.nodes.object.IsForeignObjectNode;
102105
import com.oracle.graal.python.runtime.CallerFlags;
103106
import com.oracle.graal.python.runtime.PythonOptions;
104107
import com.oracle.graal.python.runtime.exception.PException;
105108
import com.oracle.graal.python.runtime.exception.PythonErrorType;
106109
import com.oracle.graal.python.runtime.object.PFactory;
107110
import com.oracle.truffle.api.CompilerDirectives;
111+
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
108112
import com.oracle.truffle.api.bytecode.BytecodeFrame;
109113
import com.oracle.truffle.api.bytecode.BytecodeNode;
110114
import com.oracle.truffle.api.dsl.Bind;
@@ -459,19 +463,37 @@ private Object supercheck(VirtualFrame frame, Node inliningTarget, Object cls, O
459463
@GenerateNodeFactory
460464
public abstract static class GetNode extends DescrGetBuiltinNode {
461465
@Specialization
462-
static Object doNoneOrBound(SuperObject self, Object obj, @SuppressWarnings("unused") Object type,
466+
static Object doNoneOrBound(VirtualFrame frame, SuperObject self, Object obj, @SuppressWarnings("unused") Object type,
463467
@Bind Node inliningTarget,
464468
@Cached InlinedConditionProfile objIsNoneProfile,
465469
@Cached InlinedConditionProfile selfObjIsNullProfile,
470+
@Cached IsBuiltinClassExactProfile isBuiltinSuperProfile,
466471
@Cached GetObjectNode getObject,
472+
@Cached GetTypeNode getType,
473+
@Cached GetPythonObjectClassNode getClass,
474+
@Cached CallNode callNode,
475+
@Cached InlinedConditionProfile superTypeIsNullProfile,
467476
@Cached DoGetNode doGetNode) {
468-
// TODO: (GR-53092) doesn't seem to handle super subclasses like CPython
469477
if (objIsNoneProfile.profile(inliningTarget, PGuards.isPNone(obj)) || //
470478
selfObjIsNullProfile.profile(inliningTarget, getObject.execute(inliningTarget, self) != null)) {
471479
return self;
472480
}
481+
Object cls = getClass.execute(inliningTarget, self);
482+
if (!isBuiltinSuperProfile.profileClass(inliningTarget, cls, PythonBuiltinClassType.Super)) {
483+
return doSuperSubclass(frame, inliningTarget, self, obj, cls, getType, callNode, superTypeIsNullProfile);
484+
}
473485
return doGetNode.execute(inliningTarget, self, obj);
474486
}
487+
488+
@InliningCutoff
489+
private static Object doSuperSubclass(VirtualFrame frame, Node inliningTarget, SuperObject self, Object obj, Object cls, GetTypeNode getType, CallNode callNode,
490+
InlinedConditionProfile superTypeIsNullProfile) {
491+
Object superType = getType.execute(inliningTarget, self);
492+
if (superTypeIsNullProfile.profile(inliningTarget, superType == null)) {
493+
return callNode.execute(frame, cls);
494+
}
495+
return callNode.execute(frame, cls, superType, obj);
496+
}
475497
}
476498

477499
@GenerateInline

0 commit comments

Comments
 (0)