[FE] Cache checking intersection type emptiness results

This commit is contained in:
Victor Petukhov
2022-05-05 18:23:29 +02:00
committed by teamcity
parent 9a3b5e547b
commit e133ee3765
4 changed files with 27 additions and 8 deletions
@@ -584,7 +584,7 @@ internal object CheckIncompatibleTypeVariableUpperBounds : ResolutionStage() {
if (upperTypes.size <= 1 || variableWithConstraints.constraints.any { it.kind.isLower() })
continue
if (upperTypes.computeEmptyIntersectionTypeKind().isDefinitelyEmpty()) {
if (candidate.system.getEmptyIntersectionTypeKind(upperTypes).isDefinitelyEmpty()) {
sink.yieldDiagnostic(
@Suppress("UNCHECKED_CAST")
InferredEmptyIntersectionDiagnostic(
@@ -10,6 +10,8 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemC
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
import org.jetbrains.kotlin.types.EmptyIntersectionTypeKind
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeVariableMarker
interface NewConstraintSystem {
@@ -24,6 +26,8 @@ interface NewConstraintSystem {
fun asConstraintSystemCompleterContext(): ConstraintSystemCompletionContext
fun asPostponedArgumentsAnalyzerContext(): PostponedArgumentsAnalyzerContext
fun processForkConstraints()
fun getEmptyIntersectionTypeKind(types: Collection<KotlinTypeMarker>): EmptyIntersectionTypeKind
}
typealias ForkPointData = List<ConstraintsFromSingleFork>
@@ -10,10 +10,7 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext
import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.components.*
import org.jetbrains.kotlin.types.AbstractTypeApproximator
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.types.isDefinitelyEmpty
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.SmartSet
@@ -41,6 +38,8 @@ 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 var couldBeResolvedWithUnrestrictedBuilderInference: Boolean = false
@@ -444,6 +443,21 @@ class NewConstraintSystemImpl(
doPostponedComputationsIfAllVariablesAreFixed()
}
override fun getEmptyIntersectionTypeKind(types: Collection<KotlinTypeMarker>): EmptyIntersectionTypeKind {
if (types in emptyIntersectionTypesCache)
return EmptyIntersectionTypeKind.MULTIPLE_CLASSES
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)
}
}
}
private fun checkInferredEmptyIntersection(variable: TypeVariableMarker, resultType: KotlinTypeMarker) {
val intersectionTypeConstructor = resultType.typeConstructor().takeIf { it is IntersectionTypeConstructorMarker } ?: return
val isInferredEmptyIntersectionForbidden =
@@ -454,7 +468,7 @@ class NewConstraintSystemImpl(
if (upperTypes.size <= 1 || storage.errors.any { it is InferredEmptyIntersection && it.incompatibleTypes == upperTypes })
return
if (upperTypes.computeEmptyIntersectionTypeKind().isDefinitelyEmpty()) {
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 =
@@ -889,7 +889,8 @@ internal object CheckIncompatibleTypeVariableUpperBounds : ResolutionPart() {
callComponents.statelessCallbacks.isOldIntersectionIsEmpty(upperTypes.cast())
override fun ResolutionCandidate.process(workIndex: Int) = with(getSystem().asConstraintSystemCompleterContext()) {
for (variableWithConstraints in getSystem().getBuilder().currentStorage().notFixedTypeVariables.values) {
val constraintSystem = getSystem()
for (variableWithConstraints in constraintSystem.getBuilder().currentStorage().notFixedTypeVariables.values) {
val upperTypes = variableWithConstraints.constraints.extractUpperTypesToCheckIntersectionEmptiness()
when {
@@ -900,7 +901,7 @@ internal object CheckIncompatibleTypeVariableUpperBounds : ResolutionPart() {
markCandidateForCompatibilityResolve(needToReportWarning = false)
continue
}
upperTypes.computeEmptyIntersectionTypeKind().isDefinitelyEmpty() -> {
constraintSystem.getEmptyIntersectionTypeKind(upperTypes).isDefinitelyEmpty() -> {
val isInferredEmptyIntersectionForbidden =
callComponents.languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)