[FE] Show causing types in the INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION diagnostic
This commit is contained in:
committed by
teamcity
parent
6a34b184ac
commit
867ad24c86
+72
-78
@@ -12,16 +12,15 @@ import org.jetbrains.kotlin.types.isPossiblyEmpty
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
|
||||
internal object EmptyIntersectionTypeChecker {
|
||||
fun computeEmptyIntersectionTypeKind(
|
||||
fun computeEmptyIntersectionEmptiness(
|
||||
context: TypeSystemInferenceExtensionContext,
|
||||
types: Collection<KotlinTypeMarker>
|
||||
): EmptyIntersectionTypeKind = with(context) {
|
||||
if (types.isEmpty())
|
||||
return EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
|
||||
): EmptyIntersectionTypeInfo? = with(context) {
|
||||
if (types.isEmpty()) return null
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val types = types.toList()
|
||||
var possibleEmptyIntersectionKind: EmptyIntersectionTypeKind? = null
|
||||
var possibleEmptyIntersectionTypeInfo: EmptyIntersectionTypeInfo? = null
|
||||
|
||||
for (i in 0 until types.size) {
|
||||
val firstType = types[i]
|
||||
@@ -39,22 +38,22 @@ internal object EmptyIntersectionTypeChecker {
|
||||
|
||||
if (!mayCauseEmptyIntersection(secondSubstitutedType) && !mayCauseEmptyIntersection(firstSubstitutedType)) continue
|
||||
|
||||
val kind = computeKindByHavingCommonSubtype(firstSubstitutedType, secondSubstitutedType)
|
||||
val typeInfo = computeByHavingCommonSubtype(firstSubstitutedType, secondSubstitutedType) ?: continue
|
||||
|
||||
if (kind.isDefinitelyEmpty())
|
||||
return kind
|
||||
if (typeInfo.kind.isDefinitelyEmpty())
|
||||
return typeInfo
|
||||
|
||||
if (kind.isPossiblyEmpty())
|
||||
possibleEmptyIntersectionKind = kind
|
||||
if (typeInfo.kind.isPossiblyEmpty())
|
||||
possibleEmptyIntersectionTypeInfo = typeInfo
|
||||
}
|
||||
}
|
||||
|
||||
return possibleEmptyIntersectionKind ?: EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
|
||||
return possibleEmptyIntersectionTypeInfo
|
||||
}
|
||||
|
||||
private fun TypeSystemInferenceExtensionContext.computeKindByHavingCommonSubtype(
|
||||
private fun TypeSystemInferenceExtensionContext.computeByHavingCommonSubtype(
|
||||
first: KotlinTypeMarker, second: KotlinTypeMarker
|
||||
): EmptyIntersectionTypeKind {
|
||||
): EmptyIntersectionTypeInfo? {
|
||||
fun extractIntersectionComponentsIfNeeded(type: KotlinTypeMarker) =
|
||||
if (type.typeConstructor() is IntersectionTypeConstructorMarker) {
|
||||
type.typeConstructor().supertypes().toList()
|
||||
@@ -62,7 +61,7 @@ internal object EmptyIntersectionTypeChecker {
|
||||
|
||||
val expandedTypes = extractIntersectionComponentsIfNeeded(first) + extractIntersectionComponentsIfNeeded(second)
|
||||
val typeCheckerState by lazy { newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true) }
|
||||
var possibleEmptyIntersectionKind: EmptyIntersectionTypeKind? = null
|
||||
var possibleEmptyIntersectionKind: EmptyIntersectionTypeInfo? = null
|
||||
|
||||
for (i in expandedTypes.indices) {
|
||||
val firstType = expandedTypes[i].withNullability(false)
|
||||
@@ -89,7 +88,7 @@ internal object EmptyIntersectionTypeChecker {
|
||||
firstTypeConstructor.isDefinitelyClassTypeConstructor() && secondTypeConstructor.isDefinitelyClassTypeConstructor()
|
||||
&& (firstTypeConstructor.parametersCount() == 0 || secondTypeConstructor.parametersCount() == 0)
|
||||
) {
|
||||
return EmptyIntersectionTypeKind.MULTIPLE_CLASSES
|
||||
return EmptyIntersectionTypeInfo(EmptyIntersectionTypeKind.MULTIPLE_CLASSES, firstType, secondType)
|
||||
}
|
||||
|
||||
val superTypeByFirstConstructor = AbstractTypeChecker.findCorrespondingSupertypes(
|
||||
@@ -103,37 +102,42 @@ internal object EmptyIntersectionTypeChecker {
|
||||
|
||||
// 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
|
||||
return EmptyIntersectionTypeInfo(EmptyIntersectionTypeKind.MULTIPLE_CLASSES, firstType, secondType)
|
||||
|
||||
if (anyInference && !canHaveCommonSubtypeWithInterface(firstType, secondType))
|
||||
return EmptyIntersectionTypeKind.INCOMPATIBLE_SUPERTYPES
|
||||
if (anyInference) {
|
||||
val incompatibleSupertypes = getIncompatibleSuperTypes(firstType, secondType)
|
||||
if (incompatibleSupertypes != null) {
|
||||
return EmptyIntersectionTypeInfo(EmptyIntersectionTypeKind.INCOMPATIBLE_SUPERTYPES, *incompatibleSupertypes)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
possibleEmptyIntersectionKind =
|
||||
EmptyIntersectionTypeInfo(EmptyIntersectionTypeKind.SINGLE_FINAL_CLASS, firstType, secondType)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
val argumentsIntersectionKind =
|
||||
computeKindByCheckingTypeArguments(superTypeByFirstConstructor, superTypeBySecondConstructor)
|
||||
computeByCheckingTypeArguments(superTypeByFirstConstructor, superTypeBySecondConstructor) ?: continue
|
||||
|
||||
if (argumentsIntersectionKind.isDefinitelyEmpty())
|
||||
if (argumentsIntersectionKind.kind.isDefinitelyEmpty())
|
||||
return argumentsIntersectionKind
|
||||
|
||||
if (possibleEmptyIntersectionKind == null && argumentsIntersectionKind.isPossiblyEmpty())
|
||||
if (possibleEmptyIntersectionKind == null && argumentsIntersectionKind.kind.isPossiblyEmpty())
|
||||
possibleEmptyIntersectionKind = argumentsIntersectionKind
|
||||
}
|
||||
}
|
||||
|
||||
return possibleEmptyIntersectionKind ?: EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
|
||||
return possibleEmptyIntersectionKind
|
||||
}
|
||||
|
||||
private fun TypeSystemInferenceExtensionContext.computeKindByCheckingTypeArguments(
|
||||
private fun TypeSystemInferenceExtensionContext.computeByCheckingTypeArguments(
|
||||
firstType: KotlinTypeMarker,
|
||||
secondType: KotlinTypeMarker,
|
||||
): EmptyIntersectionTypeKind {
|
||||
): EmptyIntersectionTypeInfo? {
|
||||
require(firstType.typeConstructor() == secondType.typeConstructor()) {
|
||||
"Type constructors of the passed types should be the same to compare their arguments"
|
||||
}
|
||||
@@ -144,10 +148,10 @@ internal object EmptyIntersectionTypeChecker {
|
||||
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
|
||||
fun Boolean.toEmptyIntersectionKind(vararg types: KotlinTypeMarker) =
|
||||
if (this) null else EmptyIntersectionTypeInfo(EmptyIntersectionTypeKind.INCOMPATIBLE_TYPE_ARGUMENTS, *types)
|
||||
|
||||
var possibleEmptyIntersectionKind: EmptyIntersectionTypeKind? = null
|
||||
var possibleEmptyIntersectionTypeInfo: EmptyIntersectionTypeInfo? = null
|
||||
|
||||
for ((i, argumentOfFirst) in firstType.getArguments().withIndex()) {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
@@ -161,43 +165,42 @@ internal object EmptyIntersectionTypeChecker {
|
||||
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()
|
||||
}
|
||||
areEqualTypes(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
.toEmptyIntersectionKind(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.OUT) ->
|
||||
isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
.toEmptyIntersectionKind(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.IN) ->
|
||||
isSubtypeOf(argumentTypeOfSecond, argumentTypeOfFirst)
|
||||
.toEmptyIntersectionKind(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.IN, TypeVariance.OUT) -> {
|
||||
if (argumentTypeOfFirst.argumentsCount() == 0 && argumentTypeOfSecond.argumentsCount() == 0) {
|
||||
isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond).toEmptyIntersectionKind()
|
||||
isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
.toEmptyIntersectionKind(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
} else {
|
||||
computeKindByHavingCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
computeByHavingCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
}
|
||||
}
|
||||
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.OUT, TypeVariance.OUT)
|
||||
|| areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.IN, TypeVariance.IN) -> {
|
||||
computeKindByHavingCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
computeByHavingCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
}
|
||||
else -> true.toEmptyIntersectionKind()
|
||||
}
|
||||
else -> true.toEmptyIntersectionKind(argumentTypeOfFirst, argumentTypeOfSecond)
|
||||
} ?: continue
|
||||
|
||||
if (intersectionKindOfArguments.isDefinitelyEmpty())
|
||||
if (intersectionKindOfArguments.kind.isDefinitelyEmpty())
|
||||
return intersectionKindOfArguments
|
||||
|
||||
if (possibleEmptyIntersectionKind == null && intersectionKindOfArguments.isPossiblyEmpty())
|
||||
possibleEmptyIntersectionKind = intersectionKindOfArguments
|
||||
if (possibleEmptyIntersectionTypeInfo == null && intersectionKindOfArguments.kind.isPossiblyEmpty())
|
||||
possibleEmptyIntersectionTypeInfo = intersectionKindOfArguments
|
||||
}
|
||||
|
||||
return possibleEmptyIntersectionKind ?: EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
|
||||
return possibleEmptyIntersectionTypeInfo
|
||||
}
|
||||
|
||||
private fun TypeSystemInferenceExtensionContext.canHaveCommonSubtypeWithInterface(
|
||||
private fun TypeSystemInferenceExtensionContext.getIncompatibleSuperTypes(
|
||||
firstType: KotlinTypeMarker, secondType: KotlinTypeMarker
|
||||
): Boolean {
|
||||
require(firstType.typeConstructor().isInterface() || secondType.typeConstructor().isInterface()) {
|
||||
"One of the passed type should be an interface"
|
||||
}
|
||||
): Array<KotlinTypeMarker>? {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val firstType = firstType.eraseContainingTypeParameters()
|
||||
|
||||
@@ -207,26 +210,7 @@ internal object EmptyIntersectionTypeChecker {
|
||||
// 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 {
|
||||
// => B and C have incompatible supertypes
|
||||
val superTypesOfFirst = firstType.typeConstructor().supertypes()
|
||||
val firstTypeSubstitutor = createSubstitutorForSuperTypes(firstType)
|
||||
val superTypesOfSecond = secondType.typeConstructor().supertypes()
|
||||
@@ -237,25 +221,33 @@ internal object EmptyIntersectionTypeChecker {
|
||||
val superTypeOfFirst = firstTypeSubstitutor?.safeSubstitute(superTypeOfFirst) ?: superTypeOfFirst
|
||||
|
||||
if (areIncompatibleSuperTypes(superTypeOfFirst, secondType))
|
||||
return true
|
||||
return arrayOf(superTypeOfFirst, secondType)
|
||||
|
||||
for (superTypeOfSecond in superTypesOfSecond) {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val superTypeOfSecond = secondTypeSubstitutor?.safeSubstitute(superTypeOfSecond) ?: superTypeOfSecond
|
||||
|
||||
if (
|
||||
areIncompatibleSuperTypes(firstType, superTypeOfSecond)
|
||||
|| areIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond)
|
||||
) return true
|
||||
if (areIncompatibleSuperTypes(firstType, superTypeOfSecond))
|
||||
return arrayOf(firstType, superTypeOfSecond)
|
||||
|
||||
if (hasIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond))
|
||||
return true
|
||||
if (areIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond))
|
||||
return arrayOf(superTypeOfFirst, superTypeOfSecond)
|
||||
|
||||
getIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond)?.let { return it }
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return null
|
||||
}
|
||||
|
||||
private fun TypeSystemInferenceExtensionContext.areIncompatibleSuperTypes(
|
||||
firstType: KotlinTypeMarker, secondType: KotlinTypeMarker
|
||||
): Boolean = firstType.typeConstructor() == secondType.typeConstructor()
|
||||
&& !AbstractTypeChecker.equalTypes(
|
||||
newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true),
|
||||
firstType, secondType
|
||||
)
|
||||
|
||||
private fun TypeSystemInferenceExtensionContext.mayCauseEmptyIntersection(type: KotlinTypeMarker): Boolean {
|
||||
val typeConstructor = type.typeConstructor()
|
||||
|
||||
@@ -294,4 +286,6 @@ internal object EmptyIntersectionTypeChecker {
|
||||
val type = argument.getType()
|
||||
return if (type is CapturedTypeMarker) type.typeConstructorProjection() else argument
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyIntersectionTypeInfo(val kind: EmptyIntersectionTypeKind, vararg val casingTypes: KotlinTypeMarker)
|
||||
@@ -5,13 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.types
|
||||
|
||||
// TODO: add `SINGLE_FINAL_CLASS` later to report warnings
|
||||
enum class EmptyIntersectionTypeKind(val description: String? = null) {
|
||||
NOT_EMPTY_INTERSECTION,
|
||||
enum class EmptyIntersectionTypeKind(val description: String) {
|
||||
MULTIPLE_CLASSES("multiple incompatible classes"),
|
||||
INCOMPATIBLE_SUPERTYPES("incompatible supertypes"),
|
||||
INCOMPATIBLE_TYPE_ARGUMENTS("incompatible type arguments"),
|
||||
SINGLE_FINAL_CLASS
|
||||
SINGLE_FINAL_CLASS("final class and interface")
|
||||
}
|
||||
|
||||
fun EmptyIntersectionTypeKind.isDefinitelyEmpty(): Boolean =
|
||||
@@ -20,5 +18,3 @@ fun EmptyIntersectionTypeKind.isDefinitelyEmpty(): Boolean =
|
||||
|| this == EmptyIntersectionTypeKind.INCOMPATIBLE_TYPE_ARGUMENTS
|
||||
|
||||
fun EmptyIntersectionTypeKind.isPossiblyEmpty(): Boolean = this == EmptyIntersectionTypeKind.SINGLE_FINAL_CLASS
|
||||
|
||||
fun EmptyIntersectionTypeKind.isEmpty(): Boolean = this != EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.types.model
|
||||
|
||||
import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeInfo
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
@@ -326,8 +327,8 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui
|
||||
|
||||
fun createSubstitutorForSuperTypes(baseType: KotlinTypeMarker): TypeSubstitutorMarker?
|
||||
|
||||
fun computeEmptyIntersectionTypeKind(types: Collection<KotlinTypeMarker>): EmptyIntersectionTypeKind =
|
||||
EmptyIntersectionTypeChecker.computeEmptyIntersectionTypeKind(this, types)
|
||||
fun computeEmptyIntersectionTypeKind(types: Collection<KotlinTypeMarker>): EmptyIntersectionTypeInfo? =
|
||||
EmptyIntersectionTypeChecker.computeEmptyIntersectionEmptiness(this, types)
|
||||
|
||||
private fun computeEffectiveVariance(parameter: TypeParameterMarker, argument: TypeArgumentMarker): TypeVariance? =
|
||||
AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
|
||||
|
||||
Reference in New Issue
Block a user