@@ -129,6 +129,7 @@ class PythonSubtypeChecker(
129
129
if (! left.isPythonType() || ! right.isPythonType())
130
130
error(" Trying to create PythonSubtypeChecker for non-Python types $left , $right " )
131
131
}
132
+
132
133
fun rightIsSubtypeOfLeft (): Boolean {
133
134
val leftWrapper = PythonTypeWrapperForEqualityCheck (left)
134
135
val rightWrapper = PythonTypeWrapperForEqualityCheck (right)
@@ -171,6 +172,7 @@ class PythonSubtypeChecker(
171
172
is PythonTupleTypeDescription -> caseOfLeftTupleType(leftMeta)
172
173
}
173
174
}
175
+
174
176
private fun caseOfLeftTupleType (leftMeta : PythonTupleTypeDescription ): Boolean {
175
177
return when (val rightMeta = right.pythonDescription()) {
176
178
is PythonAnyTypeDescription -> true
@@ -195,6 +197,7 @@ class PythonSubtypeChecker(
195
197
else -> false
196
198
}
197
199
}
200
+
198
201
private fun caseOfLeftOverload (leftMeta : PythonOverloadTypeDescription ): Boolean {
199
202
val leftAsStatefulType = leftMeta.castToCompatibleTypeApi(left)
200
203
return leftAsStatefulType.parameters.all {
@@ -208,6 +211,7 @@ class PythonSubtypeChecker(
208
211
).rightIsSubtypeOfLeft()
209
212
}
210
213
}
214
+
211
215
private fun caseOfLeftCompositeType (leftMeta : PythonConcreteCompositeTypeDescription ): Boolean {
212
216
if (left.isPythonObjectType())
213
217
return true
@@ -221,7 +225,8 @@ class PythonSubtypeChecker(
221
225
val (args, param) = it
222
226
val (leftArg, rightArg) = args
223
227
if (leftArg.pythonDescription() is PythonAnyTypeDescription ||
224
- rightArg.pythonDescription() is PythonAnyTypeDescription )
228
+ rightArg.pythonDescription() is PythonAnyTypeDescription
229
+ )
225
230
return @all true
226
231
val typeVarDescription = param.pythonDescription()
227
232
if (typeVarDescription !is PythonTypeVarDescription ) // shouldn't be possible
@@ -264,17 +269,21 @@ class PythonSubtypeChecker(
264
269
else -> false
265
270
}
266
271
}
272
+
267
273
private fun caseOfLeftTypeVar (leftMeta : PythonTypeVarDescription ): Boolean {
268
274
// TODO: more accurate case analysis
275
+ if (! typeParameterCorrespondence.any { it.first == left })
276
+ return true // treat unbounded TypeVars like Any. TODO: here might occur false-positives
269
277
return when (val rightMeta = right.pythonDescription()) {
270
278
is PythonAnyTypeDescription -> true
271
279
is PythonTypeVarDescription -> caseOfLeftAndRightTypeVar(leftMeta, rightMeta)
272
280
else -> false
273
281
}
274
282
}
283
+
275
284
private fun caseOfLeftUnion (leftMeta : PythonUnionTypeDescription ): Boolean {
276
285
val children = leftMeta.getAnnotationParameters(left)
277
- return children.any { childType ->
286
+ return children.any { childType ->
278
287
PythonSubtypeChecker (
279
288
left = childType,
280
289
right = right,
@@ -285,12 +294,17 @@ class PythonSubtypeChecker(
285
294
).rightIsSubtypeOfLeft()
286
295
}
287
296
}
288
- private fun caseOfLeftAndRightTypeVar (leftMeta : PythonTypeVarDescription , rightMeta : PythonTypeVarDescription ): Boolean {
297
+
298
+ private fun caseOfLeftAndRightTypeVar (
299
+ leftMeta : PythonTypeVarDescription ,
300
+ rightMeta : PythonTypeVarDescription
301
+ ): Boolean {
289
302
val leftParam = leftMeta.castToCompatibleTypeApi(left)
290
303
val rightParam = rightMeta.castToCompatibleTypeApi(right)
291
304
// TODO: more accurate case analysis
292
305
return typeParameterCorrespondence.contains(Pair (leftParam, rightParam))
293
306
}
307
+
294
308
private fun caseOfLeftProtocol (leftMeta : PythonProtocolDescription ): Boolean {
295
309
val membersNotToCheck = listOf (" __new__" , " __init__" )
296
310
return leftMeta.protocolMemberNames.subtract(membersNotToCheck).all { protocolMemberName ->
@@ -310,6 +324,7 @@ class PythonSubtypeChecker(
310
324
).rightIsSubtypeOfLeft()
311
325
}
312
326
}
327
+
313
328
private fun caseOfLeftCallable (leftMeta : PythonCallableTypeDescription ): Boolean {
314
329
val rightCallAttribute = right.getPythonAttributeByName(pythonTypeStorage, " __call__" )?.type as ? FunctionType
315
330
? : return false
@@ -321,12 +336,21 @@ class PythonSubtypeChecker(
321
336
val rightBounded = rightCallAttribute.getBoundedParameters()
322
337
323
338
// TODO: more accurate case analysis
324
- if (leftBounded.size != rightBounded.size)
339
+ if (rightBounded.isNotEmpty() && leftBounded.size != rightBounded.size)
325
340
return false
326
341
327
- val newCorrespondence = typeParameterCorrespondence + (leftBounded zip rightBounded)
342
+ var newLeftAsFunctionType = leftAsFunctionType
343
+
344
+ // TODO: here might occur false-positives
345
+ if (rightBounded.isEmpty() && leftBounded.isNotEmpty()) {
346
+ val newLeft = DefaultSubstitutionProvider .substitute(left, leftBounded.associateWith { pythonAnyType })
347
+ newLeftAsFunctionType = leftMeta.castToCompatibleTypeApi(newLeft)
348
+ }
349
+
350
+ val newCorrespondence = typeParameterCorrespondence +
351
+ if (rightBounded.isNotEmpty()) (leftBounded zip rightBounded) else emptyList()
328
352
329
- var args = leftAsFunctionType .arguments zip rightCallAttribute.arguments
353
+ var args = newLeftAsFunctionType .arguments zip rightCallAttribute.arguments
330
354
if (skipFirstArgument)
331
355
args = args.drop(1 )
332
356
@@ -340,7 +364,7 @@ class PythonSubtypeChecker(
340
364
recursionDepth + 1
341
365
).rightIsSubtypeOfLeft()
342
366
} && PythonSubtypeChecker (
343
- left = leftAsFunctionType .returnValue,
367
+ left = newLeftAsFunctionType .returnValue,
344
368
right = rightCallAttribute.returnValue,
345
369
pythonTypeStorage,
346
370
newCorrespondence,
@@ -353,14 +377,20 @@ class PythonSubtypeChecker(
353
377
correspondence.map { Pair (it.second, it.first) }
354
378
355
379
private val nextAssumingSubtypePairs: List <Pair <PythonTypeWrapperForEqualityCheck , PythonTypeWrapperForEqualityCheck >>
356
- by lazy {
357
- if (left.pythonDescription() is PythonCompositeTypeDescription
358
- && right.pythonDescription() is PythonCompositeTypeDescription )
359
- assumingSubtypePairs +
360
- listOf (Pair (PythonTypeWrapperForEqualityCheck (left), PythonTypeWrapperForEqualityCheck (right)))
361
- else
362
- assumingSubtypePairs
363
- }
380
+ by lazy {
381
+ if (left.pythonDescription() is PythonCompositeTypeDescription
382
+ && right.pythonDescription() is PythonCompositeTypeDescription
383
+ )
384
+ assumingSubtypePairs +
385
+ listOf (
386
+ Pair (
387
+ PythonTypeWrapperForEqualityCheck (left),
388
+ PythonTypeWrapperForEqualityCheck (right)
389
+ )
390
+ )
391
+ else
392
+ assumingSubtypePairs
393
+ }
364
394
365
395
companion object {
366
396
fun checkIfRightIsSubtypeOfLeft (left : Type , right : Type , pythonTypeStorage : PythonTypeStorage ): Boolean =
0 commit comments