Skip to content

Commit 7f93b56

Browse files
committed
fix IsSubtypeNode for variable subtypes and constant basetype
The isSubMro method tried to be clever and only check if the basetype is at a single position in the subtype's MRO. This works fine if all classes from the subtype up to (but not including) the basetype class only have a single base. But if anything in between has multiple bases, the MRO will have extra elements in between. Example: _io.UnsupportedOperation inherits from ValueError and OSError, leading to this MRO: <class 'io.UnsupportedOperation'> <class 'OSError'> <class 'ValueError'> <class 'Exception'> <class 'BaseException'> <class 'object'> OSError has this: <class 'OSError'> <class 'Exception'> <class 'BaseException'> <class 'object'> The diff is two, previously the IsSubtypeNode might now check at index 2 of the subtype's MRO, and find ValueError to be not the same as OSError. Thus the need to loop from index 0 to index 2.
1 parent ea46983 commit 7f93b56

File tree

1 file changed

+37
-14
lines changed
  • graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes

1 file changed

+37
-14
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/IsSubtypeNode.java

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,29 @@ protected static boolean isSameType(IsSameTypeNode isSameTypeNode, Object cls, O
8585
return isSameTypeNode.execute(cls, cachedCls);
8686
}
8787

88-
protected boolean isSubMro(Object base, MroSequenceStorage derivedMro, int baseMroLen, IsSameTypeNode isSameTypeNode) {
89-
CompilerAsserts.partialEvaluationConstant(baseMroLen);
90-
PythonAbstractClass[] derivedMroAry = derivedMro.getInternalClassArray();
91-
int derivedMroLen = derivedMroAry.length;
92-
int offset = derivedMroLen - baseMroLen;
93-
if (offset >= 0) {
94-
return isSameType(isSameTypeNode, derivedMroAry[offset], base);
95-
} else {
96-
return false;
88+
/**
89+
* This method is used to search for a constant base type in the mro of
90+
* non-constant potential subtypes when all subtypes' MROs have the same
91+
* length. Since the entire base mro must strictly be behind the base, we
92+
* only need to search from the beginning of the mro to the length
93+
* difference.
94+
*/
95+
@ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN)
96+
protected boolean isSubMro(Object base, PythonAbstractClass[] derivedMroAry, int mroDiff, IsSameTypeNode isSameTypeNode) {
97+
CompilerAsserts.partialEvaluationConstant(base);
98+
CompilerAsserts.partialEvaluationConstant(mroDiff);
99+
for (int i = 0; i <= mroDiff; i++) {
100+
if (isSameType(isSameTypeNode, derivedMroAry[i], base)) {
101+
return true;
102+
}
97103
}
104+
return false;
98105
}
99106

107+
/**
108+
* This method is used to search in a constant length subtype mro for a
109+
* (non-constant) base type. It has to loop over the entire mro to do this.
110+
*/
100111
@ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN)
101112
protected boolean isInMro(Object cls, MroSequenceStorage mro, int sz, IsSameTypeNode isSameTypeNode) {
102113
PythonAbstractClass[] mroAry = mro.getInternalClassArray();
@@ -147,19 +158,27 @@ boolean isSubtypeOfCachedMultiContext(Object derived, Object cls,
147158
return isInMro;
148159
}
149160

161+
protected static int sub(int a, int b) {
162+
return a - b;
163+
}
164+
150165
@Specialization(guards = {
151166
"cachedCls != null",
152167
"getType(cls, builtinTypeProfile, builtinClassProfile) == cachedCls",
153-
"isKindOfBuiltinClass(derived)" // see assertion in isSubMro
168+
"isKindOfBuiltinClass(derived)", // see assertion in isSubMro
169+
"mroAry.length == derivedMroLen",
170+
"mroDiff < 16",
154171
}, replaces = "isSubtypeOfCachedMultiContext", limit = "getVariableArgumentInlineCacheLimit()")
155172
boolean isVariableSubtypeOfConstantTypeCachedMultiContext(Object derived, @SuppressWarnings("unused") Object cls,
156173
@SuppressWarnings("unused") @Cached ConditionProfile builtinTypeProfile,
157174
@SuppressWarnings("unused") @Cached ConditionProfile builtinClassProfile,
158175
@Cached IsSameTypeNode isSameTypeNode,
159176
@Cached GetMroStorageNode getMro,
177+
@Bind("getMro.execute(derived).getInternalClassArray()") PythonAbstractClass[] mroAry,
178+
@Cached("mroAry.length") int derivedMroLen,
160179
@Cached("getType(cls, builtinTypeProfile, builtinClassProfile)") PythonBuiltinClassType cachedCls,
161-
@Cached("getMro.execute(cachedCls).getInternalClassArray().length") int baseMroLen) {
162-
return isSubMro(cachedCls, getMro.execute(derived), baseMroLen, isSameTypeNode);
180+
@Cached("sub(derivedMroLen, getMro.execute(cachedCls).getInternalClassArray().length)") int mroDiff) {
181+
return isSubMro(cachedCls, mroAry, mroDiff, isSameTypeNode);
163182
}
164183

165184
@Specialization(guards = {
@@ -215,6 +234,8 @@ boolean isSubtypeOfVariableTypeCached(@SuppressWarnings("unused") Object derived
215234
@Specialization(guards = {
216235
"isKindOfBuiltinClass(derived)", // see assertion in isSubMro
217236
"isKindOfBuiltinClass(cls)", // see assertion in isSubMro
237+
"mroAry.length == derivedMroLen",
238+
"mroDiff < 16",
218239
"isSameType(isSameClsNode, cls, cachedCls)",
219240
}, limit = "getVariableArgumentInlineCacheLimit()", replaces = {
220241
"isSubtypeOfCachedMultiContext",
@@ -229,10 +250,12 @@ boolean isVariableSubtypeOfConstantTypeCached(Object derived, @SuppressWarnings(
229250
@Cached("cls") @SuppressWarnings("unused") Object cachedCls,
230251
@Cached GetMroStorageNode getMro,
231252
@SuppressWarnings("unused") @Cached("getMro.execute(cachedCls)") MroSequenceStorage baseMro,
232-
@Cached("baseMro.getInternalClassArray().length") int baseMroLen,
233253
@Cached IsSameTypeNode isSameTypeInLoopNode,
254+
@Bind("getMro.execute(derived).getInternalClassArray()") PythonAbstractClass[] mroAry,
255+
@Cached("mroAry.length") int derivedMroLen,
256+
@Cached("sub(derivedMroLen, baseMro.getInternalClassArray().length)") int mroDiff,
234257
@Cached @SuppressWarnings("unused") IsSameTypeNode isSameClsNode) {
235-
return isSubMro(cachedCls, getMro.execute(derived), baseMroLen, isSameTypeInLoopNode);
258+
return isSubMro(cachedCls, mroAry, mroDiff, isSameTypeInLoopNode);
236259
}
237260

238261
@Specialization(guards = {

0 commit comments

Comments
 (0)