[FIR] Support determining emptiness of an intersection type with type parameters' types

This commit is contained in:
Victor Petukhov
2022-04-05 17:19:06 +03:00
committed by teamcity
parent 313da6f41e
commit 47d6236a2d
97 changed files with 1970 additions and 106 deletions
@@ -8,11 +8,9 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
import org.jetbrains.kotlin.types.AbstractTypeChecker.isRelatedBySubtypingTo
import org.jetbrains.kotlin.types.model.*
abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context {
@@ -37,50 +35,6 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex
abstract fun couldBeResolvedWithUnrestrictedBuilderInference(): Boolean
abstract fun processForkConstraints()
private fun TypeConstructorMarker.isDefinitelyClass() = isClassTypeConstructor() && !isInterface()
private fun KotlinTypeMarker.containsTypeParameter() = contains { it.typeConstructor().isTypeParameterTypeConstructor() }
fun Collection<KotlinTypeMarker>.isEmptyIntersection(constraintSystem: NewConstraintSystem?): Boolean {
val indexedComponents = withIndex()
return indexedComponents.any firstTypes@{ (i, first) ->
val firstTypeConstructor = first.typeConstructor()
indexedComponents.any secondTypes@{ (j, second) ->
if (i >= j) return@secondTypes false
val secondTypeConstructor = second.typeConstructor()
if (!firstTypeConstructor.isDefinitelyClass() && !firstTypeConstructor.isTypeParameterTypeConstructor())
return@secondTypes false
if (!secondTypeConstructor.isDefinitelyClass() && !secondTypeConstructor.isTypeParameterTypeConstructor())
return@secondTypes false
if (!first.containsTypeParameter() && !second.containsTypeParameter()) {
return@secondTypes !isRelatedBySubtypingTo(this@ConstraintSystemCompletionContext, first, second)
}
if (constraintSystem == null) return false
val completerContext = constraintSystem.asConstraintSystemCompleterContext()
val substitutionMap = completerContext.allTypeVariables.entries.associate { (key, value) ->
val typeParameter = (key as TypeVariableTypeConstructorMarker).typeParameter
require(typeParameter != null) {
"Constraint system for checking type parameters for intersection emptiness" +
"should be build with type variables which refer to those type parameters"
}
typeParameter.getTypeConstructor() to value.defaultType()
}
val substitutor = typeSubstitutorByTypeConstructor(substitutionMap)
completerContext.getBuilder().addSubtypeConstraint(
substitutor.safeSubstitute(first), substitutor.safeSubstitute(second), TypeParametersIntersectionEmptyCheckingPosition
)
constraintSystem.hasContradiction
}
}
}
fun <A : PostponedResolvedAtomMarker> analyzeArgumentWithFixedParameterTypes(
languageVersionSettings: LanguageVersionSettings,
postponedArguments: List<A>,
@@ -44,7 +44,6 @@ class ResultTypeResolver(
return createCapturedStarProjectionForSelfType(typeVariableConstructor, typesForRecursiveTypeParameters)
}
@OptIn(ExperimentalStdlibApi::class)
private fun Context.getDefaultType(
direction: ResolveDirection,
constraints: List<Constraint>,
@@ -241,10 +240,13 @@ class ResultTypeResolver(
}
}
private fun Context.findSuperType(variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? {
val upperConstraints =
variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && this@findSuperType.isProperTypeForFixation(it.type) }
if (upperConstraints.isNotEmpty()) {
private fun Context.computeUpperType(upperConstraints: List<Constraint>): KotlinTypeMarker {
val isInferringIntoEmptyIntersectionEnabled =
languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)
// TODO: Remove this after stopping support of disabling `ForbidInferringTypeVariablesIntoEmptyIntersection`
// If `ForbidInferringTypeVariablesIntoEmptyIntersection` is enabled, we do the corresponding checks during resolution and completion
return if (!isInferringIntoEmptyIntersectionEnabled) {
val intersectionUpperType = intersectTypes(upperConstraints.map { it.type })
val resultIsActuallyIntersection = intersectionUpperType.typeConstructor().isIntersection()
@@ -268,12 +270,25 @@ class ResultTypeResolver(
val filteredUpperConstraints = upperConstraints.filterNot { it.isExpectedTypePosition() }.map { it.type }
if (filteredUpperConstraints.isNotEmpty()) intersectTypes(filteredUpperConstraints) else intersectionUpperType
} else intersectionUpperType
upperType
} else {
intersectTypes(upperConstraints.map { it.type })
}
}
private fun Context.findSuperType(variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? {
val upperConstraints =
variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && this@findSuperType.isProperTypeForFixation(it.type) }
if (upperConstraints.isNotEmpty()) {
val upperType = computeUpperType(upperConstraints)
return typeApproximator.approximateToSubType(
upperType,
TypeApproximatorConfiguration.InternalTypesApproximation
) ?: upperType
}
return null
}
@@ -13,6 +13,7 @@ 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.model.*
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.SmartSet
@@ -443,12 +444,14 @@ class NewConstraintSystemImpl(
val intersectionTypeConstructor = resultType.typeConstructor().takeIf { it is IntersectionTypeConstructorMarker } ?: return
val isInferredEmptyIntersectionForbidden =
languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)
val intersectionComponents = intersectionTypeConstructor.supertypes()
val upperTypes = intersectionTypeConstructor.supertypes()
if (intersectionComponents.isEmptyIntersection()) {
if (upperTypes.computeEmptyIntersectionTypeKind().isDefinitelyEmpty()) {
// Remove existing errors from resolution stage because a completion error is more precise
storage.errors.removeIf { it is InferredEmptyIntersectionError || it is InferredEmptyIntersectionWarning }
val errorFactory =
if (isInferredEmptyIntersectionForbidden) ::InferredEmptyIntersectionError else ::InferredEmptyIntersectionWarning
addError(errorFactory(intersectionComponents, variable))
addError(errorFactory(upperTypes, variable))
}
}