[FE] Introduce warnings on possible empty intersection types, and improve errors reporting in general

^KT-52361 Fixed
This commit is contained in:
Victor Petukhov
2022-05-23 10:10:59 +02:00
committed by teamcity
parent e133ee3765
commit 6a34b184ac
53 changed files with 501 additions and 335 deletions
@@ -0,0 +1,297 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.EmptyIntersectionTypeKind
import org.jetbrains.kotlin.types.isDefinitelyEmpty
import org.jetbrains.kotlin.types.isPossiblyEmpty
import org.jetbrains.kotlin.types.model.*
internal object EmptyIntersectionTypeChecker {
fun computeEmptyIntersectionTypeKind(
context: TypeSystemInferenceExtensionContext,
types: Collection<KotlinTypeMarker>
): EmptyIntersectionTypeKind = with(context) {
if (types.isEmpty())
return EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
@Suppress("NAME_SHADOWING")
val types = types.toList()
var possibleEmptyIntersectionKind: EmptyIntersectionTypeKind? = null
for (i in 0 until types.size) {
val firstType = types[i]
if (!mayCauseEmptyIntersection(firstType)) continue
val firstSubstitutedType by lazy { firstType.eraseContainingTypeParameters() }
for (j in i + 1 until types.size) {
val secondType = types[j]
if (!mayCauseEmptyIntersection(secondType)) continue
val secondSubstitutedType = secondType.eraseContainingTypeParameters()
if (!mayCauseEmptyIntersection(secondSubstitutedType) && !mayCauseEmptyIntersection(firstSubstitutedType)) continue
val kind = computeKindByHavingCommonSubtype(firstSubstitutedType, secondSubstitutedType)
if (kind.isDefinitelyEmpty())
return kind
if (kind.isPossiblyEmpty())
possibleEmptyIntersectionKind = kind
}
}
return possibleEmptyIntersectionKind ?: EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
}
private fun TypeSystemInferenceExtensionContext.computeKindByHavingCommonSubtype(
first: KotlinTypeMarker, second: KotlinTypeMarker
): EmptyIntersectionTypeKind {
fun extractIntersectionComponentsIfNeeded(type: KotlinTypeMarker) =
if (type.typeConstructor() is IntersectionTypeConstructorMarker) {
type.typeConstructor().supertypes().toList()
} else listOf(type)
val expandedTypes = extractIntersectionComponentsIfNeeded(first) + extractIntersectionComponentsIfNeeded(second)
val typeCheckerState by lazy { newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true) }
var possibleEmptyIntersectionKind: EmptyIntersectionTypeKind? = null
for (i in expandedTypes.indices) {
val firstType = expandedTypes[i].withNullability(false)
val firstTypeConstructor = firstType.typeConstructor()
if (!mayCauseEmptyIntersection(firstType))
continue
for (j in i + 1 until expandedTypes.size) {
val secondType = expandedTypes[j].withNullability(false)
val secondTypeConstructor = secondType.typeConstructor()
if (!mayCauseEmptyIntersection(secondType))
continue
if (areEqualTypeConstructors(firstTypeConstructor, secondTypeConstructor) && secondTypeConstructor.parametersCount() == 0)
continue
if (AbstractTypeChecker.areRelatedBySubtyping(this, firstType, secondType))
continue
// If two classes aren't related by subtyping and no need to compare their type arguments, then they can't have a common subtype
if (
firstTypeConstructor.isDefinitelyClassTypeConstructor() && secondTypeConstructor.isDefinitelyClassTypeConstructor()
&& (firstTypeConstructor.parametersCount() == 0 || secondTypeConstructor.parametersCount() == 0)
) {
return EmptyIntersectionTypeKind.MULTIPLE_CLASSES
}
val superTypeByFirstConstructor = AbstractTypeChecker.findCorrespondingSupertypes(
typeCheckerState, firstType.lowerBoundIfFlexible(), secondTypeConstructor
).singleOrNull()
val superTypeBySecondConstructor = AbstractTypeChecker.findCorrespondingSupertypes(
typeCheckerState, secondType.lowerBoundIfFlexible(), firstTypeConstructor
).singleOrNull()
val anyInference = firstTypeConstructor.isInterface() || secondTypeConstructor.isInterface()
// Two classes can't have a common subtype if neither is a subtype of another
if (superTypeByFirstConstructor == null && superTypeBySecondConstructor == null && !anyInference)
return EmptyIntersectionTypeKind.MULTIPLE_CLASSES
if (anyInference && !canHaveCommonSubtypeWithInterface(firstType, secondType))
return EmptyIntersectionTypeKind.INCOMPATIBLE_SUPERTYPES
if (superTypeByFirstConstructor == null || superTypeBySecondConstructor == null) {
// don't have incompatible supertypes so can have a common subtype only if all types are interfaces
if (firstTypeConstructor.isFinalClassConstructor() || secondTypeConstructor.isFinalClassConstructor()) {
possibleEmptyIntersectionKind = EmptyIntersectionTypeKind.SINGLE_FINAL_CLASS
}
continue
}
val argumentsIntersectionKind =
computeKindByCheckingTypeArguments(superTypeByFirstConstructor, superTypeBySecondConstructor)
if (argumentsIntersectionKind.isDefinitelyEmpty())
return argumentsIntersectionKind
if (possibleEmptyIntersectionKind == null && argumentsIntersectionKind.isPossiblyEmpty())
possibleEmptyIntersectionKind = argumentsIntersectionKind
}
}
return possibleEmptyIntersectionKind ?: EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
}
private fun TypeSystemInferenceExtensionContext.computeKindByCheckingTypeArguments(
firstType: KotlinTypeMarker,
secondType: KotlinTypeMarker,
): EmptyIntersectionTypeKind {
require(firstType.typeConstructor() == secondType.typeConstructor()) {
"Type constructors of the passed types should be the same to compare their arguments"
}
fun isSubtypeOf(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker) =
AbstractTypeChecker.isSubtypeOf(this, firstType, secondType)
fun areEqualTypes(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker) =
AbstractTypeChecker.equalTypes(this, firstType, secondType)
fun Boolean.toEmptyIntersectionKind() =
if (this) EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION else EmptyIntersectionTypeKind.INCOMPATIBLE_TYPE_ARGUMENTS
var possibleEmptyIntersectionKind: EmptyIntersectionTypeKind? = null
for ((i, argumentOfFirst) in firstType.getArguments().withIndex()) {
@Suppress("NAME_SHADOWING")
val argumentOfFirst = uncaptureIfNeeded(argumentOfFirst)
val argumentOfSecond = uncaptureIfNeeded(secondType.getArgument(i))
if (argumentOfFirst == argumentOfSecond || argumentOfFirst.isStarProjection() || argumentOfSecond.isStarProjection())
continue
val argumentTypeOfFirst = argumentOfFirst.getType()
val argumentTypeOfSecond = argumentOfSecond.getType()
val intersectionKindOfArguments = when {
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.INV) ->
areEqualTypes(argumentTypeOfFirst, argumentTypeOfSecond).toEmptyIntersectionKind()
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.OUT) -> {
isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond).toEmptyIntersectionKind()
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.IN) -> {
isSubtypeOf(argumentTypeOfSecond, argumentTypeOfFirst).toEmptyIntersectionKind()
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.IN, TypeVariance.OUT) -> {
if (argumentTypeOfFirst.argumentsCount() == 0 && argumentTypeOfSecond.argumentsCount() == 0) {
isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond).toEmptyIntersectionKind()
} else {
computeKindByHavingCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond)
}
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.OUT, TypeVariance.OUT)
|| areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.IN, TypeVariance.IN) -> {
computeKindByHavingCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond)
}
else -> true.toEmptyIntersectionKind()
}
if (intersectionKindOfArguments.isDefinitelyEmpty())
return intersectionKindOfArguments
if (possibleEmptyIntersectionKind == null && intersectionKindOfArguments.isPossiblyEmpty())
possibleEmptyIntersectionKind = intersectionKindOfArguments
}
return possibleEmptyIntersectionKind ?: EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
}
private fun TypeSystemInferenceExtensionContext.canHaveCommonSubtypeWithInterface(
firstType: KotlinTypeMarker, secondType: KotlinTypeMarker
): Boolean {
require(firstType.typeConstructor().isInterface() || secondType.typeConstructor().isInterface()) {
"One of the passed type should be an interface"
}
@Suppress("NAME_SHADOWING")
val firstType = firstType.eraseContainingTypeParameters()
@Suppress("NAME_SHADOWING")
val secondType = secondType.eraseContainingTypeParameters()
// interface A<K>
// interface B: A<String>
// interface C: A<Int>
// B & C can't have common subtype due to having incompatible supertypes: A<String> and A<Int>
return !hasIncompatibleSuperTypes(firstType, secondType)
}
private fun TypeSystemInferenceExtensionContext.areIncompatibleSuperTypes(
firstType: KotlinTypeMarker, secondType: KotlinTypeMarker
): Boolean = firstType.typeConstructor() == secondType.typeConstructor()
&& !AbstractTypeChecker.equalTypes(
newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true),
firstType,
secondType
)
// interface A<T>
// interface B : A<Int>
// interface C : A<String>
// => B and C have incompatible supertypes
private fun TypeSystemInferenceExtensionContext.hasIncompatibleSuperTypes(
firstType: KotlinTypeMarker, secondType: KotlinTypeMarker
): Boolean {
val superTypesOfFirst = firstType.typeConstructor().supertypes()
val firstTypeSubstitutor = createSubstitutorForSuperTypes(firstType)
val superTypesOfSecond = secondType.typeConstructor().supertypes()
val secondTypeSubstitutor = createSubstitutorForSuperTypes(secondType)
for (superTypeOfFirst in superTypesOfFirst) {
@Suppress("NAME_SHADOWING")
val superTypeOfFirst = firstTypeSubstitutor?.safeSubstitute(superTypeOfFirst) ?: superTypeOfFirst
if (areIncompatibleSuperTypes(superTypeOfFirst, secondType))
return true
for (superTypeOfSecond in superTypesOfSecond) {
@Suppress("NAME_SHADOWING")
val superTypeOfSecond = secondTypeSubstitutor?.safeSubstitute(superTypeOfSecond) ?: superTypeOfSecond
if (
areIncompatibleSuperTypes(firstType, superTypeOfSecond)
|| areIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond)
) return true
if (hasIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond))
return true
}
}
return false
}
private fun TypeSystemInferenceExtensionContext.mayCauseEmptyIntersection(type: KotlinTypeMarker): Boolean {
val typeConstructor = type.typeConstructor()
if (!typeConstructor.isClassTypeConstructor() && !typeConstructor.isTypeParameterTypeConstructor())
return false
// Even two interfaces may be an empty intersection type:
// interface Inv<K>
// interface B : Inv<Int>
// `Inv<String> & B` or `Inv<String> & Inv<Int>` are empty
// So we don't filter out interfaces here
return !typeConstructor.isAnyConstructor() && !typeConstructor.isNothingConstructor()
}
private fun TypeSystemInferenceExtensionContext.areArgumentsOfSpecifiedVariances(
firstType: KotlinTypeMarker,
secondType: KotlinTypeMarker,
argumentIndex: Int,
variance1: TypeVariance,
variance2: TypeVariance,
): Boolean {
fun getEffectiveVariance(type: KotlinTypeMarker): TypeVariance? {
val argument = uncaptureIfNeeded(type.getArgument(argumentIndex))
val parameter = type.typeConstructor().getParameter(argumentIndex)
return AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
}
val effectiveVariance1 = getEffectiveVariance(firstType)
val effectiveVariance2 = getEffectiveVariance(secondType)
return (effectiveVariance1 == variance1 && effectiveVariance2 == variance2)
|| (effectiveVariance1 == variance2 && effectiveVariance2 == variance1)
}
private fun TypeSystemInferenceExtensionContext.uncaptureIfNeeded(argument: TypeArgumentMarker): TypeArgumentMarker {
val type = argument.getType()
return if (type is CapturedTypeMarker) type.typeConstructorProjection() else argument
}
}
@@ -6,6 +6,19 @@
package org.jetbrains.kotlin.types
// TODO: add `SINGLE_FINAL_CLASS` later to report warnings
enum class EmptyIntersectionTypeKind { NOT_EMPTY_INTERSECTION, MULTIPLE_CLASSES }
enum class EmptyIntersectionTypeKind(val description: String? = null) {
NOT_EMPTY_INTERSECTION,
MULTIPLE_CLASSES("multiple incompatible classes"),
INCOMPATIBLE_SUPERTYPES("incompatible supertypes"),
INCOMPATIBLE_TYPE_ARGUMENTS("incompatible type arguments"),
SINGLE_FINAL_CLASS
}
fun EmptyIntersectionTypeKind.isDefinitelyEmpty(): Boolean = this == EmptyIntersectionTypeKind.MULTIPLE_CLASSES
fun EmptyIntersectionTypeKind.isDefinitelyEmpty(): Boolean =
this == EmptyIntersectionTypeKind.MULTIPLE_CLASSES
|| this == EmptyIntersectionTypeKind.INCOMPATIBLE_SUPERTYPES
|| this == EmptyIntersectionTypeKind.INCOMPATIBLE_TYPE_ARGUMENTS
fun EmptyIntersectionTypeKind.isPossiblyEmpty(): Boolean = this == EmptyIntersectionTypeKind.SINGLE_FINAL_CLASS
fun EmptyIntersectionTypeKind.isEmpty(): Boolean = this != EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.types.model
import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeChecker
import org.jetbrains.kotlin.types.*
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@@ -217,6 +218,8 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui
fun KotlinTypeMarker.hasExactAnnotation(): Boolean
fun KotlinTypeMarker.hasNoInferAnnotation(): Boolean
fun TypeConstructorMarker.isFinalClassConstructor(): Boolean
fun TypeVariableMarker.freshTypeConstructor(): TypeConstructorMarker
fun CapturedTypeMarker.typeConstructorProjection(): TypeArgumentMarker
@@ -321,252 +324,13 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui
return createCapturedType(starProjection, listOf(superType), lowerType = null, CaptureStatus.FROM_EXPRESSION)
}
fun Collection<KotlinTypeMarker>.computeEmptyIntersectionTypeKind(): EmptyIntersectionTypeKind {
if (this.isEmpty())
return EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
val types = this.toList()
for (i in 0 until types.size) {
val firstType = types[i]
if (!firstType.mayCauseEmptyIntersection()) continue
val firstSubstitutedType by lazy { firstType.eraseContainingTypeParameters() }
for (j in i + 1 until types.size) {
val secondType = types[j]
if (!secondType.mayCauseEmptyIntersection()) continue
val secondSubstitutedType = secondType.eraseContainingTypeParameters()
if (!secondSubstitutedType.mayCauseEmptyIntersection() && !firstSubstitutedType.mayCauseEmptyIntersection()) continue
if (!canHaveCommonSubtype(firstSubstitutedType, secondSubstitutedType))
return EmptyIntersectionTypeKind.MULTIPLE_CLASSES
}
}
return EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
}
fun createSubstitutorForSuperTypes(baseType: KotlinTypeMarker): TypeSubstitutorMarker?
private fun areIncompatibleSuperTypes(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker): Boolean =
firstType.typeConstructor() == secondType.typeConstructor()
&& !AbstractTypeChecker.equalTypes(
newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true),
firstType,
secondType
)
// interface A<T>
// interface B : A<Int>
// interface C : A<String>
// => B and C have incompatible supertypes
private fun hasIncompatibleSuperTypes(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker): Boolean {
val superTypesOfFirst = firstType.typeConstructor().supertypes()
val firstTypeSubstitutor = createSubstitutorForSuperTypes(firstType)
val superTypesOfSecond = secondType.typeConstructor().supertypes()
val secondTypeSubstitutor = createSubstitutorForSuperTypes(secondType)
for (superTypeOfFirst in superTypesOfFirst) {
@Suppress("NAME_SHADOWING")
val superTypeOfFirst = firstTypeSubstitutor?.safeSubstitute(superTypeOfFirst) ?: superTypeOfFirst
if (areIncompatibleSuperTypes(superTypeOfFirst, secondType))
return true
for (superTypeOfSecond in superTypesOfSecond) {
@Suppress("NAME_SHADOWING")
val superTypeOfSecond = secondTypeSubstitutor?.safeSubstitute(superTypeOfSecond) ?: superTypeOfSecond
if (
areIncompatibleSuperTypes(firstType, superTypeOfSecond)
|| areIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond)
) return true
if (hasIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond))
return true
}
}
return false
}
private fun canHaveCommonSubtypeWithInterface(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker): Boolean {
require(firstType.typeConstructor().isInterface() || secondType.typeConstructor().isInterface()) {
"One of the passed type should be an interface"
}
@Suppress("NAME_SHADOWING")
val firstType = firstType.eraseContainingTypeParameters()
@Suppress("NAME_SHADOWING")
val secondType = secondType.eraseContainingTypeParameters()
// interface A<K>
// interface B: A<String>
// interface C: A<Int>
// B & C can't have common subtype due to having incompatible supertypes: A<String> and A<Int>
return !hasIncompatibleSuperTypes(firstType, secondType)
}
private fun canHaveCommonSubtype(first: KotlinTypeMarker, second: KotlinTypeMarker): Boolean {
fun extractIntersectionComponentsIfNeeded(type: KotlinTypeMarker) =
if (type.typeConstructor() is IntersectionTypeConstructorMarker) {
type.typeConstructor().supertypes().toList()
} else listOf(type)
val expandedTypes = extractIntersectionComponentsIfNeeded(first) + extractIntersectionComponentsIfNeeded(second)
val typeCheckerState by lazy { newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true) }
for (i in expandedTypes.indices) {
val firstType = expandedTypes[i].withNullability(false)
val firstTypeConstructor = firstType.typeConstructor()
if (!firstType.mayCauseEmptyIntersection())
continue
for (j in i + 1 until expandedTypes.size) {
val secondType = expandedTypes[j].withNullability(false)
val secondTypeConstructor = secondType.typeConstructor()
if (!secondType.mayCauseEmptyIntersection())
continue
if (areEqualTypeConstructors(firstTypeConstructor, secondTypeConstructor) && secondTypeConstructor.parametersCount() == 0)
continue
if (AbstractTypeChecker.areRelatedBySubtyping(this, firstType, secondType))
continue
// If two classes aren't related by subtyping and no need to compare their type arguments, then they can't have a common subtype
if (
firstTypeConstructor.isDefinitelyClassTypeConstructor() && secondTypeConstructor.isDefinitelyClassTypeConstructor()
&& (firstTypeConstructor.parametersCount() == 0 || secondTypeConstructor.parametersCount() == 0)
) return false
val superTypeByFirstConstructor = AbstractTypeChecker.findCorrespondingSupertypes(
typeCheckerState, firstType.lowerBoundIfFlexible(), secondTypeConstructor
).singleOrNull()
val superTypeBySecondConstructor = AbstractTypeChecker.findCorrespondingSupertypes(
typeCheckerState, secondType.lowerBoundIfFlexible(), firstTypeConstructor
).singleOrNull()
val anyInference = firstTypeConstructor.isInterface() || secondTypeConstructor.isInterface()
// Two classes can't have a common subtype if neither is a subtype of another
if (superTypeByFirstConstructor == null && superTypeBySecondConstructor == null && !anyInference)
return false
if (anyInference && !canHaveCommonSubtypeWithInterface(firstType, secondType))
return false
if (superTypeByFirstConstructor == null || superTypeBySecondConstructor == null)
continue // don't have incompatible supertypes so can have a common subtype
if (!checkArgumentsOfTypesToBeAbleToHaveCommonSubtype(superTypeByFirstConstructor, superTypeBySecondConstructor))
return false
}
}
return true
}
private fun TypeArgumentMarker.uncaptureIfNeeded(): TypeArgumentMarker {
val type = getType()
return if (type is CapturedTypeMarker) type.typeConstructorProjection() else this
}
fun computeEmptyIntersectionTypeKind(types: Collection<KotlinTypeMarker>): EmptyIntersectionTypeKind =
EmptyIntersectionTypeChecker.computeEmptyIntersectionTypeKind(this, types)
private fun computeEffectiveVariance(parameter: TypeParameterMarker, argument: TypeArgumentMarker): TypeVariance? =
AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
private fun KotlinTypeMarker.mayCauseEmptyIntersection(): Boolean {
val typeConstructor = typeConstructor()
if (!typeConstructor.isClassTypeConstructor() && !typeConstructor.isTypeParameterTypeConstructor())
return false
// Even two interfaces may be an empty intersection type:
// interface Inv<K>
// interface B : Inv<Int>
// `Inv<String> & B` or `Inv<String> & Inv<Int>` are empty
// So we don't filter out interfaces here
return !typeConstructor.isAnyConstructor() && !typeConstructor.isNothingConstructor()
}
private fun areArgumentsOfSpecifiedVariances(
firstType: KotlinTypeMarker,
secondType: KotlinTypeMarker,
argumentIndex: Int,
variance1: TypeVariance,
variance2: TypeVariance,
): Boolean {
fun getEffectiveVariance(type: KotlinTypeMarker): TypeVariance? {
val argument = type.getArgument(argumentIndex).uncaptureIfNeeded()
val parameter = type.typeConstructor().getParameter(argumentIndex)
return AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
}
val effectiveVariance1 = getEffectiveVariance(firstType)
val effectiveVariance2 = getEffectiveVariance(secondType)
return (effectiveVariance1 == variance1 && effectiveVariance2 == variance2)
|| (effectiveVariance1 == variance2 && effectiveVariance2 == variance1)
}
private fun checkArgumentsOfTypesToBeAbleToHaveCommonSubtype(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker): Boolean {
require(firstType.typeConstructor() == secondType.typeConstructor()) {
"Type constructors of the passed types should be the same to compare their arguments"
}
fun isSubtypeOf(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker) =
AbstractTypeChecker.isSubtypeOf(this, firstType, secondType)
fun areEqualTypes(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker) =
AbstractTypeChecker.equalTypes(this, firstType, secondType)
for ((i, argumentOfFirst) in firstType.getArguments().withIndex()) {
@Suppress("NAME_SHADOWING")
val argumentOfFirst = argumentOfFirst.uncaptureIfNeeded()
val argumentOfSecond = secondType.getArgument(i).uncaptureIfNeeded()
if (argumentOfFirst == argumentOfSecond || argumentOfFirst.isStarProjection() || argumentOfSecond.isStarProjection())
continue
val argumentTypeOfFirst = argumentOfFirst.getType()
val argumentTypeOfSecond = argumentOfSecond.getType()
when {
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.INV) ->
return areEqualTypes(argumentTypeOfFirst, argumentTypeOfSecond)
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.OUT) -> {
if (!isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond))
return false
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.IN) -> {
if (!isSubtypeOf(argumentTypeOfSecond, argumentTypeOfFirst))
return false
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.IN, TypeVariance.OUT) -> {
if (argumentTypeOfFirst.argumentsCount() == 0 && argumentTypeOfSecond.argumentsCount() == 0) {
if (!isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond))
return false
} else if (!canHaveCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond)) {
return false
}
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.OUT, TypeVariance.OUT)
|| areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.IN, TypeVariance.IN) -> {
if (!canHaveCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond))
return false
}
}
}
return true
}
}