AbstractTypeChecker: don't create forks for identical supertypes
The main reason of this commit is the fact that it makes no sense to create type checker forks in case of two or more types which equal each other. Also, this commit fixes the test taken from KT-59514 (see interconnectedGenerics.kt test changed in this commit). In this particular case we have a subType of C.WithL<Pr!, En<Pr>!>, and a superType of R<T> with a superType constructor R. In findCorrespondingSupertypes we get *two* similar supertypes here, both have a string representation of: R<Il<@EnhancedNullability En<Pr>!>!>. If we create two forks because of it, we get NEW_INFERENCE_ERROR with the following subtyping violation: Il<@EnhancedNullability En<Pr>!>! <: En<Pr>!. NEW_INFERENCE_ERROR happens because we make a redundant fork in that case, but the forks should still work despite it (see KT-62333). These two types appear due to the content of FirCorrespondingSupertypeCache. For a key C.WithL and a superType R it stores the following pair of supertypes: - R<Il<@EnhancedNullability S>!> - R<Il<(@EnhancedNullability S & Any, @EnhancedNullability S?)>!> After substitution of S to En<Pr>! they become similar. NB: if we change jspecify severity level from strict to warn, then 'R<Il<(S & Any, S?)>!>' is the only remaining supertype, and @EnhancedNullability annotation is no more in use. The type hierarchy in this example looks like: WithL<T, S> <: CanWithB<T, S>, R.F<S, Il<S>> CanWithB<T, S> <: R.F<S, Il<S>> R.F<S, Il<S>> <: R<Il<S>> So R is reachable by two different ways, via CanWithB and directly via R.F. #KT-59514 Fixed
This commit is contained in:
committed by
Space Team
parent
d4ec088afe
commit
87904fd766
@@ -374,8 +374,20 @@ object AbstractTypeChecker {
|
||||
if (areEqualTypeConstructors(subType.typeConstructor(), superConstructor) && superConstructor.parametersCount() == 0) return true
|
||||
if (superType.typeConstructor().isAnyConstructor()) return true
|
||||
|
||||
val supertypesWithSameConstructor = findCorrespondingSupertypes(state, subType, superConstructor)
|
||||
.map { state.prepareType(it).asSimpleType() ?: it }
|
||||
val supertypesWithSameConstructor = with(findCorrespondingSupertypes(state, subType, superConstructor)) {
|
||||
// Note: in K1, we can have partially computed types here, like SomeType<NON COMPUTED YET>
|
||||
// (see e.g. interClassesRecursion.kt from diagnostic tests)
|
||||
// In this case we don't want to affect lazy computation in normal case (size <= 1), that's why we don't create a set
|
||||
// (adding to a hash set requires hash-code calculation for each set element)
|
||||
|
||||
if (size > 1 && (state.typeSystemContext as? TypeSystemInferenceExtensionContext)?.isK2 == true) {
|
||||
// Here we want to filter out equivalent types to avoid unnecessary forking
|
||||
mapTo(mutableSetOf()) { state.prepareType(it).asSimpleType() ?: it }
|
||||
} else {
|
||||
// TODO: drop this branch together with K1 code
|
||||
map { state.prepareType(it).asSimpleType() ?: it }
|
||||
}
|
||||
}
|
||||
when (supertypesWithSameConstructor.size) {
|
||||
0 -> return hasNothingSupertype(state, subType) // todo Nothing & Array<Number> <: Array<String>
|
||||
1 -> return state.isSubtypeForSameConstructor(supertypesWithSameConstructor.first().asArgumentList(), superType)
|
||||
|
||||
Reference in New Issue
Block a user