[FIR] Support determining emptiness of an intersection type with type parameters' types
This commit is contained in:
committed by
teamcity
parent
313da6f41e
commit
47d6236a2d
-46
@@ -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>,
|
||||
|
||||
+20
-5
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user