[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
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.model
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability.*
import org.jetbrains.kotlin.types.EmptyIntersectionTypeKind
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeVariableMarker
@@ -138,16 +139,19 @@ class NoSuccessfulFork(val position: IncorporationConstraintPosition) : Constrai
sealed interface InferredEmptyIntersection {
val incompatibleTypes: Collection<KotlinTypeMarker>
val typeVariable: TypeVariableMarker
val kind: EmptyIntersectionTypeKind
}
class InferredEmptyIntersectionWarning(
override val incompatibleTypes: Collection<KotlinTypeMarker>,
override val typeVariable: TypeVariableMarker
override val typeVariable: TypeVariableMarker,
override val kind: EmptyIntersectionTypeKind,
) : ConstraintSystemError(RESOLVED), InferredEmptyIntersection
class InferredEmptyIntersectionError(
override val incompatibleTypes: Collection<KotlinTypeMarker>,
override val typeVariable: TypeVariableMarker
override val typeVariable: TypeVariableMarker,
override val kind: EmptyIntersectionTypeKind,
) : ConstraintSystemError(INAPPLICABLE), InferredEmptyIntersection
class OnlyInputTypesDiagnostic(val typeVariable: TypeVariableMarker) : ConstraintSystemError(INAPPLICABLE)
@@ -38,9 +38,7 @@ class NewConstraintSystemImpl(
private val typeVariablesTransaction: MutableList<TypeVariableMarker> = SmartList()
private val properTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val notProperTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val emptyIntersectionTypesCache: MutableSet<Collection<KotlinTypeMarker>> = SmartSet.create()
private val nonEmptyIntersectionTypesCache: MutableSet<Collection<KotlinTypeMarker>> = SmartSet.create()
private val intersectionTypesCache: MutableMap<Collection<KotlinTypeMarker>, EmptyIntersectionTypeKind> = mutableMapOf()
private var couldBeResolvedWithUnrestrictedBuilderInference: Boolean = false
override var atCompletionState: Boolean = false
@@ -444,37 +442,34 @@ class NewConstraintSystemImpl(
}
override fun getEmptyIntersectionTypeKind(types: Collection<KotlinTypeMarker>): EmptyIntersectionTypeKind {
if (types in emptyIntersectionTypesCache)
return EmptyIntersectionTypeKind.MULTIPLE_CLASSES
if (types in intersectionTypesCache)
return intersectionTypesCache.getValue(types)
if (types in nonEmptyIntersectionTypesCache)
return EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
return types.computeEmptyIntersectionTypeKind().also {
when (it) {
EmptyIntersectionTypeKind.MULTIPLE_CLASSES -> emptyIntersectionTypesCache.add(types)
EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION -> nonEmptyIntersectionTypesCache.add(types)
}
return computeEmptyIntersectionTypeKind(types).also {
intersectionTypesCache[types] = it
}
}
private fun checkInferredEmptyIntersection(variable: TypeVariableMarker, resultType: KotlinTypeMarker) {
val intersectionTypeConstructor = resultType.typeConstructor().takeIf { it is IntersectionTypeConstructorMarker } ?: return
val isInferredEmptyIntersectionForbidden =
languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)
val upperTypes = intersectionTypeConstructor.supertypes()
// Diagnostic with these incompatible types has already been reported at the resolution stage
if (upperTypes.size <= 1 || storage.errors.any { it is InferredEmptyIntersection && it.incompatibleTypes == upperTypes })
return
if (getEmptyIntersectionTypeKind(upperTypes).isDefinitelyEmpty()) {
// Remove existing errors from resolution stage because a completion error is more precise
storage.errors.removeIf { it is InferredEmptyIntersection }
val errorFactory =
if (isInferredEmptyIntersectionForbidden) ::InferredEmptyIntersectionError else ::InferredEmptyIntersectionWarning
addError(errorFactory(upperTypes, variable))
}
val emptyIntersectionKind = getEmptyIntersectionTypeKind(upperTypes).takeIf { it.isEmpty() } ?: return
// Remove existing errors from the resolution stage because a completion stage error is always more precise
storage.errors.removeIf { it is InferredEmptyIntersection }
val isInferredEmptyIntersectionForbidden =
languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)
val errorFactory = if (emptyIntersectionKind.isDefinitelyEmpty() && isInferredEmptyIntersectionForbidden)
::InferredEmptyIntersectionError
else ::InferredEmptyIntersectionWarning
addError(errorFactory(upperTypes, variable, emptyIntersectionKind))
}
private fun checkMissedConstraints() {