Move common inference classes to :compiler:resolution.common
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":core:compiler.common"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" {}
|
||||
}
|
||||
+449
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls
|
||||
|
||||
import org.jetbrains.kotlin.types.AbstractFlexibilityChecker.hasDifferentFlexibilityAtDepth
|
||||
import org.jetbrains.kotlin.types.AbstractNullabilityChecker
|
||||
import org.jetbrains.kotlin.types.AbstractNullabilityChecker.hasPathByNotMarkedNullableNodes
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
|
||||
object NewCommonSuperTypeCalculator {
|
||||
fun TypeSystemCommonSuperTypesContext.commonSuperType(types: List<KotlinTypeMarker>): KotlinTypeMarker {
|
||||
val maxDepth = types.maxOfOrNull { it.typeDepth() } ?: 0
|
||||
return commonSuperType(types, -maxDepth, true)
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.commonSuperType(
|
||||
types: List<KotlinTypeMarker>,
|
||||
depth: Int,
|
||||
isTopLevelType: Boolean = false
|
||||
): KotlinTypeMarker {
|
||||
if (types.isEmpty()) throw IllegalStateException("Empty collection for input")
|
||||
|
||||
types.singleOrNull()?.let { return it }
|
||||
|
||||
var thereIsFlexibleTypes = false
|
||||
|
||||
val lowers = types.map {
|
||||
when (it) {
|
||||
is SimpleTypeMarker -> {
|
||||
if (it.isCapturedDynamic()) return it
|
||||
|
||||
it
|
||||
}
|
||||
is FlexibleTypeMarker -> {
|
||||
if (it.isDynamic()) return it
|
||||
// raw types are allowed here and will be transformed to FlexibleTypes
|
||||
|
||||
thereIsFlexibleTypes = true
|
||||
it.lowerBound()
|
||||
}
|
||||
else -> error("sealed")
|
||||
}
|
||||
}
|
||||
|
||||
val contextStubTypesEqualToAnything = newBaseTypeCheckerContext(errorTypesEqualToAnything = false, stubTypesEqualToAnything = true)
|
||||
val contextStubTypesNotEqual = newBaseTypeCheckerContext(errorTypesEqualToAnything = false, stubTypesEqualToAnything = false)
|
||||
|
||||
val lowerSuperType = commonSuperTypeForSimpleTypes(lowers, depth, contextStubTypesEqualToAnything, contextStubTypesNotEqual)
|
||||
if (!thereIsFlexibleTypes) return lowerSuperType
|
||||
|
||||
val upperSuperType = commonSuperTypeForSimpleTypes(
|
||||
types.map { it.upperBoundIfFlexible() }, depth, contextStubTypesEqualToAnything, contextStubTypesNotEqual
|
||||
)
|
||||
|
||||
if (!isTopLevelType) {
|
||||
val nonStubTypes =
|
||||
types.filter { !isStubRelatedType(it.lowerBoundIfFlexible()) && !isStubRelatedType(it.upperBoundIfFlexible()) }
|
||||
val equalToEachOtherTypes = nonStubTypes.filter { potentialCommonSuperType ->
|
||||
nonStubTypes.all {
|
||||
AbstractTypeChecker.equalTypes(this, it, potentialCommonSuperType)
|
||||
}
|
||||
}
|
||||
|
||||
if (equalToEachOtherTypes.isNotEmpty()) {
|
||||
// TODO: merge flexibilities of type arguments instead of select the first suitable type
|
||||
return equalToEachOtherTypes.first()
|
||||
}
|
||||
}
|
||||
|
||||
return createFlexibleType(lowerSuperType, upperSuperType)
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.commonSuperTypeForSimpleTypes(
|
||||
types: List<SimpleTypeMarker>,
|
||||
depth: Int,
|
||||
contextStubTypesEqualToAnything: AbstractTypeCheckerContext,
|
||||
contextStubTypesNotEqual: AbstractTypeCheckerContext
|
||||
): SimpleTypeMarker {
|
||||
if (types.any { it.isError() }) {
|
||||
return createErrorType("CST(${types.joinToString()}")
|
||||
}
|
||||
|
||||
// i.e. result type also should be marked nullable
|
||||
val notAllNotNull =
|
||||
types.any { !isStubRelatedType(it) && !AbstractNullabilityChecker.isSubtypeOfAny(contextStubTypesEqualToAnything, it) }
|
||||
val notNullTypes = if (notAllNotNull) types.map { it.withNullability(false) } else types
|
||||
|
||||
val commonSuperType = commonSuperTypeForNotNullTypes(notNullTypes, depth, contextStubTypesEqualToAnything, contextStubTypesNotEqual)
|
||||
return if (notAllNotNull)
|
||||
refineNullabilityForUndefinedNullability(types, commonSuperType) ?: commonSuperType.withNullability(true)
|
||||
else
|
||||
commonSuperType
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.refineNullabilityForUndefinedNullability(
|
||||
types: List<SimpleTypeMarker>,
|
||||
commonSuperType: SimpleTypeMarker
|
||||
): SimpleTypeMarker? {
|
||||
if (!commonSuperType.canHaveUndefinedNullability()) return null
|
||||
|
||||
val actuallyNotNull =
|
||||
types.all { hasPathByNotMarkedNullableNodes(it, commonSuperType.typeConstructor()) }
|
||||
return if (actuallyNotNull) commonSuperType else null
|
||||
}
|
||||
|
||||
// Makes representative sample, i.e. (A, B, A) -> (A, B)
|
||||
private fun TypeSystemCommonSuperTypesContext.uniquify(
|
||||
types: List<SimpleTypeMarker>,
|
||||
contextStubTypesNotEqual: AbstractTypeCheckerContext
|
||||
): List<SimpleTypeMarker> {
|
||||
val uniqueTypes = arrayListOf<SimpleTypeMarker>()
|
||||
for (type in types) {
|
||||
val isNewUniqueType = uniqueTypes.all {
|
||||
val equalsModuloFlexibility = AbstractTypeChecker.equalTypes(contextStubTypesNotEqual, it, type) &&
|
||||
!it.typeConstructor().isIntegerLiteralTypeConstructor()
|
||||
|
||||
!equalsModuloFlexibility || hasDifferentFlexibilityAtDepth(listOf(it, type))
|
||||
}
|
||||
if (isNewUniqueType) {
|
||||
uniqueTypes += type
|
||||
}
|
||||
}
|
||||
return uniqueTypes
|
||||
}
|
||||
|
||||
// This function leaves only supertypes, i.e. A0 is a strong supertype for A iff A != A0 && A <: A0
|
||||
// Explanation: consider types (A : A0, B : B0, A0, B0), then CST(A, B, A0, B0) == CST(CST(A, A0), CST(B, B0)) == CST(A0, B0)
|
||||
private fun TypeSystemCommonSuperTypesContext.filterSupertypes(
|
||||
list: List<SimpleTypeMarker>,
|
||||
contextStubTypesNotEqual: AbstractTypeCheckerContext
|
||||
): List<SimpleTypeMarker> {
|
||||
val supertypes = list.toMutableList()
|
||||
val iterator = supertypes.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val potentialSubtype = iterator.next()
|
||||
val isSubtype = supertypes.any { supertype ->
|
||||
supertype !== potentialSubtype &&
|
||||
AbstractTypeChecker.isSubtypeOf(contextStubTypesNotEqual, potentialSubtype, supertype) &&
|
||||
!hasDifferentFlexibilityAtDepth(listOf(potentialSubtype, supertype))
|
||||
}
|
||||
|
||||
if (isSubtype) iterator.remove()
|
||||
}
|
||||
|
||||
return supertypes
|
||||
}
|
||||
|
||||
/*
|
||||
* Common Supertype calculator works with proper types and stub types (which is a replacement for non-proper types)
|
||||
* Also, there are two invariant related to stub types:
|
||||
* - resulting type should be only proper type
|
||||
* - one of the input types is definitely proper type
|
||||
* */
|
||||
private fun TypeSystemCommonSuperTypesContext.commonSuperTypeForNotNullTypes(
|
||||
types: List<SimpleTypeMarker>,
|
||||
depth: Int,
|
||||
contextStubTypesEqualToAnything: AbstractTypeCheckerContext,
|
||||
contextStubTypesNotEqual: AbstractTypeCheckerContext
|
||||
): SimpleTypeMarker {
|
||||
if (types.size == 1) return types.single()
|
||||
|
||||
val nonStubTypes = types.filter { !isStubRelatedType(it) }
|
||||
if (nonStubTypes.size == 1) return nonStubTypes.single()
|
||||
|
||||
assert(nonStubTypes.isNotEmpty()) {
|
||||
"There should be at least one non-stub type to compute common supertype but there are: $types"
|
||||
}
|
||||
|
||||
val uniqueTypes = uniquify(nonStubTypes, contextStubTypesNotEqual)
|
||||
if (uniqueTypes.size == 1) return uniqueTypes.single()
|
||||
|
||||
val explicitSupertypes = filterSupertypes(uniqueTypes, contextStubTypesNotEqual)
|
||||
if (explicitSupertypes.size == 1) return explicitSupertypes.single()
|
||||
findErrorTypeInSupertypes(explicitSupertypes, contextStubTypesEqualToAnything)?.let { return it }
|
||||
|
||||
findCommonIntegerLiteralTypesSuperType(explicitSupertypes)?.let { return it }
|
||||
|
||||
return findSuperTypeConstructorsAndIntersectResult(explicitSupertypes, depth, contextStubTypesEqualToAnything)
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.isStubRelatedType(type: SimpleTypeMarker): Boolean {
|
||||
return type.isStubType() || isCapturedStubType(type)
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.isCapturedStubType(type: SimpleTypeMarker): Boolean {
|
||||
val projectedType =
|
||||
type.asCapturedType()?.typeConstructor()?.projection()?.takeUnless { it.isStarProjection() }?.getType() ?: return false
|
||||
return projectedType.asSimpleType()?.isStubType() == true
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.findErrorTypeInSupertypes(
|
||||
types: List<SimpleTypeMarker>,
|
||||
contextStubTypesEqualToAnything: AbstractTypeCheckerContext
|
||||
): SimpleTypeMarker? {
|
||||
for (type in types) {
|
||||
collectAllSupertypes(type, contextStubTypesEqualToAnything).firstOrNull { it.isError() }?.let { return it.toErrorType() }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.findSuperTypeConstructorsAndIntersectResult(
|
||||
types: List<SimpleTypeMarker>,
|
||||
depth: Int,
|
||||
contextStubTypesEqualToAnything: AbstractTypeCheckerContext
|
||||
): SimpleTypeMarker =
|
||||
intersectTypes(
|
||||
allCommonSuperTypeConstructors(types, contextStubTypesEqualToAnything)
|
||||
.map { superTypeWithGivenConstructor(types, it, depth) }
|
||||
)
|
||||
|
||||
/**
|
||||
* Note that if there is captured type C, then no one else is not subtype of C => lowerType cannot help here
|
||||
*/
|
||||
private fun TypeSystemCommonSuperTypesContext.allCommonSuperTypeConstructors(
|
||||
types: List<SimpleTypeMarker>,
|
||||
contextStubTypesEqualToAnything: AbstractTypeCheckerContext
|
||||
): List<TypeConstructorMarker> {
|
||||
val result = collectAllSupertypes(types.first(), contextStubTypesEqualToAnything)
|
||||
// retain all super constructors of the first type that are present in the supertypes of all other types
|
||||
for (type in types) {
|
||||
if (type === types.first()) continue
|
||||
|
||||
result.retainAll(collectAllSupertypes(type, contextStubTypesEqualToAnything))
|
||||
}
|
||||
// remove all constructors that have subtype(s) with constructors from the resulting set - they are less precise
|
||||
return result.filterNot { target ->
|
||||
result.any { other ->
|
||||
other != target && other.supertypes().any { it.typeConstructor() == target }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.collectAllSupertypes(
|
||||
type: SimpleTypeMarker,
|
||||
contextStubTypesEqualToAnything: AbstractTypeCheckerContext
|
||||
) =
|
||||
LinkedHashSet<TypeConstructorMarker>().apply {
|
||||
contextStubTypesEqualToAnything.anySupertype(
|
||||
type,
|
||||
{ add(it.typeConstructor()); false },
|
||||
{ AbstractTypeCheckerContext.SupertypesPolicy.LowerIfFlexible }
|
||||
)
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.superTypeWithGivenConstructor(
|
||||
types: List<SimpleTypeMarker>,
|
||||
constructor: TypeConstructorMarker,
|
||||
depth: Int
|
||||
): SimpleTypeMarker {
|
||||
if (constructor.parametersCount() == 0) return createSimpleType(
|
||||
constructor,
|
||||
emptyList(),
|
||||
nullable = false
|
||||
)
|
||||
|
||||
val typeCheckerContext = newBaseTypeCheckerContext(errorTypesEqualToAnything = false, stubTypesEqualToAnything = true)
|
||||
|
||||
/**
|
||||
* Sometimes one type can have several supertypes with given type constructor, suppose A <: List<Int> and A <: List<Double>.
|
||||
* Also suppose that B <: List<String>.
|
||||
* Note that common supertype for A and B is CS(List<Int>, List<String>) & CS(List<Double>, List<String>),
|
||||
* but it is too complicated and we will return not so accurate type: CS(List<Int>, List<Double>, List<String>)
|
||||
*/
|
||||
val correspondingSuperTypes = types.flatMap {
|
||||
with(AbstractTypeChecker) {
|
||||
typeCheckerContext.findCorrespondingSupertypes(it, constructor)
|
||||
}
|
||||
}
|
||||
|
||||
val arguments = ArrayList<TypeArgumentMarker>(constructor.parametersCount())
|
||||
for (index in 0 until constructor.parametersCount()) {
|
||||
val parameter = constructor.getParameter(index)
|
||||
var thereIsStar = false
|
||||
val typeProjections = correspondingSuperTypes.mapNotNull {
|
||||
val typeArgumentFromSupertype = it.getArgumentOrNull(index) ?: return@mapNotNull null
|
||||
|
||||
// We have to uncapture types with status FOR_SUBTYPING because such captured types are creating during
|
||||
// `findCorrespondingSupertypes` call. Normally, we shouldn't create intermediate captured types here, it's needed only
|
||||
// to check subtyping. It'll be fixed but for a while we do this uncapturing here
|
||||
val typeArgument = uncaptureFromSubtyping(typeArgumentFromSupertype)
|
||||
|
||||
when {
|
||||
typeArgument.isStarProjection() -> {
|
||||
thereIsStar = true
|
||||
null
|
||||
}
|
||||
|
||||
typeArgument.getType().lowerBoundIfFlexible().isStubType() -> null
|
||||
|
||||
else -> typeArgument
|
||||
}
|
||||
}
|
||||
|
||||
// This is used for folding recursive types like Inv<Inv<*>> into Inv<*>
|
||||
fun collapseRecursiveArgumentIfPossible(argument: TypeArgumentMarker): TypeArgumentMarker {
|
||||
if (argument.isStarProjection()) return argument
|
||||
val argumentType = argument.getType().asSimpleType()
|
||||
val argumentConstructor = argumentType?.typeConstructor()
|
||||
return if (argument.getVariance() == TypeVariance.OUT && argumentConstructor == constructor && argumentType.asArgumentList()[index].isStarProjection()) {
|
||||
createStarProjection(parameter)
|
||||
} else {
|
||||
argument
|
||||
}
|
||||
}
|
||||
|
||||
val argument =
|
||||
if (thereIsStar || typeProjections.isEmpty() || checkRecursion(types, typeProjections, parameter)) {
|
||||
createStarProjection(parameter)
|
||||
} else {
|
||||
collapseRecursiveArgumentIfPossible(calculateArgument(parameter, typeProjections, depth))
|
||||
}
|
||||
|
||||
arguments.add(argument)
|
||||
}
|
||||
return createSimpleType(constructor, arguments, nullable = false, isExtensionFunction = types.all { it.isExtensionFunction() })
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.uncaptureFromSubtyping(typeArgument: TypeArgumentMarker): TypeArgumentMarker {
|
||||
val capturedType = typeArgument.getType().asSimpleType()?.asCapturedType() ?: return typeArgument
|
||||
if (capturedType.captureStatus() != CaptureStatus.FOR_SUBTYPING) return typeArgument
|
||||
|
||||
return capturedType.typeConstructor().projection()
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.checkRecursion(
|
||||
originalTypesForCst: List<SimpleTypeMarker>,
|
||||
typeArgumentsForSuperConstructorParameter: List<TypeArgumentMarker>,
|
||||
parameter: TypeParameterMarker,
|
||||
): Boolean {
|
||||
if (parameter.getVariance() == TypeVariance.IN)
|
||||
return false // arguments for contravariant parameters are intersected, recursion should not be possible
|
||||
|
||||
val originalTypesSet = originalTypesForCst.toSet()
|
||||
val typeArgumentsTypeSet = typeArgumentsForSuperConstructorParameter.map { it.getType().lowerBoundIfFlexible() }.toSet()
|
||||
|
||||
if (originalTypesSet.size != typeArgumentsTypeSet.size)
|
||||
return false
|
||||
|
||||
// only needed in case of captured star projections in argument types
|
||||
val originalTypeConstructorSet by lazy { typeConstructorsWithExpandedStarProjections(originalTypesSet).toSet() }
|
||||
|
||||
for (argumentType in typeArgumentsTypeSet) {
|
||||
if (argumentType in originalTypesSet) continue
|
||||
|
||||
var starProjectionFound = false
|
||||
for (supertype in supertypesIfCapturedStarProjection(argumentType).orEmpty()) {
|
||||
if (supertype.lowerBoundIfFlexible().typeConstructor() !in originalTypeConstructorSet)
|
||||
return false
|
||||
else starProjectionFound = true
|
||||
}
|
||||
|
||||
if (!starProjectionFound)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.typeConstructorsWithExpandedStarProjections(types: Set<SimpleTypeMarker>) = sequence {
|
||||
for (type in types) {
|
||||
if (isCapturedStarProjection(type)) {
|
||||
for (supertype in supertypesIfCapturedStarProjection(type).orEmpty()) {
|
||||
yield(supertype.lowerBoundIfFlexible().typeConstructor())
|
||||
}
|
||||
} else {
|
||||
yield(type.typeConstructor())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.isCapturedStarProjection(type: SimpleTypeMarker): Boolean =
|
||||
type.asCapturedType()?.typeConstructor()?.projection()?.isStarProjection() == true
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.supertypesIfCapturedStarProjection(type: SimpleTypeMarker): Collection<KotlinTypeMarker>? {
|
||||
val constructor = type.asCapturedType()?.typeConstructor() ?: return null
|
||||
return if (constructor.projection().isStarProjection())
|
||||
constructor.supertypes()
|
||||
else null
|
||||
}
|
||||
|
||||
// no star projections in arguments
|
||||
private fun TypeSystemCommonSuperTypesContext.calculateArgument(
|
||||
parameter: TypeParameterMarker,
|
||||
arguments: List<TypeArgumentMarker>,
|
||||
depth: Int
|
||||
): TypeArgumentMarker {
|
||||
if (depth > 0) {
|
||||
return createStarProjection(parameter)
|
||||
}
|
||||
|
||||
// Inv<A>, Inv<A> = Inv<A>
|
||||
if (parameter.getVariance() == TypeVariance.INV && arguments.all { it.getVariance() == TypeVariance.INV }) {
|
||||
val first = arguments.first()
|
||||
if (arguments.all { it.getType() == first.getType() }) return first
|
||||
}
|
||||
|
||||
val asOut: Boolean
|
||||
if (parameter.getVariance() != TypeVariance.INV) {
|
||||
asOut = parameter.getVariance() == TypeVariance.OUT
|
||||
} else {
|
||||
val thereIsOut = arguments.any { it.getVariance() == TypeVariance.OUT }
|
||||
val thereIsIn = arguments.any { it.getVariance() == TypeVariance.IN }
|
||||
if (thereIsOut) {
|
||||
if (thereIsIn) {
|
||||
// CS(Inv<out X>, Inv<in Y>) = Inv<*>
|
||||
return createStarProjection(parameter)
|
||||
} else {
|
||||
asOut = true
|
||||
}
|
||||
} else {
|
||||
asOut = !thereIsIn
|
||||
}
|
||||
}
|
||||
|
||||
// CS(Out<X>, Out<Y>) = Out<CS(X, Y)>
|
||||
// CS(In<X>, In<Y>) = In<X & Y>
|
||||
// CS(Inv<X>, Inv<Y>) = Inv<out CS(X, Y)>)
|
||||
if (asOut) {
|
||||
val argumentTypes = arguments.map { it.getType() }
|
||||
val parameterIsNotInv = parameter.getVariance() != TypeVariance.INV
|
||||
|
||||
if (parameterIsNotInv) {
|
||||
return commonSuperType(argumentTypes, depth + 1).asTypeArgument()
|
||||
}
|
||||
|
||||
val equalToEachOtherType = arguments.firstOrNull { potentialSuperType ->
|
||||
arguments.all { AbstractTypeChecker.equalTypes(this, it.getType(), potentialSuperType.getType()) }
|
||||
}
|
||||
|
||||
return if (equalToEachOtherType == null) {
|
||||
createTypeArgument(commonSuperType(argumentTypes, depth + 1), TypeVariance.OUT)
|
||||
} else {
|
||||
val thereIsNotInv = arguments.any { it.getVariance() != TypeVariance.INV }
|
||||
createTypeArgument(equalToEachOtherType.getType(), if (thereIsNotInv) TypeVariance.OUT else TypeVariance.INV)
|
||||
}
|
||||
} else {
|
||||
val type = intersectTypes(arguments.map { it.getType() })
|
||||
|
||||
return if (parameter.getVariance() != TypeVariance.INV) type.asTypeArgument() else createTypeArgument(
|
||||
type,
|
||||
TypeVariance.IN
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
|
||||
interface PostponedArgumentsAnalyzerContext : TypeSystemInferenceExtensionContext {
|
||||
fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>): TypeSubstitutorMarker
|
||||
fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker
|
||||
fun bindingStubsForPostponedVariables(): Map<TypeVariableMarker, StubTypeMarker>
|
||||
|
||||
// type can be proper if it not contains not fixed type variables
|
||||
fun canBeProper(type: KotlinTypeMarker): Boolean
|
||||
|
||||
fun hasUpperOrEqualUnitConstraint(type: KotlinTypeMarker): Boolean
|
||||
|
||||
// mutable operations
|
||||
fun addOtherSystem(otherSystem: ConstraintStorage)
|
||||
|
||||
fun getBuilder(): ConstraintSystemBuilder
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeVariableMarker
|
||||
|
||||
interface ConstraintSystemOperation {
|
||||
val hasContradiction: Boolean
|
||||
fun registerVariable(variable: TypeVariableMarker)
|
||||
fun markPostponedVariable(variable: TypeVariableMarker)
|
||||
fun unmarkPostponedVariable(variable: TypeVariableMarker)
|
||||
fun removePostponedVariables()
|
||||
|
||||
fun addSubtypeConstraint(lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, position: ConstraintPosition)
|
||||
fun addEqualityConstraint(a: KotlinTypeMarker, b: KotlinTypeMarker, position: ConstraintPosition)
|
||||
|
||||
fun isProperType(type: KotlinTypeMarker): Boolean
|
||||
fun isTypeVariable(type: KotlinTypeMarker): Boolean
|
||||
fun isPostponedTypeVariable(typeVariable: TypeVariableMarker): Boolean
|
||||
|
||||
fun getProperSuperTypeConstructors(type: KotlinTypeMarker): List<TypeConstructorMarker>
|
||||
|
||||
fun addOtherSystem(otherSystem: ConstraintStorage)
|
||||
}
|
||||
|
||||
interface ConstraintSystemBuilder : ConstraintSystemOperation {
|
||||
// if runOperations return true, then this operation will be applied, and function return true
|
||||
fun runTransaction(runOperations: ConstraintSystemOperation.() -> Boolean): Boolean
|
||||
|
||||
fun buildCurrentSubstitutor(): TypeSubstitutorMarker
|
||||
|
||||
fun currentStorage(): ConstraintStorage
|
||||
}
|
||||
|
||||
fun ConstraintSystemBuilder.addSubtypeConstraintIfCompatible(
|
||||
lowerType: KotlinTypeMarker,
|
||||
upperType: KotlinTypeMarker,
|
||||
position: ConstraintPosition
|
||||
): Boolean = addConstraintIfCompatible(lowerType, upperType, position, ConstraintKind.LOWER)
|
||||
|
||||
fun ConstraintSystemBuilder.addEqualityConstraintIfCompatible(
|
||||
lowerType: KotlinTypeMarker,
|
||||
upperType: KotlinTypeMarker,
|
||||
position: ConstraintPosition
|
||||
): Boolean = addConstraintIfCompatible(lowerType, upperType, position, ConstraintKind.EQUALITY)
|
||||
|
||||
private fun ConstraintSystemBuilder.addConstraintIfCompatible(
|
||||
lowerType: KotlinTypeMarker,
|
||||
upperType: KotlinTypeMarker,
|
||||
position: ConstraintPosition,
|
||||
kind: ConstraintKind
|
||||
): Boolean = runTransaction {
|
||||
if (!hasContradiction) {
|
||||
when (kind) {
|
||||
ConstraintKind.LOWER -> addSubtypeConstraint(lowerType, upperType, position)
|
||||
ConstraintKind.UPPER -> addSubtypeConstraint(upperType, lowerType, position)
|
||||
ConstraintKind.EQUALITY -> addEqualityConstraint(lowerType, upperType, position)
|
||||
}
|
||||
}
|
||||
!hasContradiction
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
|
||||
fun ConstraintStorage.buildCurrentSubstitutor(
|
||||
context: TypeSystemInferenceExtensionContext,
|
||||
additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>
|
||||
): TypeSubstitutorMarker {
|
||||
return context.typeSubstitutorByTypeConstructor(fixedTypeVariables.entries.associate { it.key to it.value } + additionalBindings)
|
||||
}
|
||||
|
||||
fun ConstraintStorage.buildAbstractResultingSubstitutor(
|
||||
context: TypeSystemInferenceExtensionContext,
|
||||
transformTypeVariablesToErrorTypes: Boolean = true
|
||||
): TypeSubstitutorMarker = with(context) {
|
||||
if (allTypeVariables.isEmpty()) return createEmptySubstitutor()
|
||||
|
||||
val currentSubstitutorMap = fixedTypeVariables.entries.associate {
|
||||
it.key to it.value
|
||||
}
|
||||
val uninferredSubstitutorMap = if (transformTypeVariablesToErrorTypes) {
|
||||
notFixedTypeVariables.entries.associate { (freshTypeConstructor, typeVariable) ->
|
||||
freshTypeConstructor to context.createErrorTypeWithCustomConstructor(
|
||||
"Uninferred type",
|
||||
(typeVariable.typeVariable).freshTypeConstructor()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
notFixedTypeVariables.entries.associate { (freshTypeConstructor, typeVariable) ->
|
||||
freshTypeConstructor to typeVariable.typeVariable.defaultType(this)
|
||||
}
|
||||
}
|
||||
return context.typeSubstitutorByTypeConstructor(currentSubstitutorMap + uninferredSubstitutorMap)
|
||||
}
|
||||
|
||||
fun ConstraintStorage.buildNotFixedVariablesToNonSubtypableTypesSubstitutor(
|
||||
context: TypeSystemInferenceExtensionContext
|
||||
): TypeSubstitutorMarker {
|
||||
return context.typeSubstitutorByTypeConstructor(
|
||||
notFixedTypeVariables.mapValues { context.createStubType(it.value.typeVariable) }
|
||||
)
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
|
||||
|
||||
interface NewConstraintSystem {
|
||||
val hasContradiction: Boolean
|
||||
val errors: List<ConstraintSystemError>
|
||||
|
||||
fun getBuilder(): ConstraintSystemBuilder
|
||||
|
||||
// after this method we shouldn't mutate system via ConstraintSystemBuilder
|
||||
fun asReadOnlyStorage(): ConstraintStorage
|
||||
|
||||
fun asConstraintSystemCompleterContext(): ConstraintSystemCompletionContext
|
||||
fun asPostponedArgumentsAnalyzerContext(): PostponedArgumentsAnalyzerContext
|
||||
}
|
||||
+362
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.types.AbstractNullabilityChecker
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
|
||||
abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheckerContext(), TypeSystemInferenceExtensionContext {
|
||||
|
||||
override val KotlinTypeMarker.isAllowedTypeVariable: Boolean
|
||||
get() = false
|
||||
|
||||
override val isErrorTypeEqualsToAnything: Boolean
|
||||
get() = true
|
||||
|
||||
override val isStubTypeEqualsToAnything: Boolean
|
||||
get() = true
|
||||
|
||||
abstract fun isMyTypeVariable(type: SimpleTypeMarker): Boolean
|
||||
|
||||
// super and sub type isSingleClassifierType
|
||||
abstract fun addUpperConstraint(typeVariable: TypeConstructorMarker, superType: KotlinTypeMarker)
|
||||
|
||||
abstract fun addLowerConstraint(
|
||||
typeVariable: TypeConstructorMarker,
|
||||
subType: KotlinTypeMarker,
|
||||
isFromNullabilityConstraint: Boolean = false
|
||||
)
|
||||
|
||||
override fun getLowerCapturedTypePolicy(subType: SimpleTypeMarker, superType: CapturedTypeMarker): LowerCapturedTypePolicy {
|
||||
return when {
|
||||
isMyTypeVariable(subType) -> {
|
||||
val projection = superType.typeConstructorProjection()
|
||||
val type = projection.getType().asSimpleType()
|
||||
if (projection.getVariance() == TypeVariance.IN && type != null && isMyTypeVariable(type)) {
|
||||
LowerCapturedTypePolicy.CHECK_ONLY_LOWER
|
||||
} else {
|
||||
LowerCapturedTypePolicy.SKIP_LOWER
|
||||
}
|
||||
}
|
||||
subType.contains { it.anyBound(this::isMyTypeVariable) } -> LowerCapturedTypePolicy.CHECK_ONLY_LOWER
|
||||
else -> LowerCapturedTypePolicy.CHECK_SUBTYPE_AND_LOWER
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* todo: possible we should override this method, because otherwise OR in subtyping transformed to AND in constraint system
|
||||
* Now we cannot do this, because sometimes we have proper intersection type as lower type and if we first supertype,
|
||||
* then we can get wrong result.
|
||||
* override val sameConstructorPolicy get() = SeveralSupertypesWithSameConstructorPolicy.TAKE_FIRST_FOR_SUBTYPING
|
||||
*/
|
||||
final override fun addSubtypeConstraint(
|
||||
subType: KotlinTypeMarker,
|
||||
superType: KotlinTypeMarker,
|
||||
isFromNullabilityConstraint: Boolean
|
||||
): Boolean? {
|
||||
val hasNoInfer = subType.isTypeVariableWithNoInfer() || superType.isTypeVariableWithNoInfer()
|
||||
if (hasNoInfer) return true
|
||||
|
||||
val hasExact = subType.isTypeVariableWithExact() || superType.isTypeVariableWithExact()
|
||||
|
||||
// we should strip annotation's because we have incorporation operation and they should be not affected
|
||||
val mySubType =
|
||||
if (hasExact) extractTypeForProjectedType(subType, out = true) ?: subType.removeExactAnnotation() else subType
|
||||
val mySuperType =
|
||||
if (hasExact) extractTypeForProjectedType(superType, out = false) ?: superType.removeExactAnnotation() else superType
|
||||
|
||||
val result = internalAddSubtypeConstraint(mySubType, mySuperType, isFromNullabilityConstraint)
|
||||
if (!hasExact) return result
|
||||
|
||||
val result2 = internalAddSubtypeConstraint(mySuperType, mySubType, isFromNullabilityConstraint)
|
||||
|
||||
if (result == null && result2 == null) return null
|
||||
return (result ?: true) && (result2 ?: true)
|
||||
}
|
||||
|
||||
private fun extractTypeForProjectedType(type: KotlinTypeMarker, out: Boolean): KotlinTypeMarker? {
|
||||
val typeMarker = type.asSimpleType()?.asCapturedType() ?: return null
|
||||
|
||||
val projection = typeMarker.typeConstructorProjection()
|
||||
if (projection.isStarProjection()) return null
|
||||
|
||||
|
||||
return when (projection.getVariance()) {
|
||||
TypeVariance.IN -> if (!out) typeMarker.lowerType() ?: projection.getType() else null
|
||||
TypeVariance.OUT -> if (out) projection.getType() else null
|
||||
TypeVariance.INV -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinTypeMarker.isTypeVariableWithExact() =
|
||||
hasExactAnnotation() && anyBound(this@AbstractTypeCheckerContextForConstraintSystem::isMyTypeVariable)
|
||||
|
||||
private fun KotlinTypeMarker.isTypeVariableWithNoInfer() =
|
||||
hasNoInferAnnotation() && anyBound(this@AbstractTypeCheckerContextForConstraintSystem::isMyTypeVariable)
|
||||
|
||||
private fun internalAddSubtypeConstraint(
|
||||
subType: KotlinTypeMarker,
|
||||
superType: KotlinTypeMarker,
|
||||
isFromNullabilityConstraint: Boolean
|
||||
): Boolean? {
|
||||
assertInputTypes(subType, superType)
|
||||
|
||||
var answer: Boolean? = null
|
||||
|
||||
if (superType.anyBound(this::isMyTypeVariable)) {
|
||||
answer = simplifyLowerConstraint(superType, subType, isFromNullabilityConstraint)
|
||||
}
|
||||
|
||||
if (subType.anyBound(this::isMyTypeVariable)) {
|
||||
return simplifyUpperConstraint(subType, superType) && (answer ?: true)
|
||||
} else {
|
||||
extractTypeVariableForSubtype(subType, superType)?.let {
|
||||
return simplifyUpperConstraint(it, superType) && (answer ?: true)
|
||||
}
|
||||
|
||||
return simplifyConstraintForPossibleIntersectionSubType(subType, superType) ?: answer
|
||||
}
|
||||
}
|
||||
|
||||
// extract type variable only from type like Captured(out T)
|
||||
private fun extractTypeVariableForSubtype(subType: KotlinTypeMarker, superType: KotlinTypeMarker): KotlinTypeMarker? {
|
||||
|
||||
val typeMarker = subType.asSimpleType()?.asCapturedType() ?: return null
|
||||
|
||||
val projection = typeMarker.typeConstructorProjection()
|
||||
if (projection.isStarProjection()) return null
|
||||
if (projection.getVariance() == TypeVariance.IN) {
|
||||
val type = projection.getType().asSimpleType() ?: return null
|
||||
if (isMyTypeVariable(type)) {
|
||||
simplifyLowerConstraint(type, superType)
|
||||
if (isMyTypeVariable(superType.asSimpleType() ?: return null)) {
|
||||
addLowerConstraint(superType.typeConstructor(), nullableAnyType())
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
return if (projection.getVariance() == TypeVariance.OUT) {
|
||||
val type = projection.getType()
|
||||
when {
|
||||
type is SimpleTypeMarker && isMyTypeVariable(type) -> type.asSimpleType()
|
||||
type is FlexibleTypeMarker && isMyTypeVariable(type.lowerBound()) -> type.asFlexibleType()?.lowerBound()
|
||||
else -> null
|
||||
}
|
||||
} else null
|
||||
}
|
||||
|
||||
/**
|
||||
* Foo <: T -- leave as is
|
||||
*
|
||||
* T?
|
||||
*
|
||||
* Foo <: T? -- Foo & Any <: T -- Foo!! <: T
|
||||
* Foo? <: T? -- Foo? & Any <: T -- Foo!! <: T
|
||||
* (Foo..Bar) <: T? -- (Foo..Bar) & Any <: T -- (Foo..Bar)!! <: T
|
||||
*
|
||||
* T!
|
||||
*
|
||||
* Foo <: T! --
|
||||
* assert T! == (T..T?)
|
||||
* Foo <: T?
|
||||
* Foo <: T (optional constraint, needs to preserve nullability)
|
||||
* =>
|
||||
* Foo & Any <: T
|
||||
* Foo <: T
|
||||
* =>
|
||||
* (Foo & Any .. Foo) <: T -- (Foo!! .. Foo) <: T
|
||||
*
|
||||
* => Foo <: T! -- (Foo!! .. Foo) <: T
|
||||
*
|
||||
* Foo? <: T! -- Foo? <: T
|
||||
*
|
||||
*
|
||||
* (Foo..Bar) <: T! --
|
||||
* assert T! == (T..T?)
|
||||
* (Foo..Bar) <: (T..T?)
|
||||
* =>
|
||||
* Foo <: T?
|
||||
* Bar <: T (optional constraint, needs to preserve nullability)
|
||||
* =>
|
||||
* (Foo & Any .. Bar) <: T -- (Foo!! .. Bar) <: T
|
||||
*
|
||||
* => (Foo..Bar) <: T! -- (Foo!! .. Bar) <: T
|
||||
*/
|
||||
private fun simplifyLowerConstraint(
|
||||
typeVariable: KotlinTypeMarker,
|
||||
subType: KotlinTypeMarker,
|
||||
isFromNullabilityConstraint: Boolean = false
|
||||
): Boolean {
|
||||
val lowerConstraint = when (typeVariable) {
|
||||
is SimpleTypeMarker ->
|
||||
/*
|
||||
* Foo <: T -- Foo <: T
|
||||
* Foo <: T? (T is contained in invariant or contravariant positions of a return type) -- Foo <: T
|
||||
* Example:
|
||||
* fun <T> foo(x: T?): Inv<T> {}
|
||||
* fun <K> main(z: K) { val x = foo(z) }
|
||||
* Foo <: T? (T isn't contained there) -- Foo!! <: T
|
||||
* Example:
|
||||
* fun <T> foo(x: T?) {}
|
||||
* fun <K> main(z: K) { foo(z) }
|
||||
*/
|
||||
if (typeVariable.isMarkedNullable()) {
|
||||
val typeVariableTypeConstructor = typeVariable.typeConstructor()
|
||||
val subTypeConstructor = subType.typeConstructor()
|
||||
|
||||
if (
|
||||
!subTypeConstructor.isTypeVariable() &&
|
||||
typeVariableTypeConstructor.isTypeVariable() &&
|
||||
(typeVariableTypeConstructor as TypeVariableTypeConstructorMarker).isContainedInInvariantOrContravariantPositions()
|
||||
) {
|
||||
if (subType.isCapturedType()) {
|
||||
(subType as CapturedTypeMarker).withNotNullProjection()
|
||||
} else {
|
||||
subType.withNullability(false)
|
||||
}
|
||||
} else {
|
||||
subType.makeDefinitelyNotNullOrNotNull()
|
||||
}
|
||||
} else subType
|
||||
|
||||
is FlexibleTypeMarker -> {
|
||||
assertFlexibleTypeVariable(typeVariable)
|
||||
|
||||
when (subType) {
|
||||
is SimpleTypeMarker ->
|
||||
// Foo <: T! -- (Foo!! .. Foo) <: T
|
||||
if (subType.isMarkedNullable()) {
|
||||
subType // prefer nullable type to flexible one: `Foo? <: (T..T?)` => lowerConstraint = `Foo?`
|
||||
} else {
|
||||
createFlexibleType(subType, subType.withNullability(true))
|
||||
}
|
||||
|
||||
is FlexibleTypeMarker ->
|
||||
// (Foo..Bar) <: T! -- (Foo!! .. Bar) <: T
|
||||
createFlexibleType(subType.lowerBound().makeSimpleTypeDefinitelyNotNullOrNotNull(), subType.upperBound())
|
||||
|
||||
else -> error("sealed")
|
||||
}
|
||||
}
|
||||
else -> error("sealed")
|
||||
}
|
||||
|
||||
addLowerConstraint(typeVariable.typeConstructor(), lowerConstraint, isFromNullabilityConstraint)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun assertFlexibleTypeVariable(typeVariable: FlexibleTypeMarker) {
|
||||
assert(typeVariable.lowerBound().typeConstructor() == typeVariable.upperBound().typeConstructor()) {
|
||||
"Flexible type variable ($typeVariable) should have bounds with the same type constructor, i.e. (T..T?)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* T! <: Foo <=> T <: Foo..Foo?
|
||||
* T? <: Foo <=> T <: Foo && Nothing? <: Foo
|
||||
* T <: Foo -- leave as is
|
||||
*/
|
||||
private fun simplifyUpperConstraint(typeVariable: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean {
|
||||
val typeVariableLowerBound = typeVariable.lowerBoundIfFlexible()
|
||||
val simplifiedSuperType = if (typeVariableLowerBound.isDefinitelyNotNullType()) {
|
||||
superType.withNullability(true)
|
||||
} else if (typeVariable.isFlexible() && superType is SimpleTypeMarker) {
|
||||
createFlexibleType(superType, superType.withNullability(true))
|
||||
} else superType
|
||||
|
||||
addUpperConstraint(typeVariableLowerBound.typeConstructor(), simplifiedSuperType)
|
||||
|
||||
if (typeVariableLowerBound.isMarkedNullable()) {
|
||||
// here is important that superType is singleClassifierType
|
||||
return simplifiedSuperType.anyBound(this::isMyTypeVariable) ||
|
||||
isSubtypeOfByTypeChecker(nullableNothingType(), simplifiedSuperType)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun simplifyConstraintForPossibleIntersectionSubType(subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean? {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val subType = subType.lowerBoundIfFlexible()
|
||||
|
||||
if (!subType.typeConstructor().isIntersection()) return null
|
||||
|
||||
assert(!subType.isMarkedNullable()) { "Intersection type should not be marked nullable!: $subType" }
|
||||
|
||||
// TODO: may be we lose flexibility here
|
||||
val subIntersectionTypes = (subType.typeConstructor().supertypes()).map { it.lowerBoundIfFlexible() }
|
||||
|
||||
val typeVariables = subIntersectionTypes.filter(this::isMyTypeVariable).takeIf { it.isNotEmpty() } ?: return null
|
||||
val notTypeVariables = subIntersectionTypes.filterNot(this::isMyTypeVariable)
|
||||
|
||||
// todo: may be we can do better then that.
|
||||
if (notTypeVariables.isNotEmpty() &&
|
||||
AbstractTypeChecker.isSubtypeOf(
|
||||
this as TypeCheckerProviderContext,
|
||||
intersectTypes(notTypeVariables),
|
||||
superType
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Consider the following example:
|
||||
// fun <T> id(x: T): T = x
|
||||
// fun <S> id2(x: S?, y: S): S = y
|
||||
//
|
||||
// fun checkLeftAssoc(a: Int?) : Int {
|
||||
// return id2(id(a), 3)
|
||||
// }
|
||||
//
|
||||
// fun box() : String {
|
||||
// return "OK"
|
||||
// }
|
||||
//
|
||||
// here we try to add constraint {Any & T} <: S from `id(a)`
|
||||
// Previously we thought that if `Any` isn't a subtype of S => T <: S, which is wrong, now we use weaker upper constraint
|
||||
// TODO: rethink, maybe we should take nullability into account somewhere else
|
||||
if (notTypeVariables.any { AbstractNullabilityChecker.isSubtypeOfAny(this as TypeCheckerProviderContext, it) }) {
|
||||
return typeVariables.all { simplifyUpperConstraint(it, superType.withNullability(true)) }
|
||||
}
|
||||
|
||||
return typeVariables.all { simplifyUpperConstraint(it, superType) }
|
||||
}
|
||||
|
||||
private fun isSubtypeOfByTypeChecker(subType: KotlinTypeMarker, superType: KotlinTypeMarker) =
|
||||
AbstractTypeChecker.isSubtypeOf(this as AbstractTypeCheckerContext, subType, superType)
|
||||
|
||||
private fun assertInputTypes(subType: KotlinTypeMarker, superType: KotlinTypeMarker) {
|
||||
if (!AbstractTypeChecker.RUN_SLOW_ASSERTIONS) return
|
||||
fun correctSubType(subType: SimpleTypeMarker) =
|
||||
subType.isSingleClassifierType() || subType.typeConstructor().isIntersection() || isMyTypeVariable(subType) || subType.isError() || subType.isIntegerLiteralType()
|
||||
|
||||
fun correctSuperType(superType: SimpleTypeMarker) =
|
||||
superType.isSingleClassifierType() || superType.typeConstructor().isIntersection() || isMyTypeVariable(superType) || superType.isError() || superType.isIntegerLiteralType()
|
||||
|
||||
assert(subType.bothBounds(::correctSubType)) {
|
||||
"Not singleClassifierType and not intersection subType: $subType"
|
||||
}
|
||||
assert(superType.bothBounds(::correctSuperType)) {
|
||||
"Not singleClassifierType superType: $superType"
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun KotlinTypeMarker.bothBounds(f: (SimpleTypeMarker) -> Boolean) = when (this) {
|
||||
is SimpleTypeMarker -> f(this)
|
||||
is FlexibleTypeMarker -> f(lowerBound()) && f(upperBound())
|
||||
else -> error("sealed")
|
||||
}
|
||||
|
||||
private inline fun KotlinTypeMarker.anyBound(f: (SimpleTypeMarker) -> Boolean) = when (this) {
|
||||
is SimpleTypeMarker -> f(this)
|
||||
is FlexibleTypeMarker -> f(lowerBound()) || f(upperBound())
|
||||
else -> error("sealed")
|
||||
}
|
||||
}
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.*
|
||||
import org.jetbrains.kotlin.types.AbstractTypeApproximator
|
||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.util.*
|
||||
|
||||
// todo problem: intersection types in constrains: A <: Number, B <: Inv<A & Any> =>? B <: Inv<out Number & Any>
|
||||
class ConstraintIncorporator(
|
||||
val typeApproximator: AbstractTypeApproximator,
|
||||
val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle,
|
||||
val utilContext: ConstraintSystemUtilContext
|
||||
) {
|
||||
|
||||
interface Context : TypeSystemInferenceExtensionContext {
|
||||
val allTypeVariablesWithConstraints: Collection<VariableWithConstraints>
|
||||
|
||||
// if such type variable is fixed then it is error
|
||||
fun getTypeVariable(typeConstructor: TypeConstructorMarker): TypeVariableMarker?
|
||||
|
||||
fun getConstraintsForVariable(typeVariable: TypeVariableMarker): Collection<Constraint>
|
||||
|
||||
fun addNewIncorporatedConstraint(
|
||||
lowerType: KotlinTypeMarker,
|
||||
upperType: KotlinTypeMarker,
|
||||
shouldTryUseDifferentFlexibilityForUpperType: Boolean,
|
||||
isFromNullabilityConstraint: Boolean = false,
|
||||
isFromDeclaredUpperBound: Boolean = false
|
||||
)
|
||||
|
||||
fun addNewIncorporatedConstraint(typeVariable: TypeVariableMarker, type: KotlinTypeMarker, constraintContext: ConstraintContext)
|
||||
}
|
||||
|
||||
fun incorporateIntoOtherConstraints(c: Context, typeVariable: TypeVariableMarker, constraint: Constraint) {
|
||||
// we shouldn't incorporate recursive constraint -- It is too dangerous
|
||||
if (c.areThereRecursiveConstraints(typeVariable, constraint)) return
|
||||
|
||||
c.insideOtherConstraint(typeVariable, constraint)
|
||||
}
|
||||
|
||||
// \alpha is typeVariable, \beta -- other type variable registered in ConstraintStorage
|
||||
fun incorporate(c: Context, typeVariable: TypeVariableMarker, constraint: Constraint) {
|
||||
// we shouldn't incorporate recursive constraint -- It is too dangerous
|
||||
if (c.areThereRecursiveConstraints(typeVariable, constraint)) return
|
||||
|
||||
c.directWithVariable(typeVariable, constraint)
|
||||
c.otherInsideMyConstraint(typeVariable, constraint)
|
||||
c.insideOtherConstraint(typeVariable, constraint)
|
||||
}
|
||||
|
||||
private fun Context.areThereRecursiveConstraints(typeVariable: TypeVariableMarker, constraint: Constraint) =
|
||||
constraint.type.contains { it.typeConstructor() == typeVariable.freshTypeConstructor() }
|
||||
|
||||
// A <:(=) \alpha <:(=) B => A <: B
|
||||
private fun Context.directWithVariable(
|
||||
typeVariable: TypeVariableMarker,
|
||||
constraint: Constraint
|
||||
) {
|
||||
val shouldBeTypeVariableFlexible = with(utilContext) { typeVariable.shouldBeFlexible() }
|
||||
|
||||
// \alpha <: constraint.type
|
||||
if (constraint.kind != ConstraintKind.LOWER) {
|
||||
getConstraintsForVariable(typeVariable).forEach {
|
||||
if (it.kind != ConstraintKind.UPPER) {
|
||||
addNewIncorporatedConstraint(it.type, constraint.type, shouldBeTypeVariableFlexible, it.isNullabilityConstraint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// constraint.type <: \alpha
|
||||
if (constraint.kind != ConstraintKind.UPPER) {
|
||||
getConstraintsForVariable(typeVariable).forEach {
|
||||
if (it.kind != ConstraintKind.LOWER) {
|
||||
val isFromDeclaredUpperBound =
|
||||
it.position.from is DeclaredUpperBoundConstraintPosition<*> && !it.type.typeConstructor().isTypeVariable()
|
||||
|
||||
addNewIncorporatedConstraint(
|
||||
constraint.type,
|
||||
it.type,
|
||||
shouldBeTypeVariableFlexible,
|
||||
isFromDeclaredUpperBound = isFromDeclaredUpperBound
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// \alpha <: Inv<\beta>, \beta <: Number => \alpha <: Inv<out Number>
|
||||
private fun Context.otherInsideMyConstraint(
|
||||
typeVariable: TypeVariableMarker,
|
||||
constraint: Constraint
|
||||
) {
|
||||
val otherInMyConstraint = SmartSet.create<TypeVariableMarker>()
|
||||
constraint.type.contains {
|
||||
otherInMyConstraint.addIfNotNull(this.getTypeVariable(it.typeConstructor()))
|
||||
false
|
||||
}
|
||||
|
||||
for (otherTypeVariable in otherInMyConstraint) {
|
||||
// to avoid ConcurrentModificationException
|
||||
val otherConstraints = SmartList(this.getConstraintsForVariable(otherTypeVariable))
|
||||
for (otherConstraint in otherConstraints) {
|
||||
generateNewConstraint(typeVariable, constraint, otherTypeVariable, otherConstraint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// \alpha <: Number, \beta <: Inv<\alpha> => \beta <: Inv<out Number>
|
||||
private fun Context.insideOtherConstraint(
|
||||
typeVariable: TypeVariableMarker,
|
||||
constraint: Constraint
|
||||
) {
|
||||
val freshTypeConstructor = typeVariable.freshTypeConstructor()
|
||||
for (typeVariableWithConstraint in this@insideOtherConstraint.allTypeVariablesWithConstraints) {
|
||||
val constraintsWhichConstraintMyVariable = typeVariableWithConstraint.constraints.filter {
|
||||
it.type.contains { it.typeConstructor() == freshTypeConstructor }
|
||||
}
|
||||
constraintsWhichConstraintMyVariable.forEach {
|
||||
generateNewConstraint(typeVariableWithConstraint.typeVariable, it, typeVariable, constraint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.approximateIfNeededAndAddNewConstraint(
|
||||
baseConstraint: Constraint,
|
||||
type: KotlinTypeMarker,
|
||||
targetVariable: TypeVariableMarker,
|
||||
otherVariable: TypeVariableMarker,
|
||||
otherConstraint: Constraint,
|
||||
needApproximation: Boolean = true
|
||||
) {
|
||||
val typeWithSubstitution = baseConstraint.type.substitute(this, otherVariable, type)
|
||||
val prepareType = { toSuper: Boolean ->
|
||||
if (needApproximation) approximateCapturedTypes(typeWithSubstitution, toSuper) else typeWithSubstitution
|
||||
}
|
||||
|
||||
if (baseConstraint.kind != ConstraintKind.LOWER) {
|
||||
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, prepareType(true), isSubtype = false)
|
||||
}
|
||||
if (baseConstraint.kind != ConstraintKind.UPPER) {
|
||||
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, prepareType(false), isSubtype = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.generateNewConstraint(
|
||||
targetVariable: TypeVariableMarker,
|
||||
baseConstraint: Constraint,
|
||||
otherVariable: TypeVariableMarker,
|
||||
otherConstraint: Constraint
|
||||
) {
|
||||
val isBaseGenericType = baseConstraint.type.argumentsCount() != 0
|
||||
val isOtherCapturedType = otherConstraint.type.isCapturedType()
|
||||
val (type, needApproximation) = when (otherConstraint.kind) {
|
||||
ConstraintKind.EQUALITY -> {
|
||||
otherConstraint.type to false
|
||||
}
|
||||
ConstraintKind.UPPER -> {
|
||||
/*
|
||||
* Creating a captured type isn't needed due to its future approximation to `Nothing` or itself
|
||||
* Example:
|
||||
* targetVariable = TypeVariable(A)
|
||||
* baseConstraint = LOWER(TypeVariable(B))
|
||||
* otherConstraint = UPPER(Number)
|
||||
* incorporatedConstraint = Approx(CapturedType(out Number)) <: TypeVariable(A) => Nothing <: TypeVariable(A)
|
||||
* TODO: implement this for generics and captured types
|
||||
*/
|
||||
if (baseConstraint.kind == ConstraintKind.LOWER && !isBaseGenericType && !isOtherCapturedType) {
|
||||
nothingType() to false
|
||||
} else if (baseConstraint.kind == ConstraintKind.UPPER && !isBaseGenericType && !isOtherCapturedType) {
|
||||
otherConstraint.type to false
|
||||
} else {
|
||||
createCapturedType(
|
||||
createTypeArgument(otherConstraint.type, TypeVariance.OUT),
|
||||
listOf(otherConstraint.type),
|
||||
null,
|
||||
CaptureStatus.FOR_INCORPORATION
|
||||
) to true
|
||||
}
|
||||
}
|
||||
ConstraintKind.LOWER -> {
|
||||
/*
|
||||
* Creating a captured type isn't needed due to its future approximation to `Any?` or itself
|
||||
* Example:
|
||||
* targetVariable = TypeVariable(A)
|
||||
* baseConstraint = UPPER(TypeVariable(B))
|
||||
* otherConstraint = LOWER(Number)
|
||||
* incorporatedConstraint = TypeVariable(A) <: Approx(CapturedType(in Number)) => TypeVariable(A) <: Any?
|
||||
* TODO: implement this for generics and captured types
|
||||
*/
|
||||
if (baseConstraint.kind == ConstraintKind.UPPER && !isBaseGenericType && !isOtherCapturedType) {
|
||||
nullableAnyType() to false
|
||||
} else if (baseConstraint.kind == ConstraintKind.LOWER && !isBaseGenericType && !isOtherCapturedType) {
|
||||
otherConstraint.type to false
|
||||
} else {
|
||||
createCapturedType(
|
||||
createTypeArgument(otherConstraint.type, TypeVariance.IN),
|
||||
emptyList(),
|
||||
otherConstraint.type,
|
||||
CaptureStatus.FOR_INCORPORATION
|
||||
) to true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
approximateIfNeededAndAddNewConstraint(baseConstraint, type, targetVariable, otherVariable, otherConstraint, needApproximation)
|
||||
}
|
||||
|
||||
private fun Context.addNewConstraint(
|
||||
targetVariable: TypeVariableMarker,
|
||||
baseConstraint: Constraint,
|
||||
otherVariable: TypeVariableMarker,
|
||||
otherConstraint: Constraint,
|
||||
newConstraint: KotlinTypeMarker,
|
||||
isSubtype: Boolean
|
||||
) {
|
||||
if (targetVariable in getNestedTypeVariables(newConstraint)) return
|
||||
|
||||
val isUsefulForNullabilityConstraint =
|
||||
isPotentialUsefulNullabilityConstraint(newConstraint, otherConstraint.type, otherConstraint.kind)
|
||||
val isFromVariableFixation = baseConstraint.position.from is FixVariableConstraintPosition<*>
|
||||
|| otherConstraint.position.from is FixVariableConstraintPosition<*>
|
||||
|
||||
if (!otherConstraint.kind.isEqual() &&
|
||||
!isUsefulForNullabilityConstraint &&
|
||||
!isFromVariableFixation &&
|
||||
!containsConstrainingTypeWithoutProjection(newConstraint, otherConstraint)
|
||||
) return
|
||||
|
||||
if (trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial(
|
||||
baseConstraint, otherConstraint, newConstraint, isSubtype
|
||||
)
|
||||
) return
|
||||
|
||||
val derivedFrom = SmartSet.create(baseConstraint.derivedFrom).also { it.addAll(otherConstraint.derivedFrom) }
|
||||
if (otherVariable in derivedFrom) return
|
||||
|
||||
derivedFrom.add(otherVariable)
|
||||
|
||||
val kind = if (isSubtype) ConstraintKind.LOWER else ConstraintKind.UPPER
|
||||
|
||||
val inputTypePosition = baseConstraint.position.from as? OnlyInputTypeConstraintPosition
|
||||
|
||||
val isNewConstraintUsefulForNullability = isUsefulForNullabilityConstraint && newConstraint.isNullableNothing()
|
||||
val isOtherConstraintUsefulForNullability = otherConstraint.isNullabilityConstraint && otherConstraint.type.isNullableNothing()
|
||||
val isNullabilityConstraint = isNewConstraintUsefulForNullability || isOtherConstraintUsefulForNullability
|
||||
|
||||
val constraintContext = ConstraintContext(kind, derivedFrom, inputTypePosition, isNullabilityConstraint)
|
||||
|
||||
addNewIncorporatedConstraint(targetVariable, newConstraint, constraintContext)
|
||||
}
|
||||
|
||||
private fun Context.containsConstrainingTypeWithoutProjection(
|
||||
newConstraint: KotlinTypeMarker,
|
||||
otherConstraint: Constraint
|
||||
): Boolean {
|
||||
return getNestedArguments(newConstraint).any {
|
||||
it.getType().typeConstructor() == otherConstraint.type.typeConstructor() && it.getVariance() == TypeVariance.INV
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.isPotentialUsefulNullabilityConstraint(
|
||||
newConstraint: KotlinTypeMarker,
|
||||
otherConstraint: KotlinTypeMarker,
|
||||
kind: ConstraintKind
|
||||
): Boolean {
|
||||
if (trivialConstraintTypeInferenceOracle.isSuitableResultedType(newConstraint)) return false
|
||||
|
||||
val otherConstraintCanAddNullabilityToNewOne =
|
||||
!newConstraint.isNullableType() && otherConstraint.isNullableType() && kind == ConstraintKind.LOWER
|
||||
val newConstraintCanAddNullabilityToOtherOne =
|
||||
newConstraint.isNullableType() && !otherConstraint.isNullableType() && kind == ConstraintKind.UPPER
|
||||
|
||||
return otherConstraintCanAddNullabilityToNewOne || newConstraintCanAddNullabilityToOtherOne
|
||||
}
|
||||
|
||||
private fun Context.getNestedTypeVariables(type: KotlinTypeMarker): List<TypeVariableMarker> =
|
||||
getNestedArguments(type).mapNotNullTo(SmartList()) { getTypeVariable(it.getType().typeConstructor()) }
|
||||
|
||||
|
||||
private fun KotlinTypeMarker.substitute(c: Context, typeVariable: TypeVariableMarker, value: KotlinTypeMarker): KotlinTypeMarker {
|
||||
val substitutor = c.typeSubstitutorByTypeConstructor(mapOf(typeVariable.freshTypeConstructor(c) to value))
|
||||
return substitutor.safeSubstitute(c, this)
|
||||
}
|
||||
|
||||
|
||||
private fun approximateCapturedTypes(type: KotlinTypeMarker, toSuper: Boolean): KotlinTypeMarker =
|
||||
if (toSuper) typeApproximator.approximateToSuperType(type, TypeApproximatorConfiguration.IncorporationConfiguration) ?: type
|
||||
else typeApproximator.approximateToSubType(type, TypeApproximatorConfiguration.IncorporationConfiguration) ?: type
|
||||
}
|
||||
|
||||
private fun TypeSystemInferenceExtensionContext.getNestedArguments(type: KotlinTypeMarker): List<TypeArgumentMarker> {
|
||||
val result = SmartList<TypeArgumentMarker>()
|
||||
val stack = ArrayDeque<TypeArgumentMarker>()
|
||||
|
||||
when (type) {
|
||||
is FlexibleTypeMarker -> {
|
||||
stack.push(createTypeArgument(type.lowerBound(), TypeVariance.INV))
|
||||
stack.push(createTypeArgument(type.upperBound(), TypeVariance.INV))
|
||||
}
|
||||
else -> stack.push(createTypeArgument(type, TypeVariance.INV))
|
||||
}
|
||||
|
||||
stack.push(createTypeArgument(type, TypeVariance.INV))
|
||||
|
||||
val addArgumentsToStack = { projectedType: KotlinTypeMarker ->
|
||||
for (argumentIndex in 0 until projectedType.argumentsCount()) {
|
||||
stack.add(projectedType.getArgument(argumentIndex))
|
||||
}
|
||||
}
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
val typeProjection = stack.pop()
|
||||
if (typeProjection.isStarProjection()) continue
|
||||
|
||||
result.add(typeProjection)
|
||||
|
||||
when (val projectedType = typeProjection.getType()) {
|
||||
is FlexibleTypeMarker -> {
|
||||
addArgumentsToStack(projectedType.lowerBound())
|
||||
addArgumentsToStack(projectedType.upperBound())
|
||||
}
|
||||
else -> addArgumentsToStack(projectedType)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
+351
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind.*
|
||||
import org.jetbrains.kotlin.types.AbstractTypeApproximator
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
|
||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import kotlin.math.max
|
||||
|
||||
class ConstraintInjector(
|
||||
val constraintIncorporator: ConstraintIncorporator,
|
||||
val typeApproximator: AbstractTypeApproximator
|
||||
) {
|
||||
private val ALLOWED_DEPTH_DELTA_FOR_INCORPORATION = 1
|
||||
|
||||
interface Context : TypeSystemInferenceExtensionContext {
|
||||
val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
|
||||
|
||||
var maxTypeDepthFromInitialConstraints: Int
|
||||
val notFixedTypeVariables: MutableMap<TypeConstructorMarker, MutableVariableWithConstraints>
|
||||
val fixedTypeVariables: MutableMap<TypeConstructorMarker, KotlinTypeMarker>
|
||||
|
||||
fun addInitialConstraint(initialConstraint: InitialConstraint)
|
||||
fun addError(error: ConstraintSystemError)
|
||||
}
|
||||
|
||||
fun addInitialSubtypeConstraint(c: Context, lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, position: ConstraintPosition) {
|
||||
val initialConstraint = InitialConstraint(lowerType, upperType, UPPER, position).also { c.addInitialConstraint(it) }
|
||||
|
||||
updateAllowedTypeDepth(c, lowerType)
|
||||
updateAllowedTypeDepth(c, upperType)
|
||||
|
||||
addSubTypeConstraintAndIncorporateIt(
|
||||
c,
|
||||
lowerType,
|
||||
upperType,
|
||||
TypeCheckerContext(c, IncorporationConstraintPosition(position, initialConstraint))
|
||||
)
|
||||
}
|
||||
|
||||
fun addInitialEqualityConstraint(c: Context, a: KotlinTypeMarker, b: KotlinTypeMarker, position: ConstraintPosition) {
|
||||
val initialConstraint = InitialConstraint(a, b, EQUALITY, position).also { c.addInitialConstraint(it) }
|
||||
|
||||
updateAllowedTypeDepth(c, a)
|
||||
updateAllowedTypeDepth(c, b)
|
||||
|
||||
val typeCheckerContext = TypeCheckerContext(c, IncorporationConstraintPosition(position, initialConstraint))
|
||||
|
||||
addSubTypeConstraintAndIncorporateIt(c, a, b, typeCheckerContext)
|
||||
addSubTypeConstraintAndIncorporateIt(c, b, a, typeCheckerContext)
|
||||
}
|
||||
|
||||
private fun addSubTypeConstraintAndIncorporateIt(
|
||||
c: Context,
|
||||
lowerType: KotlinTypeMarker,
|
||||
upperType: KotlinTypeMarker,
|
||||
typeCheckerContext: TypeCheckerContext
|
||||
) {
|
||||
typeCheckerContext.setConstrainingTypesToPrintDebugInfo(lowerType, upperType)
|
||||
typeCheckerContext.runIsSubtypeOf(lowerType, upperType)
|
||||
|
||||
while (typeCheckerContext.hasConstraintsToProcess()) {
|
||||
for ((typeVariable, constraint) in typeCheckerContext.extractAllConstraints()!!) {
|
||||
if (c.shouldWeSkipConstraint(typeVariable, constraint)) continue
|
||||
|
||||
val constraints =
|
||||
c.notFixedTypeVariables[typeVariable.freshTypeConstructor(c)] ?: typeCheckerContext.fixedTypeVariable(typeVariable)
|
||||
|
||||
// it is important, that we add constraint here(not inside TypeCheckerContext), because inside incorporation we read constraints
|
||||
constraints.addConstraint(constraint)?.let {
|
||||
if (!constraint.isNullabilityConstraint) {
|
||||
constraintIncorporator.incorporate(typeCheckerContext, typeVariable, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val contextOps = c as? ConstraintSystemOperation
|
||||
if (!typeCheckerContext.hasConstraintsToProcess() ||
|
||||
(contextOps != null && c.notFixedTypeVariables.all { typeVariable ->
|
||||
typeVariable.value.constraints.any { constraint ->
|
||||
constraint.kind == EQUALITY && contextOps.isProperType(constraint.type)
|
||||
}
|
||||
})
|
||||
) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAllowedTypeDepth(c: Context, initialType: KotlinTypeMarker) = with(c) {
|
||||
c.maxTypeDepthFromInitialConstraints = max(c.maxTypeDepthFromInitialConstraints, initialType.typeDepth())
|
||||
}
|
||||
|
||||
private fun Context.shouldWeSkipConstraint(typeVariable: TypeVariableMarker, constraint: Constraint): Boolean {
|
||||
assert(constraint.kind != EQUALITY)
|
||||
|
||||
val constraintType = constraint.type
|
||||
|
||||
if (constraintType.typeConstructor() == typeVariable.freshTypeConstructor()) {
|
||||
if (constraintType.lowerBoundIfFlexible().isMarkedNullable() && constraint.kind == LOWER) return false // T? <: T
|
||||
|
||||
return true // T <: T(?!)
|
||||
}
|
||||
|
||||
if (constraint.position.from is DeclaredUpperBoundConstraintPosition<*> &&
|
||||
constraint.kind == UPPER && constraintType.isNullableAny()
|
||||
) {
|
||||
return true // T <: Any?
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun Context.isAllowedType(type: KotlinTypeMarker) =
|
||||
type.typeDepth() <= maxTypeDepthFromInitialConstraints + ALLOWED_DEPTH_DELTA_FOR_INCORPORATION
|
||||
|
||||
private inner class TypeCheckerContext(val c: Context, val position: IncorporationConstraintPosition) :
|
||||
AbstractTypeCheckerContextForConstraintSystem(), ConstraintIncorporator.Context, TypeSystemInferenceExtensionContext by c {
|
||||
// We use `var` intentionally to avoid extra allocations as this property is quite "hot"
|
||||
private var possibleNewConstraints: MutableList<Pair<TypeVariableMarker, Constraint>>? = null
|
||||
|
||||
private var baseLowerType = position.initialConstraint.a
|
||||
private var baseUpperType = position.initialConstraint.b
|
||||
|
||||
private var isIncorporatingConstraintFromDeclaredUpperBound = false
|
||||
|
||||
fun extractAllConstraints() = possibleNewConstraints.also { possibleNewConstraints = null }
|
||||
|
||||
fun addPossibleNewConstraint(variable: TypeVariableMarker, constraint: Constraint) {
|
||||
if (possibleNewConstraints == null) {
|
||||
possibleNewConstraints = SmartList()
|
||||
}
|
||||
possibleNewConstraints!!.add(variable to constraint)
|
||||
}
|
||||
|
||||
fun hasConstraintsToProcess() = possibleNewConstraints != null
|
||||
|
||||
fun setConstrainingTypesToPrintDebugInfo(lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker) {
|
||||
baseLowerType = lowerType
|
||||
baseUpperType = upperType
|
||||
}
|
||||
|
||||
val baseContext: AbstractTypeCheckerContext = newBaseTypeCheckerContext(isErrorTypeEqualsToAnything, isStubTypeEqualsToAnything)
|
||||
|
||||
override fun substitutionSupertypePolicy(type: SimpleTypeMarker): SupertypesPolicy {
|
||||
return baseContext.substitutionSupertypePolicy(type)
|
||||
}
|
||||
|
||||
override fun areEqualTypeConstructors(a: TypeConstructorMarker, b: TypeConstructorMarker): Boolean {
|
||||
return baseContext.areEqualTypeConstructors(a, b)
|
||||
}
|
||||
|
||||
override fun prepareType(type: KotlinTypeMarker): KotlinTypeMarker {
|
||||
return baseContext.prepareType(type)
|
||||
}
|
||||
|
||||
override fun refineType(type: KotlinTypeMarker): KotlinTypeMarker {
|
||||
return with(constraintIncorporator.utilContext) {
|
||||
type.refineType()
|
||||
}
|
||||
}
|
||||
|
||||
fun runIsSubtypeOf(
|
||||
lowerType: KotlinTypeMarker,
|
||||
upperType: KotlinTypeMarker,
|
||||
shouldTryUseDifferentFlexibilityForUpperType: Boolean = false,
|
||||
isFromNullabilityConstraint: Boolean = false
|
||||
) {
|
||||
fun isSubtypeOf(upperType: KotlinTypeMarker) =
|
||||
AbstractTypeChecker.isSubtypeOf(
|
||||
this@TypeCheckerContext as AbstractTypeCheckerContext,
|
||||
lowerType,
|
||||
upperType,
|
||||
isFromNullabilityConstraint
|
||||
)
|
||||
|
||||
if (!isSubtypeOf(upperType)) {
|
||||
// todo improve error reporting -- add information about base types
|
||||
if (shouldTryUseDifferentFlexibilityForUpperType && upperType.isSimpleType()) {
|
||||
/*
|
||||
* Please don't reuse this logic.
|
||||
* It's necessary to solve constraint systems when flexibility isn't propagated through a type variable.
|
||||
* It's OK in the old inference because it uses already substituted types, that are with the correct flexibility.
|
||||
*/
|
||||
require(upperType is SimpleTypeMarker)
|
||||
val flexibleUpperType = createFlexibleType(upperType, upperType.withNullability(true))
|
||||
if (!isSubtypeOf(flexibleUpperType)) {
|
||||
c.addError(NewConstraintError(lowerType, flexibleUpperType, position))
|
||||
}
|
||||
} else {
|
||||
c.addError(NewConstraintError(lowerType, upperType, position))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// from AbstractTypeCheckerContextForConstraintSystem
|
||||
override fun isMyTypeVariable(type: SimpleTypeMarker): Boolean =
|
||||
c.allTypeVariables.containsKey(type.typeConstructor())
|
||||
|
||||
override fun addUpperConstraint(typeVariable: TypeConstructorMarker, superType: KotlinTypeMarker) =
|
||||
addConstraint(typeVariable, superType, UPPER)
|
||||
|
||||
override fun addLowerConstraint(
|
||||
typeVariable: TypeConstructorMarker,
|
||||
subType: KotlinTypeMarker,
|
||||
isFromNullabilityConstraint: Boolean
|
||||
) = addConstraint(typeVariable, subType, LOWER, isFromNullabilityConstraint)
|
||||
|
||||
private fun isCapturedTypeFromSubtyping(type: KotlinTypeMarker) =
|
||||
when ((type as? CapturedTypeMarker)?.captureStatus()) {
|
||||
null, CaptureStatus.FROM_EXPRESSION -> false
|
||||
CaptureStatus.FOR_SUBTYPING -> true
|
||||
CaptureStatus.FOR_INCORPORATION ->
|
||||
error("Captured type for incorporation shouldn't escape from incorporation: $type\n" + renderBaseConstraint())
|
||||
}
|
||||
|
||||
private fun addConstraint(
|
||||
typeVariableConstructor: TypeConstructorMarker,
|
||||
type: KotlinTypeMarker,
|
||||
kind: ConstraintKind,
|
||||
isFromNullabilityConstraint: Boolean = false
|
||||
) {
|
||||
val typeVariable = c.allTypeVariables[typeVariableConstructor]
|
||||
?: error("Should by type variableConstructor: $typeVariableConstructor. ${c.allTypeVariables.values}")
|
||||
|
||||
addNewIncorporatedConstraint(
|
||||
typeVariable,
|
||||
type,
|
||||
ConstraintContext(kind, emptySet(), isNullabilityConstraint = isFromNullabilityConstraint)
|
||||
)
|
||||
}
|
||||
|
||||
private fun addNewIncorporatedConstraintFromDeclaredUpperBound(runIsSubtypeOf: Runnable) {
|
||||
isIncorporatingConstraintFromDeclaredUpperBound = true
|
||||
runIsSubtypeOf.run()
|
||||
isIncorporatingConstraintFromDeclaredUpperBound = false
|
||||
}
|
||||
|
||||
// from ConstraintIncorporator.Context
|
||||
override fun addNewIncorporatedConstraint(
|
||||
lowerType: KotlinTypeMarker,
|
||||
upperType: KotlinTypeMarker,
|
||||
shouldTryUseDifferentFlexibilityForUpperType: Boolean,
|
||||
isFromNullabilityConstraint: Boolean,
|
||||
isFromDeclaredUpperBound: Boolean
|
||||
) {
|
||||
if (lowerType === upperType) return
|
||||
if (c.isAllowedType(lowerType) && c.isAllowedType(upperType)) {
|
||||
fun runIsSubtypeOf() =
|
||||
runIsSubtypeOf(lowerType, upperType, shouldTryUseDifferentFlexibilityForUpperType, isFromNullabilityConstraint)
|
||||
|
||||
if (isFromDeclaredUpperBound) addNewIncorporatedConstraintFromDeclaredUpperBound(::runIsSubtypeOf) else runIsSubtypeOf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun addNewIncorporatedConstraint(
|
||||
typeVariable: TypeVariableMarker,
|
||||
type: KotlinTypeMarker,
|
||||
constraintContext: ConstraintContext
|
||||
) {
|
||||
val (kind, derivedFrom, inputTypePosition, isNullabilityConstraint) = constraintContext
|
||||
|
||||
var targetType = type
|
||||
if (targetType.isUninferredParameter()) {
|
||||
// there already should be an error, so there is no point in reporting one more
|
||||
return
|
||||
}
|
||||
|
||||
if (targetType.isError()) {
|
||||
c.addError(ConstrainingTypeIsError(typeVariable, targetType, position))
|
||||
return
|
||||
}
|
||||
|
||||
if (type.contains(this::isCapturedTypeFromSubtyping)) {
|
||||
// TypeVariable <: type -> if TypeVariable <: subType => TypeVariable <: type
|
||||
if (kind == UPPER) {
|
||||
val subType =
|
||||
typeApproximator.approximateToSubType(type, TypeApproximatorConfiguration.SubtypeCapturedTypesApproximation)
|
||||
if (subType != null) {
|
||||
targetType = subType
|
||||
}
|
||||
}
|
||||
|
||||
if (kind == LOWER) {
|
||||
val superType =
|
||||
typeApproximator.approximateToSuperType(type, TypeApproximatorConfiguration.SubtypeCapturedTypesApproximation)
|
||||
if (superType != null) { // todo rethink error reporting for Any cases
|
||||
targetType = superType
|
||||
}
|
||||
}
|
||||
|
||||
if (targetType === type) {
|
||||
c.addError(CapturedTypeFromSubtyping(typeVariable, type, position))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val position = if (isIncorporatingConstraintFromDeclaredUpperBound) position.copy(isFromDeclaredUpperBound = true) else position
|
||||
|
||||
val newConstraint = Constraint(
|
||||
kind, targetType, position,
|
||||
derivedFrom = derivedFrom,
|
||||
isNullabilityConstraint = isNullabilityConstraint,
|
||||
inputTypePositionBeforeIncorporation = inputTypePosition
|
||||
)
|
||||
|
||||
addPossibleNewConstraint(typeVariable, newConstraint)
|
||||
}
|
||||
|
||||
override val allTypeVariablesWithConstraints: Collection<VariableWithConstraints>
|
||||
get() = c.notFixedTypeVariables.values
|
||||
|
||||
override fun getTypeVariable(typeConstructor: TypeConstructorMarker): TypeVariableMarker? {
|
||||
val typeVariable = c.allTypeVariables[typeConstructor]
|
||||
if (typeVariable != null && !c.notFixedTypeVariables.containsKey(typeConstructor)) {
|
||||
fixedTypeVariable(typeVariable)
|
||||
}
|
||||
return typeVariable
|
||||
}
|
||||
|
||||
override fun getConstraintsForVariable(typeVariable: TypeVariableMarker) =
|
||||
c.notFixedTypeVariables[typeVariable.freshTypeConstructor()]?.constraints
|
||||
?: fixedTypeVariable(typeVariable)
|
||||
|
||||
fun fixedTypeVariable(variable: TypeVariableMarker): Nothing {
|
||||
error(
|
||||
"Type variable $variable should not be fixed!\n" +
|
||||
renderBaseConstraint()
|
||||
)
|
||||
}
|
||||
|
||||
private fun renderBaseConstraint() = "Base constraint: $baseLowerType <: $baseUpperType from position: $position"
|
||||
}
|
||||
}
|
||||
|
||||
data class ConstraintContext(
|
||||
val kind: ConstraintKind,
|
||||
val derivedFrom: Set<TypeVariableMarker>,
|
||||
val inputTypePositionBeforeIncorporation: OnlyInputTypeConstraintPosition? = null,
|
||||
val isNullabilityConstraint: Boolean
|
||||
)
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.FixVariableConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeVariableMarker
|
||||
|
||||
interface ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context {
|
||||
val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
|
||||
override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
|
||||
override val postponedTypeVariables: List<TypeVariableMarker>
|
||||
|
||||
fun getBuilder(): ConstraintSystemBuilder
|
||||
|
||||
// type can be proper if it not contains not fixed type variables
|
||||
fun canBeProper(type: KotlinTypeMarker): Boolean
|
||||
|
||||
fun containsOnlyFixedOrPostponedVariables(type: KotlinTypeMarker): Boolean
|
||||
|
||||
// mutable operations
|
||||
fun addError(error: ConstraintSystemError)
|
||||
|
||||
fun fixVariable(variable: TypeVariableMarker, resultType: KotlinTypeMarker, position: FixVariableConstraintPosition<*>)
|
||||
|
||||
fun asConstraintSystemCompletionContext(): ConstraintSystemCompletionContext
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
enum class ConstraintSystemCompletionMode {
|
||||
FULL,
|
||||
PARTIAL,
|
||||
UNTIL_FIRST_LAMBDA
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeVariableMarker
|
||||
|
||||
/*
|
||||
* Functions from this context can not be moved to TypeSystemInferenceExtensionContext, because
|
||||
* it's classic implementation, ClassicTypeSystemContext lays in :core:descriptors,
|
||||
* but we need access classes from :compiler:resolution for this function implementation
|
||||
*/
|
||||
interface ConstraintSystemUtilContext {
|
||||
fun TypeVariableMarker.shouldBeFlexible(): Boolean
|
||||
fun TypeVariableMarker.hasOnlyInputTypesAttribute(): Boolean
|
||||
fun KotlinTypeMarker.unCapture(): KotlinTypeMarker
|
||||
fun TypeVariableMarker.isReified(): Boolean
|
||||
fun KotlinTypeMarker.refineType(): KotlinTypeMarker
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator.ResolveDirection
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.*
|
||||
import org.jetbrains.kotlin.types.AbstractTypeApproximator
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
|
||||
class ResultTypeResolver(
|
||||
val typeApproximator: AbstractTypeApproximator,
|
||||
val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle
|
||||
) {
|
||||
interface Context : TypeSystemInferenceExtensionContext {
|
||||
fun isProperType(type: KotlinTypeMarker): Boolean
|
||||
fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker
|
||||
fun isReified(variable: TypeVariableMarker): Boolean
|
||||
}
|
||||
|
||||
fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): KotlinTypeMarker {
|
||||
findResultTypeOrNull(c, variableWithConstraints, direction)?.let { return it }
|
||||
|
||||
// no proper constraints
|
||||
return run {
|
||||
if (direction == ResolveDirection.TO_SUBTYPE) c.nothingType() else c.nullableAnyType()
|
||||
}
|
||||
}
|
||||
|
||||
private fun findResultTypeOrNull(
|
||||
c: Context,
|
||||
variableWithConstraints: VariableWithConstraints,
|
||||
direction: ResolveDirection
|
||||
): KotlinTypeMarker? {
|
||||
findResultIfThereIsEqualsConstraint(c, variableWithConstraints)?.let { return it }
|
||||
|
||||
val subType = c.findSubType(variableWithConstraints)
|
||||
val superType = c.findSuperType(variableWithConstraints)
|
||||
return if (direction == ResolveDirection.TO_SUBTYPE || direction == ResolveDirection.UNKNOWN) {
|
||||
c.resultType(subType, superType, variableWithConstraints)
|
||||
} else {
|
||||
c.resultType(superType, subType, variableWithConstraints)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.resultType(
|
||||
firstCandidate: KotlinTypeMarker?,
|
||||
secondCandidate: KotlinTypeMarker?,
|
||||
variableWithConstraints: VariableWithConstraints
|
||||
): KotlinTypeMarker? {
|
||||
if (firstCandidate == null || secondCandidate == null) return firstCandidate ?: secondCandidate
|
||||
|
||||
specialResultForIntersectionType(firstCandidate, secondCandidate)?.let { intersectionWithAlternative ->
|
||||
return intersectionWithAlternative
|
||||
}
|
||||
|
||||
if (isSuitableType(firstCandidate, variableWithConstraints)) return firstCandidate
|
||||
|
||||
return if (isSuitableType(secondCandidate, variableWithConstraints)) {
|
||||
secondCandidate
|
||||
} else {
|
||||
firstCandidate
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.specialResultForIntersectionType(
|
||||
firstCandidate: KotlinTypeMarker,
|
||||
secondCandidate: KotlinTypeMarker,
|
||||
): KotlinTypeMarker? {
|
||||
if (firstCandidate.typeConstructor().isIntersection()) {
|
||||
if (!AbstractTypeChecker.isSubtypeOf(this, firstCandidate.toPublicType(), secondCandidate.toPublicType())) {
|
||||
return createTypeWithAlternativeForIntersectionResult(firstCandidate, secondCandidate)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun KotlinTypeMarker.toPublicType(): KotlinTypeMarker =
|
||||
typeApproximator.approximateToSuperType(this, TypeApproximatorConfiguration.PublicDeclaration) ?: this
|
||||
|
||||
private fun Context.isSuitableType(resultType: KotlinTypeMarker, variableWithConstraints: VariableWithConstraints): Boolean {
|
||||
val filteredConstraints = variableWithConstraints.constraints.filter { isProperTypeForFixation(it.type) }
|
||||
for (constraint in filteredConstraints) {
|
||||
if (!checkConstraint(this, constraint.type, constraint.kind, resultType)) return false
|
||||
}
|
||||
if (!trivialConstraintTypeInferenceOracle.isSuitableResultedType(resultType)) {
|
||||
if (resultType.isNullableType() && checkSingleLowerNullabilityConstraint(filteredConstraints)) return false
|
||||
if (isReified(variableWithConstraints.typeVariable)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun checkSingleLowerNullabilityConstraint(constraints: List<Constraint>): Boolean {
|
||||
return constraints.singleOrNull { it.kind.isLower() }?.isNullabilityConstraint ?: false
|
||||
}
|
||||
|
||||
private fun Context.findSubType(variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? {
|
||||
val lowerConstraintTypes = prepareLowerConstraints(variableWithConstraints.constraints)
|
||||
|
||||
if (lowerConstraintTypes.isNotEmpty()) {
|
||||
val types = sinkIntegerLiteralTypes(lowerConstraintTypes)
|
||||
var commonSuperType = computeCommonSuperType(types)
|
||||
|
||||
if (commonSuperType.contains { it is StubTypeMarker }) {
|
||||
val typesWithoutStubs = types.filter { lowerType ->
|
||||
!lowerType.contains { it is StubTypeMarker }
|
||||
}
|
||||
|
||||
if (typesWithoutStubs.isNotEmpty()) {
|
||||
commonSuperType = computeCommonSuperType(typesWithoutStubs)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* fun <T> Array<out T>.intersect(other: Iterable<T>) {
|
||||
* val set = toMutableSet()
|
||||
* set.retainAll(other)
|
||||
* }
|
||||
* fun <X> Array<out X>.toMutableSet(): MutableSet<X> = ...
|
||||
* fun <Y> MutableCollection<in Y>.retainAll(elements: Iterable<Y>) {}
|
||||
*
|
||||
* Here, when we solve type system for `toMutableSet` we have the following constrains:
|
||||
* Array<C(out T)> <: Array<out X> => C(out X) <: T.
|
||||
* If we fix it to T = C(out X) then return type of `toMutableSet()` will be `MutableSet<C(out X)>`
|
||||
* and type of variable `set` will be `MutableSet<out T>` and the following line will have contradiction.
|
||||
*
|
||||
* To fix this problem when we fix variable, we will approximate captured types before fixation.
|
||||
*
|
||||
*/
|
||||
|
||||
return typeApproximator.approximateToSuperType(
|
||||
commonSuperType,
|
||||
TypeApproximatorConfiguration.InternalTypesApproximation
|
||||
) ?: commonSuperType
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun Context.computeCommonSuperType(types: List<KotlinTypeMarker>): KotlinTypeMarker =
|
||||
with(NewCommonSuperTypeCalculator) { commonSuperType(types) }
|
||||
|
||||
private fun Context.prepareLowerConstraints(constraints: List<Constraint>): List<KotlinTypeMarker> {
|
||||
var atLeastOneProper = false
|
||||
var atLeastOneNonProper = false
|
||||
|
||||
val lowerConstraintTypes = mutableListOf<KotlinTypeMarker>()
|
||||
|
||||
for (constraint in constraints) {
|
||||
if (constraint.kind != ConstraintKind.LOWER) continue
|
||||
|
||||
val type = constraint.type
|
||||
lowerConstraintTypes.add(type)
|
||||
|
||||
if (isProperTypeForFixation(type)) {
|
||||
atLeastOneProper = true
|
||||
} else {
|
||||
atLeastOneNonProper = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!atLeastOneProper) return emptyList()
|
||||
if (!atLeastOneNonProper) return lowerConstraintTypes
|
||||
|
||||
val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor()
|
||||
|
||||
return lowerConstraintTypes.map { if (isProperTypeForFixation(it)) it else notFixedToStubTypesSubstitutor.safeSubstitute(it) }
|
||||
}
|
||||
|
||||
private fun Context.sinkIntegerLiteralTypes(types: List<KotlinTypeMarker>): List<KotlinTypeMarker> {
|
||||
return types.sortedBy { type ->
|
||||
|
||||
val containsILT = type.contains { it.asSimpleType()?.isIntegerLiteralType() ?: false }
|
||||
if (containsILT) 1 else 0
|
||||
}
|
||||
}
|
||||
|
||||
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 = intersectTypes(upperConstraints.map { it.type })
|
||||
|
||||
return typeApproximator.approximateToSubType(
|
||||
upperType,
|
||||
TypeApproximatorConfiguration.InternalTypesApproximation
|
||||
) ?: upperType
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun Context.isProperTypeForFixation(type: KotlinTypeMarker): Boolean =
|
||||
isProperTypeForFixation(type) { isProperType(it) }
|
||||
|
||||
private fun findResultIfThereIsEqualsConstraint(c: Context, variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? =
|
||||
with(c) {
|
||||
val properEqualityConstraints = variableWithConstraints.constraints.filter {
|
||||
it.kind == ConstraintKind.EQUALITY && c.isProperTypeForFixation(it.type)
|
||||
}
|
||||
|
||||
return c.representativeFromEqualityConstraints(properEqualityConstraints)
|
||||
}
|
||||
|
||||
// Discriminate integer literal types as they are less specific than separate integer types (Int, Short...)
|
||||
private fun Context.representativeFromEqualityConstraints(constraints: List<Constraint>): KotlinTypeMarker? {
|
||||
if (constraints.isEmpty()) return null
|
||||
|
||||
val constraintTypes = constraints.map { it.type }
|
||||
val nonLiteralTypes = constraintTypes.filter { !it.typeConstructor().isIntegerLiteralTypeConstructor() }
|
||||
return nonLiteralTypes.singleBestRepresentative()
|
||||
?: constraintTypes.singleBestRepresentative()
|
||||
?: constraintTypes.first() // seems like constraint system has contradiction
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext
|
||||
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContextDelegate
|
||||
|
||||
class TrivialConstraintTypeInferenceOracle private constructor(context: TypeSystemInferenceExtensionContext) :
|
||||
TypeSystemInferenceExtensionContext by context {
|
||||
// This constructor is used for injection only in old FE
|
||||
constructor(context: TypeSystemInferenceExtensionContextDelegate) : this(context as TypeSystemInferenceExtensionContext)
|
||||
|
||||
// The idea is to add knowledge that constraint `Nothing(?) <: T` is quite useless and
|
||||
// it's totally fine to go and resolve postponed argument without fixation T to Nothing(?).
|
||||
// In other words, constraint `Nothing(?) <: T` is *not* proper
|
||||
fun isNotInterestingConstraint(constraint: Constraint): Boolean {
|
||||
return constraint.kind == ConstraintKind.LOWER && constraint.type.typeConstructor().isNothingConstructor()
|
||||
}
|
||||
|
||||
// This function controls the choice between sub and super result type
|
||||
// Even that Nothing(?) is the most specific type for subtype, it doesn't bring valuable information to the user,
|
||||
// therefore it is discriminated in favor of supertype
|
||||
fun isSuitableResultedType(
|
||||
resultType: KotlinTypeMarker
|
||||
): Boolean {
|
||||
return !resultType.typeConstructor().isNothingConstructor()
|
||||
}
|
||||
|
||||
// It's possible to generate Nothing-like constraints inside incorporation mechanism:
|
||||
// For instance, when two type variables are in subtyping relation `T <: K`, after incorporation
|
||||
// there will be constraint `approximation(out K) <: K` => `Nothing <: K`, which is innocent
|
||||
// but can change result of the constraint system.
|
||||
// Therefore, here we avoid adding such trivial constraints to have stable constraint system
|
||||
fun isGeneratedConstraintTrivial(
|
||||
baseConstraint: Constraint,
|
||||
otherConstraint: Constraint,
|
||||
generatedConstraintType: KotlinTypeMarker,
|
||||
isSubtype: Boolean
|
||||
): Boolean {
|
||||
if (isSubtype && (generatedConstraintType.isNothing() || generatedConstraintType.isFlexibleNothing())) return true
|
||||
if (!isSubtype && generatedConstraintType.isNullableAny()) return true
|
||||
|
||||
// If types from constraints that will be used to generate new constraint already contains `Nothing(?)`,
|
||||
// then we can't decide that resulting constraint will be useless
|
||||
if (baseConstraint.type.contains { it.isNothingOrNullableNothing() }) return false
|
||||
if (otherConstraint.type.contains { it.isNothingOrNullableNothing() }) return false
|
||||
|
||||
// It's important to preserve constraints with nullable Nothing: `Nothing? <: T` (see implicitNothingConstraintFromReturn.kt test)
|
||||
if (generatedConstraintType.containsOnlyNonNullableNothing()) return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
private fun KotlinTypeMarker.isNothingOrNullableNothing(): Boolean =
|
||||
typeConstructor().isNothingConstructor()
|
||||
|
||||
|
||||
private fun KotlinTypeMarker.containsOnlyNonNullableNothing(): Boolean =
|
||||
contains {
|
||||
(it.isNothing() || it.isFlexibleNothing()) &&
|
||||
!(it is SimpleTypeMarker && it.typeConstructor().isNothingConstructor() && it.isMarkedNullable())
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun create(context: TypeSystemInferenceExtensionContext) = TrivialConstraintTypeInferenceOracle(context)
|
||||
}
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
||||
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
|
||||
class TypeVariableDependencyInformationProvider(
|
||||
private val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>,
|
||||
private val postponedKtPrimitives: List<PostponedResolvedAtomMarker>,
|
||||
private val topLevelType: KotlinTypeMarker?,
|
||||
private val typeSystemContext: TypeSystemInferenceExtensionContext
|
||||
) {
|
||||
/*
|
||||
* Not oriented edges
|
||||
* TypeVariable(A) has UPPER(Function1<TypeVariable(B), R>) => A and B are related deeply
|
||||
*/
|
||||
private val deepTypeVariableDependencies: MutableMap<TypeConstructorMarker, MutableSet<TypeConstructorMarker>> = hashMapOf()
|
||||
|
||||
/*
|
||||
* Not oriented edges
|
||||
* TypeVariable(A) has UPPER(TypeVariable(B)) => A and B are related shallowly
|
||||
*/
|
||||
private val shallowTypeVariableDependencies: MutableMap<TypeConstructorMarker, MutableSet<TypeConstructorMarker>> = hashMapOf()
|
||||
|
||||
// Oriented edges
|
||||
private val postponeArgumentsEdges: MutableMap<TypeConstructorMarker, MutableSet<TypeConstructorMarker>> = hashMapOf()
|
||||
|
||||
private val relatedToAllOutputTypes: MutableSet<TypeConstructorMarker> = hashSetOf()
|
||||
private val relatedToTopLevelType: MutableSet<TypeConstructorMarker> = hashSetOf()
|
||||
|
||||
init {
|
||||
computeConstraintEdges()
|
||||
computePostponeArgumentsEdges()
|
||||
computeRelatedToAllOutputTypes()
|
||||
computeRelatedToTopLevelType()
|
||||
}
|
||||
|
||||
fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) = relatedToTopLevelType.contains(variable)
|
||||
fun isVariableRelatedToAnyOutputType(variable: TypeConstructorMarker) = relatedToAllOutputTypes.contains(variable)
|
||||
|
||||
fun getDeeplyDependentVariables(variable: TypeConstructorMarker) = deepTypeVariableDependencies[variable]
|
||||
fun getShallowlyDependentVariables(variable: TypeConstructorMarker) = shallowTypeVariableDependencies[variable]
|
||||
|
||||
fun areVariablesDependentShallowly(a: TypeConstructorMarker, b: TypeConstructorMarker): Boolean {
|
||||
if (a == b) return true
|
||||
|
||||
val shallowDependencies = shallowTypeVariableDependencies[a] ?: return false
|
||||
|
||||
return shallowDependencies.any { it == b } ||
|
||||
shallowTypeVariableDependencies.values.any { dependencies -> a in dependencies && b in dependencies }
|
||||
}
|
||||
|
||||
private fun computeConstraintEdges() {
|
||||
fun addConstraintEdgeForDeepDependency(from: TypeConstructorMarker, to: TypeConstructorMarker) {
|
||||
deepTypeVariableDependencies.getOrPut(from) { linkedSetOf() }.add(to)
|
||||
deepTypeVariableDependencies.getOrPut(to) { linkedSetOf() }.add(from)
|
||||
}
|
||||
|
||||
fun addConstraintEdgeForShallowDependency(from: TypeConstructorMarker, to: TypeConstructorMarker) {
|
||||
shallowTypeVariableDependencies.getOrPut(from) { linkedSetOf() }.add(to)
|
||||
shallowTypeVariableDependencies.getOrPut(to) { linkedSetOf() }.add(from)
|
||||
}
|
||||
|
||||
for (variableWithConstraints in notFixedTypeVariables.values) {
|
||||
val from = variableWithConstraints.typeVariable.freshTypeConstructor(typeSystemContext)
|
||||
|
||||
for (constraint in variableWithConstraints.constraints) {
|
||||
val constraintTypeConstructor = constraint.type.typeConstructor(typeSystemContext)
|
||||
|
||||
constraint.type.forAllMyTypeVariables {
|
||||
if (isMyTypeVariable(it)) {
|
||||
addConstraintEdgeForDeepDependency(from, it)
|
||||
}
|
||||
}
|
||||
if (isMyTypeVariable(constraintTypeConstructor)) {
|
||||
addConstraintEdgeForShallowDependency(from, constraintTypeConstructor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun computePostponeArgumentsEdges() {
|
||||
fun addPostponeArgumentsEdges(from: TypeConstructorMarker, to: TypeConstructorMarker) {
|
||||
postponeArgumentsEdges.getOrPut(from) { hashSetOf() }.add(to)
|
||||
}
|
||||
|
||||
for (argument in postponedKtPrimitives) {
|
||||
if (argument.analyzed) continue
|
||||
|
||||
val typeVariablesInOutputType = SmartSet.create<TypeConstructorMarker>()
|
||||
(argument.outputType ?: continue).forAllMyTypeVariables { typeVariablesInOutputType.add(it) }
|
||||
if (typeVariablesInOutputType.isEmpty()) continue
|
||||
|
||||
for (inputType in argument.inputTypes) {
|
||||
inputType.forAllMyTypeVariables { from ->
|
||||
for (to in typeVariablesInOutputType) {
|
||||
addPostponeArgumentsEdges(from, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeRelatedToAllOutputTypes() {
|
||||
for (argument in postponedKtPrimitives) {
|
||||
if (argument.analyzed) continue
|
||||
(argument.outputType ?: continue).forAllMyTypeVariables {
|
||||
addAllRelatedNodes(relatedToAllOutputTypes, it, includePostponedEdges = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeRelatedToTopLevelType() {
|
||||
if (topLevelType == null) return
|
||||
topLevelType.forAllMyTypeVariables {
|
||||
addAllRelatedNodes(relatedToTopLevelType, it, includePostponedEdges = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isMyTypeVariable(typeConstructor: TypeConstructorMarker) = notFixedTypeVariables.containsKey(typeConstructor)
|
||||
|
||||
private fun KotlinTypeMarker.forAllMyTypeVariables(action: (TypeConstructorMarker) -> Unit) =
|
||||
with(typeSystemContext) {
|
||||
contains {
|
||||
val typeConstructor = it.typeConstructor()
|
||||
if (isMyTypeVariable(typeConstructor)) action(typeConstructor)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun getConstraintEdges(from: TypeConstructorMarker): Set<TypeConstructorMarker> = deepTypeVariableDependencies[from] ?: emptySet()
|
||||
private fun getPostponeEdges(from: TypeConstructorMarker): Set<TypeConstructorMarker> = postponeArgumentsEdges[from] ?: emptySet()
|
||||
|
||||
private fun addAllRelatedNodes(to: MutableSet<TypeConstructorMarker>, node: TypeConstructorMarker, includePostponedEdges: Boolean) {
|
||||
if (to.add(node)) {
|
||||
for (relatedNode in getConstraintEdges(node)) {
|
||||
addAllRelatedNodes(to, relatedNode, includePostponedEdges)
|
||||
}
|
||||
if (includePostponedEdges) {
|
||||
for (relatedNode in getPostponeEdges(node)) {
|
||||
addAllRelatedNodes(to, relatedNode, includePostponedEdges)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
||||
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.model.FlexibleTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeVariance
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
|
||||
|
||||
private typealias Variable = VariableWithConstraints
|
||||
|
||||
class TypeVariableDirectionCalculator(
|
||||
private val c: VariableFixationFinder.Context,
|
||||
private val postponedKtPrimitives: List<PostponedResolvedAtomMarker>,
|
||||
topLevelType: KotlinTypeMarker
|
||||
) {
|
||||
enum class ResolveDirection {
|
||||
TO_SUBTYPE,
|
||||
TO_SUPERTYPE,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
data class NodeWithDirection(val variableWithConstraints: VariableWithConstraints, val direction: ResolveDirection) {
|
||||
override fun toString() = "$variableWithConstraints to $direction"
|
||||
}
|
||||
|
||||
private val directions = HashMap<Variable, ResolveDirection>()
|
||||
|
||||
init {
|
||||
setupDirections(topLevelType)
|
||||
}
|
||||
|
||||
fun getDirection(typeVariable: Variable): ResolveDirection =
|
||||
directions.getOrDefault(typeVariable, ResolveDirection.UNKNOWN)
|
||||
|
||||
private fun setupDirections(topReturnType: KotlinTypeMarker) {
|
||||
topReturnType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction ->
|
||||
enterToNode(variableWithConstraints, direction)
|
||||
}
|
||||
for (postponedArgument in postponedKtPrimitives) {
|
||||
for (inputType in postponedArgument.inputTypes) {
|
||||
inputType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction ->
|
||||
enterToNode(variableWithConstraints, direction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun enterToNode(variable: Variable, direction: ResolveDirection) {
|
||||
if (direction == ResolveDirection.UNKNOWN) return
|
||||
|
||||
val previous = directions[variable]
|
||||
if (previous != null) {
|
||||
if (previous != direction) {
|
||||
directions[variable] = ResolveDirection.UNKNOWN
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
directions[variable] = direction
|
||||
|
||||
for ((otherVariable, otherDirection) in getConstraintDependencies(variable, direction)) {
|
||||
enterToNode(otherVariable, otherDirection)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConstraintDependencies(
|
||||
variable: Variable,
|
||||
direction: ResolveDirection
|
||||
): List<NodeWithDirection> =
|
||||
SmartList<NodeWithDirection>().also { result ->
|
||||
for (constraint in variable.constraints) {
|
||||
if (!isInterestingConstraint(direction, constraint)) continue
|
||||
|
||||
constraint.type.visitType(direction) { nodeVariable, nodeDirection ->
|
||||
result.add(NodeWithDirection(nodeVariable, nodeDirection))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun isInterestingConstraint(direction: ResolveDirection, constraint: Constraint): Boolean =
|
||||
!(direction == ResolveDirection.TO_SUBTYPE && constraint.kind == ConstraintKind.UPPER) &&
|
||||
!(direction == ResolveDirection.TO_SUPERTYPE && constraint.kind == ConstraintKind.LOWER)
|
||||
|
||||
private fun KotlinTypeMarker.visitType(
|
||||
startDirection: ResolveDirection,
|
||||
action: (variable: Variable, direction: ResolveDirection) -> Unit
|
||||
) = when (this) {
|
||||
is SimpleTypeMarker -> visitType(startDirection, action)
|
||||
is FlexibleTypeMarker -> {
|
||||
with(c) {
|
||||
lowerBound().visitType(startDirection, action)
|
||||
upperBound().visitType(startDirection, action)
|
||||
}
|
||||
}
|
||||
else -> error("?!")
|
||||
}
|
||||
|
||||
private fun SimpleTypeMarker.visitType(
|
||||
startDirection: ResolveDirection,
|
||||
action: (variable: Variable, direction: ResolveDirection) -> Unit
|
||||
): Unit = with(c) {
|
||||
val constructor = typeConstructor()
|
||||
if (constructor.isIntersection()) {
|
||||
constructor.supertypes().forEach {
|
||||
it.visitType(startDirection, action)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (argumentsCount() == 0) {
|
||||
c.notFixedTypeVariables[constructor]?.let {
|
||||
action(it, startDirection)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (constructor.parametersCount() != argumentsCount()) return // incorrect type
|
||||
|
||||
for (index in 0 until constructor.parametersCount()) {
|
||||
val parameter = constructor.getParameter(index)
|
||||
val argument = getArgument(index)
|
||||
if (argument.isStarProjection()) continue
|
||||
|
||||
val variance = AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance()) ?: TypeVariance.INV
|
||||
val innerDirection = when (variance) {
|
||||
TypeVariance.INV -> ResolveDirection.UNKNOWN
|
||||
TypeVariance.OUT -> startDirection
|
||||
TypeVariance.IN -> startDirection.opposite()
|
||||
}
|
||||
|
||||
argument.getType().visitType(innerDirection, action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ResolveDirection.opposite() = when (this) {
|
||||
ResolveDirection.UNKNOWN -> ResolveDirection.UNKNOWN
|
||||
ResolveDirection.TO_SUPERTYPE -> ResolveDirection.TO_SUBTYPE
|
||||
ResolveDirection.TO_SUBTYPE -> ResolveDirection.TO_SUPERTYPE
|
||||
}
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode.PARTIAL
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.DeclaredUpperBoundConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
||||
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
|
||||
class VariableFixationFinder(
|
||||
private val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle
|
||||
) {
|
||||
interface Context : TypeSystemInferenceExtensionContext {
|
||||
val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
|
||||
val postponedTypeVariables: List<TypeVariableMarker>
|
||||
fun isReified(variable: TypeVariableMarker): Boolean
|
||||
}
|
||||
|
||||
data class VariableForFixation(
|
||||
val variable: TypeConstructorMarker,
|
||||
val hasProperConstraint: Boolean,
|
||||
val hasOnlyTrivialProperConstraint: Boolean = false
|
||||
)
|
||||
|
||||
fun findFirstVariableForFixation(
|
||||
c: Context,
|
||||
allTypeVariables: List<TypeConstructorMarker>,
|
||||
postponedKtPrimitives: List<PostponedResolvedAtomMarker>,
|
||||
completionMode: ConstraintSystemCompletionMode,
|
||||
topLevelType: KotlinTypeMarker
|
||||
): VariableForFixation? = c.findTypeVariableForFixation(allTypeVariables, postponedKtPrimitives, completionMode, topLevelType)
|
||||
|
||||
enum class TypeVariableFixationReadiness {
|
||||
FORBIDDEN,
|
||||
WITHOUT_PROPER_ARGUMENT_CONSTRAINT, // proper constraint from arguments -- not from upper bound for type parameters
|
||||
WITH_COMPLEX_DEPENDENCY, // if type variable T has constraint with non fixed type variable inside (non-top-level): T <: Foo<S>
|
||||
WITH_TRIVIAL_OR_NON_PROPER_CONSTRAINTS, // proper trivial constraint from arguments, Nothing <: T
|
||||
RELATED_TO_ANY_OUTPUT_TYPE,
|
||||
FROM_INCORPORATION_OF_DECLARED_UPPER_BOUND,
|
||||
READY_FOR_FIXATION,
|
||||
READY_FOR_FIXATION_REIFIED,
|
||||
}
|
||||
|
||||
private fun Context.getTypeVariableReadiness(
|
||||
variable: TypeConstructorMarker,
|
||||
dependencyProvider: TypeVariableDependencyInformationProvider
|
||||
): TypeVariableFixationReadiness = when {
|
||||
!notFixedTypeVariables.contains(variable) ||
|
||||
dependencyProvider.isVariableRelatedToTopLevelType(variable) -> TypeVariableFixationReadiness.FORBIDDEN
|
||||
!variableHasProperArgumentConstraints(variable) -> TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT
|
||||
hasDependencyToOtherTypeVariables(variable) -> TypeVariableFixationReadiness.WITH_COMPLEX_DEPENDENCY
|
||||
variableHasTrivialOrNonProperConstraints(variable) -> TypeVariableFixationReadiness.WITH_TRIVIAL_OR_NON_PROPER_CONSTRAINTS
|
||||
dependencyProvider.isVariableRelatedToAnyOutputType(variable) -> TypeVariableFixationReadiness.RELATED_TO_ANY_OUTPUT_TYPE
|
||||
variableHasOnlyIncorporatedConstraintsFromDeclaredUpperBound(variable) ->
|
||||
TypeVariableFixationReadiness.FROM_INCORPORATION_OF_DECLARED_UPPER_BOUND
|
||||
isReified(variable) -> TypeVariableFixationReadiness.READY_FOR_FIXATION_REIFIED
|
||||
else -> TypeVariableFixationReadiness.READY_FOR_FIXATION
|
||||
}
|
||||
|
||||
fun isTypeVariableHasProperConstraint(context: Context, typeVariable: TypeConstructorMarker): Boolean {
|
||||
return with(context) {
|
||||
val dependencyProvider = TypeVariableDependencyInformationProvider(
|
||||
notFixedTypeVariables, emptyList(), topLevelType = null, context
|
||||
)
|
||||
when (getTypeVariableReadiness(typeVariable, dependencyProvider)) {
|
||||
TypeVariableFixationReadiness.FORBIDDEN, TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.variableHasTrivialOrNonProperConstraints(variable: TypeConstructorMarker): Boolean {
|
||||
return notFixedTypeVariables[variable]?.constraints?.all { constraint ->
|
||||
val isProperConstraint = isProperArgumentConstraint(constraint)
|
||||
isProperConstraint && trivialConstraintTypeInferenceOracle.isNotInterestingConstraint(constraint) || !isProperConstraint
|
||||
} ?: false
|
||||
}
|
||||
|
||||
private fun Context.variableHasOnlyIncorporatedConstraintsFromDeclaredUpperBound(variable: TypeConstructorMarker): Boolean {
|
||||
val constraints = notFixedTypeVariables[variable]?.constraints ?: return false
|
||||
|
||||
return constraints.filter { isProperArgumentConstraint(it) }.all { it.position.isFromDeclaredUpperBound }
|
||||
}
|
||||
|
||||
private fun Context.findTypeVariableForFixation(
|
||||
allTypeVariables: List<TypeConstructorMarker>,
|
||||
postponedArguments: List<PostponedResolvedAtomMarker>,
|
||||
completionMode: ConstraintSystemCompletionMode,
|
||||
topLevelType: KotlinTypeMarker
|
||||
): VariableForFixation? {
|
||||
if (allTypeVariables.isEmpty()) return null
|
||||
|
||||
val dependencyProvider = TypeVariableDependencyInformationProvider(
|
||||
notFixedTypeVariables, postponedArguments, topLevelType.takeIf { completionMode == PARTIAL }, this
|
||||
)
|
||||
|
||||
val candidate = allTypeVariables.maxByOrNull { getTypeVariableReadiness(it, dependencyProvider) } ?: return null
|
||||
|
||||
return when (getTypeVariableReadiness(candidate, dependencyProvider)) {
|
||||
TypeVariableFixationReadiness.FORBIDDEN -> null
|
||||
TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT -> VariableForFixation(candidate, false)
|
||||
TypeVariableFixationReadiness.WITH_TRIVIAL_OR_NON_PROPER_CONSTRAINTS ->
|
||||
VariableForFixation(candidate, hasProperConstraint = true, hasOnlyTrivialProperConstraint = true)
|
||||
|
||||
else -> VariableForFixation(candidate, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.hasDependencyToOtherTypeVariables(typeVariable: TypeConstructorMarker): Boolean {
|
||||
for (constraint in notFixedTypeVariables[typeVariable]?.constraints ?: return false) {
|
||||
val dependencyPresenceCondition = { type: KotlinTypeMarker ->
|
||||
type.typeConstructor() != typeVariable && notFixedTypeVariables.containsKey(type.typeConstructor())
|
||||
}
|
||||
if (constraint.type.lowerBoundIfFlexible().argumentsCount() != 0 && constraint.type.contains(dependencyPresenceCondition))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun Context.variableHasProperArgumentConstraints(variable: TypeConstructorMarker): Boolean =
|
||||
notFixedTypeVariables[variable]?.constraints?.any { isProperArgumentConstraint(it) } ?: false
|
||||
|
||||
private fun Context.isProperArgumentConstraint(c: Constraint) =
|
||||
isProperType(c.type)
|
||||
&& c.position.initialConstraint.position !is DeclaredUpperBoundConstraintPosition<*>
|
||||
&& !c.isNullabilityConstraint
|
||||
|
||||
private fun Context.isProperType(type: KotlinTypeMarker): Boolean =
|
||||
isProperTypeForFixation(type) { t -> !t.contains { notFixedTypeVariables.containsKey(it.typeConstructor()) } }
|
||||
|
||||
private fun Context.isReified(variable: TypeConstructorMarker): Boolean =
|
||||
notFixedTypeVariables[variable]?.typeVariable?.let { isReified(it) } ?: false
|
||||
}
|
||||
|
||||
inline fun TypeSystemInferenceExtensionContext.isProperTypeForFixation(
|
||||
type: KotlinTypeMarker,
|
||||
isProper: (KotlinTypeMarker) -> Boolean
|
||||
): Boolean {
|
||||
if (!isProper(type)) return false
|
||||
if (type.isCapturedType()) {
|
||||
val projection = (type as? SimpleTypeMarker)?.asCapturedType()?.typeConstructorProjection() ?: return true
|
||||
if (projection.isStarProjection()) return true
|
||||
|
||||
if (!isProper(projection.getType())) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.model
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.*
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeVariableMarker
|
||||
|
||||
interface OnlyInputTypeConstraintPosition
|
||||
|
||||
sealed class ConstraintPosition
|
||||
|
||||
abstract class ExplicitTypeParameterConstraintPosition<T>(val typeArgument: T) : ConstraintPosition(), OnlyInputTypeConstraintPosition {
|
||||
override fun toString(): String = "TypeParameter $typeArgument"
|
||||
}
|
||||
|
||||
abstract class ExpectedTypeConstraintPosition<T>(val topLevelCall: T) : ConstraintPosition(), OnlyInputTypeConstraintPosition {
|
||||
override fun toString(): String = "ExpectedType for call $topLevelCall"
|
||||
}
|
||||
|
||||
abstract class DeclaredUpperBoundConstraintPosition<T>(val typeParameter: T) : ConstraintPosition() {
|
||||
override fun toString(): String = "DeclaredUpperBound $typeParameter"
|
||||
}
|
||||
|
||||
abstract class ArgumentConstraintPosition<T>(val argument: T) : ConstraintPosition(), OnlyInputTypeConstraintPosition {
|
||||
override fun toString(): String = "Argument $argument"
|
||||
}
|
||||
|
||||
abstract class ReceiverConstraintPosition<T>(val argument: T) : ConstraintPosition(), OnlyInputTypeConstraintPosition {
|
||||
override fun toString(): String = "Receiver $argument"
|
||||
}
|
||||
|
||||
abstract class FixVariableConstraintPosition<T>(val variable: TypeVariableMarker, val resolvedAtom: T) : ConstraintPosition() {
|
||||
override fun toString(): String = "Fix variable $variable"
|
||||
}
|
||||
|
||||
abstract class KnownTypeParameterConstraintPosition<T : KotlinTypeMarker>(val typeArgument: T) : ConstraintPosition() {
|
||||
override fun toString(): String = "TypeArgument $typeArgument"
|
||||
}
|
||||
|
||||
abstract class LHSArgumentConstraintPosition<T, R>(
|
||||
val argument: T,
|
||||
val receiver: R
|
||||
) : ConstraintPosition() {
|
||||
override fun toString(): String {
|
||||
return "LHS receiver $receiver"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class LambdaArgumentConstraintPosition<T>(val lambda: T) : ConstraintPosition() {
|
||||
override fun toString(): String {
|
||||
return "LambdaArgument $lambda"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DelegatedPropertyConstraintPosition<T>(val topLevelCall: T) : ConstraintPosition() {
|
||||
override fun toString(): String = "Constraint from call $topLevelCall for delegated property"
|
||||
}
|
||||
|
||||
data class IncorporationConstraintPosition(
|
||||
val from: ConstraintPosition,
|
||||
val initialConstraint: InitialConstraint,
|
||||
var isFromDeclaredUpperBound: Boolean = false
|
||||
) : ConstraintPosition() {
|
||||
override fun toString(): String = "Incorporate $initialConstraint from position $from"
|
||||
}
|
||||
|
||||
object CoroutinePosition : ConstraintPosition() {
|
||||
override fun toString(): String = "for coroutine call"
|
||||
}
|
||||
|
||||
// TODO: should be used only in SimpleConstraintSystemImpl
|
||||
object SimpleConstraintSystemConstraintPosition : ConstraintPosition()
|
||||
|
||||
// ------------------------------------------------ Errors ------------------------------------------------
|
||||
|
||||
sealed class ConstraintSystemError(val applicability: ResolutionCandidateApplicability)
|
||||
|
||||
class NewConstraintError(
|
||||
val lowerType: KotlinTypeMarker,
|
||||
val upperType: KotlinTypeMarker,
|
||||
val position: IncorporationConstraintPosition
|
||||
) : ConstraintSystemError(if (position.from is ReceiverConstraintPosition<*>) INAPPLICABLE_WRONG_RECEIVER else INAPPLICABLE)
|
||||
|
||||
class CapturedTypeFromSubtyping(
|
||||
val typeVariable: TypeVariableMarker,
|
||||
val constraintType: KotlinTypeMarker,
|
||||
val position: ConstraintPosition
|
||||
) : ConstraintSystemError(INAPPLICABLE)
|
||||
|
||||
abstract class NotEnoughInformationForTypeParameter<T>(
|
||||
val typeVariable: TypeVariableMarker,
|
||||
val resolvedAtom: T
|
||||
) : ConstraintSystemError(INAPPLICABLE)
|
||||
|
||||
class ConstrainingTypeIsError(
|
||||
val typeVariable: TypeVariableMarker,
|
||||
val constraintType: KotlinTypeMarker,
|
||||
val position: IncorporationConstraintPosition
|
||||
) : ConstraintSystemError(INAPPLICABLE)
|
||||
|
||||
class OnlyInputTypesDiagnostic(val typeVariable: TypeVariableMarker) : ConstraintSystemError(INAPPLICABLE)
|
||||
|
||||
object LowerPriorityToPreserveCompatibility : ConstraintSystemError(RESOLVED_NEED_PRESERVE_COMPATIBILITY)
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.model
|
||||
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
|
||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeVariableMarker
|
||||
|
||||
/**
|
||||
* Every type variable can be in the following states:
|
||||
* - not fixed => there is several constraints for this type variable(possible no one).
|
||||
* for this type variable we have VariableWithConstraints in map notFixedTypeVariables
|
||||
* - fixed to proper type or not proper type. For such type variable there is no VariableWithConstraints in notFixedTypeVariables.
|
||||
* Also we should guaranty that there is no other constraints in other VariableWithConstraints which depends on this fixed type variable.
|
||||
*
|
||||
* Note: fixedTypeVariables can contains a proper and not proper type.
|
||||
*
|
||||
* Fixing procedure(to proper types). First of all we should determinate fixing order.
|
||||
* After it, for every type variable we do the following:
|
||||
* - determinate result proper type
|
||||
* - add equality constraint, for example: T = Int
|
||||
* - run incorporation and generate all new constraints
|
||||
* - after is we remove VariableWithConstraints for type variable T from map notFixedTypeVariables
|
||||
* - also we remove all constraint in other variable which contains T
|
||||
* - add result type to fixedTypeVariables.
|
||||
*
|
||||
* Note fixing procedure to not proper type the same. The only difference in determination result type.
|
||||
*
|
||||
*/
|
||||
|
||||
interface ConstraintStorage {
|
||||
val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
|
||||
val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
|
||||
val initialConstraints: List<InitialConstraint>
|
||||
val maxTypeDepthFromInitialConstraints: Int
|
||||
val errors: List<ConstraintSystemError>
|
||||
val hasContradiction: Boolean
|
||||
val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
|
||||
val postponedTypeVariables: List<TypeVariableMarker>
|
||||
|
||||
object Empty : ConstraintStorage {
|
||||
override val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker> get() = emptyMap()
|
||||
override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints> get() = emptyMap()
|
||||
override val initialConstraints: List<InitialConstraint> get() = emptyList()
|
||||
override val maxTypeDepthFromInitialConstraints: Int get() = 1
|
||||
override val errors: List<ConstraintSystemError> get() = emptyList()
|
||||
override val hasContradiction: Boolean get() = false
|
||||
override val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker> get() = emptyMap()
|
||||
override val postponedTypeVariables: List<TypeVariableMarker> get() = emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
enum class ConstraintKind {
|
||||
LOWER,
|
||||
UPPER,
|
||||
EQUALITY;
|
||||
|
||||
fun isLower(): Boolean = this == LOWER
|
||||
fun isUpper(): Boolean = this == UPPER
|
||||
fun isEqual(): Boolean = this == EQUALITY
|
||||
|
||||
fun opposite() = when (this) {
|
||||
LOWER -> UPPER
|
||||
UPPER -> LOWER
|
||||
EQUALITY -> EQUALITY
|
||||
}
|
||||
}
|
||||
|
||||
class Constraint(
|
||||
val kind: ConstraintKind,
|
||||
val type: KotlinTypeMarker, // flexible types here is allowed
|
||||
val position: IncorporationConstraintPosition,
|
||||
val typeHashCode: Int = type.hashCode(),
|
||||
val derivedFrom: Set<TypeVariableMarker>,
|
||||
val isNullabilityConstraint: Boolean,
|
||||
val inputTypePositionBeforeIncorporation: OnlyInputTypeConstraintPosition? = null
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other?.javaClass != javaClass) return false
|
||||
|
||||
other as Constraint
|
||||
|
||||
if (typeHashCode != other.typeHashCode) return false
|
||||
if (kind != other.kind) return false
|
||||
if (position != other.position) return false
|
||||
if (type != other.type) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = typeHashCode
|
||||
|
||||
override fun toString() = "$kind($type) from $position"
|
||||
}
|
||||
|
||||
interface VariableWithConstraints {
|
||||
val typeVariable: TypeVariableMarker
|
||||
val constraints: List<Constraint>
|
||||
}
|
||||
|
||||
class InitialConstraint(
|
||||
val a: KotlinTypeMarker,
|
||||
val b: KotlinTypeMarker,
|
||||
val constraintKind: ConstraintKind, // see [checkConstraint]
|
||||
val position: ConstraintPosition
|
||||
) {
|
||||
override fun toString(): String {
|
||||
val sign =
|
||||
when (constraintKind) {
|
||||
ConstraintKind.EQUALITY -> "=="
|
||||
ConstraintKind.LOWER -> ":>"
|
||||
ConstraintKind.UPPER -> "<:"
|
||||
}
|
||||
return "$a $sign $b from $position"
|
||||
}
|
||||
}
|
||||
|
||||
//fun InitialConstraint.checkConstraint(substitutor: TypeSubstitutor): Boolean {
|
||||
// val newA = substitutor.substitute(a)
|
||||
// val newB = substitutor.substitute(b)
|
||||
// return checkConstraint(newB as KotlinTypeMarker, constraintKind, newA as KotlinTypeMarker)
|
||||
//}
|
||||
|
||||
fun checkConstraint(
|
||||
context: TypeCheckerProviderContext,
|
||||
constraintType: KotlinTypeMarker,
|
||||
constraintKind: ConstraintKind,
|
||||
resultType: KotlinTypeMarker
|
||||
): Boolean {
|
||||
|
||||
|
||||
val typeChecker = AbstractTypeChecker
|
||||
return when (constraintKind) {
|
||||
ConstraintKind.EQUALITY -> typeChecker.equalTypes(context, constraintType, resultType)
|
||||
ConstraintKind.LOWER -> typeChecker.isSubtypeOf(context, constraintType, resultType)
|
||||
ConstraintKind.UPPER -> typeChecker.isSubtypeOf(context, resultType, constraintType)
|
||||
}
|
||||
}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.model
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemUtilContext
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.trimToSize
|
||||
|
||||
private typealias Context = TypeSystemInferenceExtensionContext
|
||||
|
||||
class MutableVariableWithConstraints private constructor(
|
||||
private val context: Context,
|
||||
override val typeVariable: TypeVariableMarker,
|
||||
constraints: List<Constraint>? // assume simplified and deduplicated
|
||||
) : VariableWithConstraints {
|
||||
|
||||
constructor(context: Context, typeVariable: TypeVariableMarker) : this(context, typeVariable, null)
|
||||
|
||||
constructor(context: Context, other: VariableWithConstraints) : this(context, other.typeVariable, other.constraints)
|
||||
|
||||
override val constraints: List<Constraint>
|
||||
get() {
|
||||
if (simplifiedConstraints == null) {
|
||||
simplifiedConstraints = mutableConstraints.simplifyConstraints()
|
||||
}
|
||||
return simplifiedConstraints!!
|
||||
}
|
||||
|
||||
// see @OnlyInputTypes annotation
|
||||
fun getProjectedInputCallTypes(utilContext: ConstraintSystemUtilContext): Collection<KotlinTypeMarker> {
|
||||
return with(utilContext) {
|
||||
mutableConstraints
|
||||
.mapNotNullTo(SmartList()) {
|
||||
if (it.position.from is OnlyInputTypeConstraintPosition || it.inputTypePositionBeforeIncorporation != null)
|
||||
it.type.unCapture()
|
||||
else null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val mutableConstraints = if (constraints == null) SmartList() else SmartList(constraints)
|
||||
|
||||
private var simplifiedConstraints: SmartList<Constraint>? = mutableConstraints
|
||||
|
||||
// return new actual constraint, if this constraint is new
|
||||
fun addConstraint(constraint: Constraint): Constraint? {
|
||||
val isLowerAndFlexibleTypeWithDefNotNullLowerBound = constraint.isLowerAndFlexibleTypeWithDefNotNullLowerBound()
|
||||
|
||||
for (previousConstraint in constraints) {
|
||||
if (previousConstraint.typeHashCode == constraint.typeHashCode
|
||||
&& previousConstraint.type == constraint.type
|
||||
&& previousConstraint.isNullabilityConstraint == constraint.isNullabilityConstraint
|
||||
) {
|
||||
if (newConstraintIsUseless(previousConstraint, constraint)) return null
|
||||
val isMatchingForSimplification = when (previousConstraint.kind) {
|
||||
ConstraintKind.LOWER -> constraint.kind.isUpper()
|
||||
ConstraintKind.UPPER -> constraint.kind.isLower()
|
||||
ConstraintKind.EQUALITY -> true
|
||||
}
|
||||
if (isMatchingForSimplification) {
|
||||
val actualConstraint = Constraint(
|
||||
ConstraintKind.EQUALITY,
|
||||
constraint.type,
|
||||
constraint.position,
|
||||
constraint.typeHashCode,
|
||||
derivedFrom = constraint.derivedFrom,
|
||||
isNullabilityConstraint = false
|
||||
)
|
||||
mutableConstraints.add(actualConstraint)
|
||||
simplifiedConstraints = null
|
||||
return actualConstraint
|
||||
}
|
||||
}
|
||||
|
||||
if (isLowerAndFlexibleTypeWithDefNotNullLowerBound &&
|
||||
previousConstraint.isStrongerThanLowerAndFlexibleTypeWithDefNotNullLowerBound(constraint)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
mutableConstraints.add(constraint)
|
||||
if (simplifiedConstraints != null && simplifiedConstraints !== mutableConstraints) {
|
||||
simplifiedConstraints!!.add(constraint)
|
||||
}
|
||||
|
||||
if (simplifiedConstraints != null && isLowerAndFlexibleTypeWithDefNotNullLowerBound) {
|
||||
simplifiedConstraints = null
|
||||
}
|
||||
|
||||
return constraint
|
||||
}
|
||||
|
||||
// This method should be used only for transaction in constraint system
|
||||
// shouldRemove should give true only for tail elements
|
||||
internal fun removeLastConstraints(shouldRemove: (Constraint) -> Boolean) {
|
||||
mutableConstraints.trimToSize(mutableConstraints.indexOfLast { !shouldRemove(it) } + 1)
|
||||
if (simplifiedConstraints !== mutableConstraints) {
|
||||
simplifiedConstraints = null
|
||||
}
|
||||
}
|
||||
|
||||
// This method should be used only when constraint system has state COMPLETION
|
||||
internal fun removeConstrains(shouldRemove: (Constraint) -> Boolean) {
|
||||
mutableConstraints.removeAll(shouldRemove)
|
||||
if (simplifiedConstraints !== mutableConstraints) {
|
||||
simplifiedConstraints = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun newConstraintIsUseless(old: Constraint, new: Constraint): Boolean {
|
||||
// Constraints from declared upper bound are quite special -- they aren't considered as a proper ones
|
||||
// In other words, user-defined constraints have "higher" priority and here we're trying not to loose them
|
||||
if (old.position.from is DeclaredUpperBoundConstraintPosition<*> && new.position.from !is DeclaredUpperBoundConstraintPosition<*>)
|
||||
return false
|
||||
|
||||
return when (old.kind) {
|
||||
ConstraintKind.EQUALITY -> true
|
||||
ConstraintKind.LOWER -> new.kind.isLower()
|
||||
ConstraintKind.UPPER -> new.kind.isUpper()
|
||||
}
|
||||
}
|
||||
|
||||
private fun SmartList<Constraint>.simplifyConstraints(): SmartList<Constraint> =
|
||||
simplifyLowerConstraints().simplifyEqualityConstraints()
|
||||
|
||||
private fun SmartList<Constraint>.simplifyLowerConstraints(): SmartList<Constraint> {
|
||||
val usefulConstraints = SmartList<Constraint>()
|
||||
for (constraint in this) {
|
||||
if (!constraint.isLowerAndFlexibleTypeWithDefNotNullLowerBound()) {
|
||||
usefulConstraints.add(constraint)
|
||||
continue
|
||||
}
|
||||
|
||||
// Now we have to check that some constraint T!!.T? <: K is useless or not
|
||||
// If there is constraint T..T? <: K, then the original one (T!!.T?) is useless
|
||||
// This is so because CST(T..T?, T!!..T?) == CST(T..T?)
|
||||
|
||||
val thereIsStrongerConstraint = this.any { it.isStrongerThanLowerAndFlexibleTypeWithDefNotNullLowerBound(constraint) }
|
||||
|
||||
if (!thereIsStrongerConstraint) {
|
||||
usefulConstraints.add(constraint)
|
||||
}
|
||||
}
|
||||
|
||||
return usefulConstraints
|
||||
}
|
||||
|
||||
// Such constraint is applicable for simplification
|
||||
private fun Constraint.isLowerAndFlexibleTypeWithDefNotNullLowerBound(): Boolean {
|
||||
return with(context) {
|
||||
kind == ConstraintKind.LOWER && type.isFlexible() && type.lowerBoundIfFlexible().isDefinitelyNotNullType()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Constraint.isStrongerThanLowerAndFlexibleTypeWithDefNotNullLowerBound(other: Constraint): Boolean {
|
||||
if (this === other) return false
|
||||
|
||||
if (typeHashCode != other.typeHashCode || kind == ConstraintKind.UPPER) return false
|
||||
with(context) {
|
||||
if (!type.isFlexible() || !other.type.isFlexible()) return false
|
||||
val otherLowerBound = other.type.lowerBoundIfFlexible()
|
||||
if (!otherLowerBound.isDefinitelyNotNullType()) return false
|
||||
require(otherLowerBound is DefinitelyNotNullTypeMarker)
|
||||
val thisLowerBound = type.lowerBoundIfFlexible()
|
||||
val thisUpperBound = type.upperBoundIfFlexible()
|
||||
val otherUpperBound = other.type.upperBoundIfFlexible()
|
||||
return thisLowerBound == otherLowerBound.original() && thisUpperBound == otherUpperBound
|
||||
}
|
||||
}
|
||||
|
||||
private fun SmartList<Constraint>.simplifyEqualityConstraints(): SmartList<Constraint> {
|
||||
val equalityConstraints = filter { it.kind == ConstraintKind.EQUALITY }.groupBy { it.typeHashCode }
|
||||
return when {
|
||||
equalityConstraints.isEmpty() -> this
|
||||
else -> filterTo(SmartList()) { isUsefulConstraint(it, equalityConstraints) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun isUsefulConstraint(constraint: Constraint, equalityConstraints: Map<Int, List<Constraint>>): Boolean {
|
||||
if (constraint.kind == ConstraintKind.EQUALITY) return true
|
||||
return equalityConstraints[constraint.typeHashCode]?.none { it.type == constraint.type } ?: true
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Constraints for $typeVariable"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class MutableConstraintStorage : ConstraintStorage {
|
||||
override val allTypeVariables: MutableMap<TypeConstructorMarker, TypeVariableMarker> = LinkedHashMap()
|
||||
override val notFixedTypeVariables: MutableMap<TypeConstructorMarker, MutableVariableWithConstraints> = LinkedHashMap()
|
||||
override val initialConstraints: MutableList<InitialConstraint> = SmartList()
|
||||
override var maxTypeDepthFromInitialConstraints: Int = 1
|
||||
override val errors: MutableList<ConstraintSystemError> = SmartList()
|
||||
override val hasContradiction: Boolean get() = errors.any { !it.applicability.isSuccess }
|
||||
override val fixedTypeVariables: MutableMap<TypeConstructorMarker, KotlinTypeMarker> = LinkedHashMap()
|
||||
override val postponedTypeVariables: MutableList<TypeVariableMarker> = SmartList()
|
||||
}
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.model
|
||||
|
||||
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.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.trimToSize
|
||||
import kotlin.math.max
|
||||
|
||||
class NewConstraintSystemImpl(
|
||||
private val constraintInjector: ConstraintInjector,
|
||||
val typeSystemContext: TypeSystemInferenceExtensionContext
|
||||
) : TypeSystemInferenceExtensionContext by typeSystemContext,
|
||||
NewConstraintSystem,
|
||||
ConstraintSystemBuilder,
|
||||
ConstraintInjector.Context,
|
||||
ResultTypeResolver.Context,
|
||||
ConstraintSystemCompletionContext,
|
||||
PostponedArgumentsAnalyzerContext
|
||||
{
|
||||
private val utilContext = constraintInjector.constraintIncorporator.utilContext
|
||||
|
||||
private val storage = MutableConstraintStorage()
|
||||
private var state = State.BUILDING
|
||||
private val typeVariablesTransaction: MutableList<TypeVariableMarker> = SmartList()
|
||||
private val properTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
|
||||
private val notProperTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
|
||||
|
||||
private enum class State {
|
||||
BUILDING,
|
||||
TRANSACTION,
|
||||
FREEZED,
|
||||
COMPLETION
|
||||
}
|
||||
|
||||
private fun checkState(a: State) {
|
||||
if (!AbstractTypeChecker.RUN_SLOW_ASSERTIONS) return
|
||||
checkState(*arrayOf(a))
|
||||
}
|
||||
|
||||
private fun checkState(a: State, b: State) {
|
||||
if (!AbstractTypeChecker.RUN_SLOW_ASSERTIONS) return
|
||||
checkState(*arrayOf(a, b))
|
||||
}
|
||||
|
||||
private fun checkState(a: State, b: State, c: State) {
|
||||
if (!AbstractTypeChecker.RUN_SLOW_ASSERTIONS) return
|
||||
checkState(*arrayOf(a, b, c))
|
||||
}
|
||||
|
||||
private fun checkState(a: State, b: State, c: State, d: State) {
|
||||
if (!AbstractTypeChecker.RUN_SLOW_ASSERTIONS) return
|
||||
checkState(*arrayOf(a, b, c, d))
|
||||
}
|
||||
|
||||
private fun checkState(vararg allowedState: State) {
|
||||
if (!AbstractTypeChecker.RUN_SLOW_ASSERTIONS) return
|
||||
assert(state in allowedState) {
|
||||
"State $state is not allowed. AllowedStates: ${allowedState.joinToString()}"
|
||||
}
|
||||
}
|
||||
|
||||
override val errors: List<ConstraintSystemError>
|
||||
get() = storage.errors
|
||||
|
||||
override fun getBuilder() = apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) }
|
||||
|
||||
override fun asReadOnlyStorage(): ConstraintStorage {
|
||||
checkState(State.BUILDING, State.FREEZED)
|
||||
state = State.FREEZED
|
||||
return storage
|
||||
}
|
||||
|
||||
override fun asConstraintSystemCompleterContext() = apply { checkState(State.BUILDING) }
|
||||
|
||||
override fun asPostponedArgumentsAnalyzerContext() = apply { checkState(State.BUILDING) }
|
||||
|
||||
override fun asConstraintSystemCompletionContext(): ConstraintSystemCompletionContext = apply { checkState(State.BUILDING) }
|
||||
|
||||
// ConstraintSystemOperation
|
||||
override fun registerVariable(variable: TypeVariableMarker) {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
|
||||
transactionRegisterVariable(variable)
|
||||
storage.allTypeVariables[variable.freshTypeConstructor()] = variable
|
||||
notProperTypesCache.clear()
|
||||
storage.notFixedTypeVariables[variable.freshTypeConstructor()] = MutableVariableWithConstraints(this, variable)
|
||||
}
|
||||
|
||||
override fun markPostponedVariable(variable: TypeVariableMarker) {
|
||||
storage.postponedTypeVariables += variable
|
||||
}
|
||||
|
||||
override fun unmarkPostponedVariable(variable: TypeVariableMarker) {
|
||||
storage.postponedTypeVariables -= variable
|
||||
}
|
||||
|
||||
override fun removePostponedVariables() {
|
||||
storage.postponedTypeVariables.clear()
|
||||
}
|
||||
|
||||
override fun addSubtypeConstraint(lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, position: ConstraintPosition) =
|
||||
constraintInjector.addInitialSubtypeConstraint(
|
||||
apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) },
|
||||
lowerType,
|
||||
upperType,
|
||||
position
|
||||
)
|
||||
|
||||
override fun addEqualityConstraint(a: KotlinTypeMarker, b: KotlinTypeMarker, position: ConstraintPosition) =
|
||||
constraintInjector.addInitialEqualityConstraint(
|
||||
apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) },
|
||||
a,
|
||||
b,
|
||||
position
|
||||
)
|
||||
|
||||
override fun getProperSuperTypeConstructors(type: KotlinTypeMarker): List<TypeConstructorMarker> {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
val variableWithConstraints = notFixedTypeVariables[type.typeConstructor()] ?: return listOf(type.typeConstructor())
|
||||
|
||||
return variableWithConstraints.constraints.mapNotNull {
|
||||
if (it.kind == ConstraintKind.LOWER) return@mapNotNull null
|
||||
it.type.typeConstructor().takeUnless { allTypeVariables.containsKey(it) }
|
||||
}
|
||||
}
|
||||
|
||||
// ConstraintSystemBuilder
|
||||
private fun transactionRegisterVariable(variable: TypeVariableMarker) {
|
||||
if (state != State.TRANSACTION) return
|
||||
typeVariablesTransaction.add(variable)
|
||||
}
|
||||
|
||||
private fun closeTransaction(beforeState: State, beforeTypeVariables: Int) {
|
||||
checkState(State.TRANSACTION)
|
||||
typeVariablesTransaction.trimToSize(beforeTypeVariables)
|
||||
state = beforeState
|
||||
}
|
||||
|
||||
override fun runTransaction(runOperations: ConstraintSystemOperation.() -> Boolean): Boolean {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
val beforeState = state
|
||||
val beforeInitialConstraintCount = storage.initialConstraints.size
|
||||
val beforeErrorsCount = storage.errors.size
|
||||
val beforeMaxTypeDepthFromInitialConstraints = storage.maxTypeDepthFromInitialConstraints
|
||||
val beforeTypeVariablesTransactionSize = typeVariablesTransaction.size
|
||||
|
||||
state = State.TRANSACTION
|
||||
// typeVariablesTransaction is clear
|
||||
if (runOperations()) {
|
||||
closeTransaction(beforeState, beforeTypeVariablesTransactionSize)
|
||||
return true
|
||||
}
|
||||
|
||||
for (addedTypeVariable in typeVariablesTransaction.subList(beforeTypeVariablesTransactionSize, typeVariablesTransaction.size)) {
|
||||
storage.allTypeVariables.remove(addedTypeVariable.freshTypeConstructor())
|
||||
storage.notFixedTypeVariables.remove(addedTypeVariable.freshTypeConstructor())
|
||||
}
|
||||
storage.maxTypeDepthFromInitialConstraints = beforeMaxTypeDepthFromInitialConstraints
|
||||
storage.errors.trimToSize(beforeErrorsCount)
|
||||
|
||||
val addedInitialConstraints = storage.initialConstraints.subList(beforeInitialConstraintCount, storage.initialConstraints.size)
|
||||
|
||||
val shouldRemove = { c: Constraint -> addedInitialConstraints.contains(c.position.initialConstraint) }
|
||||
|
||||
for (variableWithConstraint in storage.notFixedTypeVariables.values) {
|
||||
variableWithConstraint.removeLastConstraints(shouldRemove)
|
||||
}
|
||||
|
||||
addedInitialConstraints.clear() // remove constraint from storage.initialConstraints
|
||||
closeTransaction(beforeState, beforeTypeVariablesTransactionSize)
|
||||
return false
|
||||
}
|
||||
|
||||
// ConstraintSystemBuilder, KotlinConstraintSystemCompleter.Context
|
||||
override val hasContradiction: Boolean
|
||||
get() = storage.hasContradiction.also {
|
||||
checkState(
|
||||
State.FREEZED,
|
||||
State.BUILDING,
|
||||
State.COMPLETION,
|
||||
State.TRANSACTION
|
||||
)
|
||||
}
|
||||
|
||||
override fun addOtherSystem(otherSystem: ConstraintStorage) {
|
||||
if (otherSystem.allTypeVariables.isNotEmpty()) {
|
||||
otherSystem.allTypeVariables.forEach {
|
||||
transactionRegisterVariable(it.value)
|
||||
}
|
||||
storage.allTypeVariables.putAll(otherSystem.allTypeVariables)
|
||||
notProperTypesCache.clear()
|
||||
}
|
||||
for ((variable, constraints) in otherSystem.notFixedTypeVariables) {
|
||||
notFixedTypeVariables[variable] = MutableVariableWithConstraints(this, constraints)
|
||||
}
|
||||
storage.initialConstraints.addAll(otherSystem.initialConstraints)
|
||||
storage.maxTypeDepthFromInitialConstraints =
|
||||
max(storage.maxTypeDepthFromInitialConstraints, otherSystem.maxTypeDepthFromInitialConstraints)
|
||||
storage.errors.addAll(otherSystem.errors)
|
||||
storage.fixedTypeVariables.putAll(otherSystem.fixedTypeVariables)
|
||||
storage.postponedTypeVariables.addAll(otherSystem.postponedTypeVariables)
|
||||
}
|
||||
|
||||
// ResultTypeResolver.Context, ConstraintSystemBuilder
|
||||
override fun isProperType(type: KotlinTypeMarker): Boolean {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
if (storage.allTypeVariables.isEmpty()) return true
|
||||
if (notProperTypesCache.contains(type)) return false
|
||||
if (properTypesCache.contains(type)) return true
|
||||
return isProperTypeImpl(type).also {
|
||||
(if (it) properTypesCache else notProperTypesCache).add(type)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isProperTypeImpl(type: KotlinTypeMarker): Boolean =
|
||||
!type.contains {
|
||||
val capturedType = it.asSimpleType()?.asCapturedType()
|
||||
// TODO: change NewCapturedType to markered one for FE-IR
|
||||
val typeToCheck = if (capturedType is CapturedTypeMarker && capturedType.captureStatus() == CaptureStatus.FROM_EXPRESSION)
|
||||
capturedType.typeConstructorProjection().takeUnless { projection -> projection.isStarProjection() }?.getType()
|
||||
else
|
||||
it
|
||||
|
||||
if (typeToCheck == null) return@contains false
|
||||
|
||||
storage.allTypeVariables.containsKey(typeToCheck.typeConstructor())
|
||||
}
|
||||
|
||||
override fun isTypeVariable(type: KotlinTypeMarker): Boolean {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
return notFixedTypeVariables.containsKey(type.typeConstructor())
|
||||
}
|
||||
|
||||
override fun isPostponedTypeVariable(typeVariable: TypeVariableMarker): Boolean {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
return typeVariable in postponedTypeVariables
|
||||
}
|
||||
|
||||
// ConstraintInjector.Context, KotlinConstraintSystemCompleter.Context
|
||||
override val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
|
||||
get() {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
return storage.allTypeVariables
|
||||
}
|
||||
|
||||
override var maxTypeDepthFromInitialConstraints: Int
|
||||
get() = storage.maxTypeDepthFromInitialConstraints
|
||||
set(value) {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
storage.maxTypeDepthFromInitialConstraints = value
|
||||
}
|
||||
|
||||
override fun addInitialConstraint(initialConstraint: InitialConstraint) {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
storage.initialConstraints.add(initialConstraint)
|
||||
}
|
||||
|
||||
// ConstraintInjector.Context, FixationOrderCalculator.Context
|
||||
override val notFixedTypeVariables: MutableMap<TypeConstructorMarker, MutableVariableWithConstraints>
|
||||
get() {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
return storage.notFixedTypeVariables
|
||||
}
|
||||
|
||||
override val fixedTypeVariables: MutableMap<TypeConstructorMarker, KotlinTypeMarker>
|
||||
get() {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
return storage.fixedTypeVariables
|
||||
}
|
||||
|
||||
override val postponedTypeVariables: List<TypeVariableMarker>
|
||||
get() {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
return storage.postponedTypeVariables
|
||||
}
|
||||
|
||||
// ConstraintInjector.Context, KotlinConstraintSystemCompleter.Context
|
||||
override fun addError(error: ConstraintSystemError) {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
storage.errors.add(error)
|
||||
}
|
||||
|
||||
// KotlinConstraintSystemCompleter.Context
|
||||
// TODO: simplify this: do only substitution a fixing type variable rather than running of subtyping and full incorporation
|
||||
override fun fixVariable(variable: TypeVariableMarker, resultType: KotlinTypeMarker, position: FixVariableConstraintPosition<*>) {
|
||||
checkState(State.BUILDING, State.COMPLETION)
|
||||
|
||||
constraintInjector.addInitialEqualityConstraint(
|
||||
this, variable.defaultType(), resultType, position
|
||||
)
|
||||
|
||||
val freshTypeConstructor = variable.freshTypeConstructor()
|
||||
|
||||
val variableWithConstraints = notFixedTypeVariables.remove(freshTypeConstructor)
|
||||
checkOnlyInputTypesAnnotation(variableWithConstraints, resultType)
|
||||
|
||||
for (variableWithConstraint in notFixedTypeVariables.values) {
|
||||
variableWithConstraint.removeConstrains {
|
||||
it.type.contains { it.typeConstructor() == freshTypeConstructor }
|
||||
}
|
||||
}
|
||||
|
||||
storage.fixedTypeVariables[freshTypeConstructor] = resultType
|
||||
}
|
||||
|
||||
private fun checkOnlyInputTypesAnnotation(
|
||||
variableWithConstraints: MutableVariableWithConstraints?,
|
||||
resultType: KotlinTypeMarker
|
||||
) {
|
||||
if (variableWithConstraints == null) return
|
||||
val variableHasOnlyInputTypes = with(utilContext) { variableWithConstraints.typeVariable.hasOnlyInputTypesAttribute() }
|
||||
if (!variableHasOnlyInputTypes) return
|
||||
|
||||
val resultTypeIsInputType = variableWithConstraints.getProjectedInputCallTypes(utilContext).any { inputType ->
|
||||
if (AbstractTypeChecker.equalTypes(this, resultType, inputType)) return@any true
|
||||
val constructor = inputType.typeConstructor()
|
||||
constructor.isIntersection() && constructor.supertypes().any { AbstractTypeChecker.equalTypes(this, resultType, it) }
|
||||
}
|
||||
if (!resultTypeIsInputType) {
|
||||
addError(OnlyInputTypesDiagnostic(variableWithConstraints.typeVariable))
|
||||
}
|
||||
}
|
||||
|
||||
// KotlinConstraintSystemCompleter.Context, PostponedArgumentsAnalyzer.Context
|
||||
override fun canBeProper(type: KotlinTypeMarker): Boolean {
|
||||
checkState(State.BUILDING, State.COMPLETION)
|
||||
return !type.contains { storage.notFixedTypeVariables.containsKey(it.typeConstructor()) }
|
||||
}
|
||||
|
||||
override fun containsOnlyFixedOrPostponedVariables(type: KotlinTypeMarker): Boolean {
|
||||
checkState(State.BUILDING, State.COMPLETION)
|
||||
return !type.contains {
|
||||
val typeConstructor = it.typeConstructor()
|
||||
val variable = storage.notFixedTypeVariables[typeConstructor]?.typeVariable
|
||||
variable !in storage.postponedTypeVariables && storage.notFixedTypeVariables.containsKey(typeConstructor)
|
||||
}
|
||||
}
|
||||
|
||||
// PostponedArgumentsAnalyzer.Context
|
||||
override fun buildCurrentSubstitutor(): TypeSubstitutorMarker {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
return buildCurrentSubstitutor(emptyMap())
|
||||
}
|
||||
|
||||
override fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>): TypeSubstitutorMarker {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
return storage.buildCurrentSubstitutor(this, additionalBindings)
|
||||
}
|
||||
|
||||
override fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
return storage.buildNotFixedVariablesToNonSubtypableTypesSubstitutor(this)
|
||||
}
|
||||
|
||||
// ResultTypeResolver.Context, VariableFixationFinder.Context
|
||||
override fun isReified(variable: TypeVariableMarker): Boolean {
|
||||
return with(utilContext) { variable.isReified() }
|
||||
}
|
||||
|
||||
override fun bindingStubsForPostponedVariables(): Map<TypeVariableMarker, StubTypeMarker> {
|
||||
checkState(State.BUILDING, State.COMPLETION)
|
||||
// TODO: SUB
|
||||
return storage.postponedTypeVariables.associateWith { createStubType(it) }
|
||||
}
|
||||
|
||||
override fun currentStorage(): ConstraintStorage {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||
return storage
|
||||
}
|
||||
|
||||
// PostponedArgumentsAnalyzer.Context
|
||||
override fun hasUpperOrEqualUnitConstraint(type: KotlinTypeMarker): Boolean {
|
||||
checkState(State.BUILDING, State.COMPLETION, State.FREEZED)
|
||||
val constraints = storage.notFixedTypeVariables[type.typeConstructor()]?.constraints ?: return false
|
||||
return constraints.any { (it.kind == ConstraintKind.UPPER || it.kind == ConstraintKind.EQUALITY) && it.type.isUnit() }
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.model
|
||||
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
|
||||
interface PostponedResolvedAtomMarker {
|
||||
val inputTypes: Collection<KotlinTypeMarker>
|
||||
val outputType: KotlinTypeMarker?
|
||||
val analyzed: Boolean
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.results
|
||||
|
||||
import org.jetbrains.kotlin.container.DefaultImplementation
|
||||
import org.jetbrains.kotlin.container.PlatformSpecificExtension
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
|
||||
@DefaultImplementation(impl = TypeSpecificityComparator.NONE::class)
|
||||
interface TypeSpecificityComparator : PlatformSpecificExtension<TypeSpecificityComparator> {
|
||||
fun isDefinitelyLessSpecific(specific: KotlinTypeMarker, general: KotlinTypeMarker): Boolean
|
||||
|
||||
object NONE : TypeSpecificityComparator {
|
||||
override fun isDefinitelyLessSpecific(specific: KotlinTypeMarker, general: KotlinTypeMarker) = false
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.tasks;
|
||||
|
||||
public enum ExplicitReceiverKind {
|
||||
EXTENSION_RECEIVER,
|
||||
DISPATCH_RECEIVER,
|
||||
NO_EXPLICIT_RECEIVER,
|
||||
|
||||
// A very special case.
|
||||
// In a call 'b.foo(1)' where class 'Foo' has an extension member 'fun B.invoke(Int)' function 'invoke' has two explicit receivers:
|
||||
// 'b' (as extension receiver) and 'foo' (as dispatch receiver).
|
||||
BOTH_RECEIVERS;
|
||||
|
||||
public boolean isExtensionReceiver() {
|
||||
return this == EXTENSION_RECEIVER || this == BOTH_RECEIVERS;
|
||||
}
|
||||
|
||||
public boolean isDispatchReceiver() {
|
||||
return this == DISPATCH_RECEIVER || this == BOTH_RECEIVERS;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.tower
|
||||
|
||||
enum class ResolutionCandidateApplicability {
|
||||
RESOLVED, // call success or has uncompleted inference or in other words possible successful candidate
|
||||
RESOLVED_WITH_ERROR, // call has error, but it is still successful from resolution perspective
|
||||
RESOLVED_NEED_PRESERVE_COMPATIBILITY, // call resolved successfully, but using new features that changes resolve
|
||||
RESOLVED_LOW_PRIORITY,
|
||||
CONVENTION_ERROR, // missing infix, operator etc
|
||||
MAY_THROW_RUNTIME_ERROR, // unsafe call or unstable smart cast
|
||||
RUNTIME_ERROR, // problems with visibility
|
||||
IMPOSSIBLE_TO_GENERATE, // access to outer class from nested
|
||||
INAPPLICABLE, // arguments have wrong types
|
||||
INAPPLICABLE_ARGUMENTS_MAPPING_ERROR, // arguments not mapped to parameters (i.e. different size of arguments and parameters)
|
||||
INAPPLICABLE_WRONG_RECEIVER, // receiver not matched
|
||||
HIDDEN, // removed from resolve
|
||||
RESOLVED_TO_SAM_WITH_VARARG, // migration warning up to 1.5 (when resolve to function with SAM conversion and array without spread as vararg)
|
||||
}
|
||||
|
||||
val ResolutionCandidateApplicability.isSuccess: Boolean
|
||||
get() = this <= ResolutionCandidateApplicability.RESOLVED_LOW_PRIORITY
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.types
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator.commonSuperType
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionContext) : TypeSystemInferenceExtensionContext by ctx {
|
||||
|
||||
private class ApproximationResult(val type: KotlinTypeMarker?)
|
||||
|
||||
private val cacheForIncorporationConfigToSuperDirection = ConcurrentHashMap<KotlinTypeMarker, ApproximationResult>()
|
||||
private val cacheForIncorporationConfigToSubtypeDirection = ConcurrentHashMap<KotlinTypeMarker, ApproximationResult>()
|
||||
|
||||
private val referenceApproximateToSuperType: (SimpleTypeMarker, TypeApproximatorConfiguration, Int) -> KotlinTypeMarker?
|
||||
get() = this::approximateSimpleToSuperType
|
||||
private val referenceApproximateToSubType: (SimpleTypeMarker, TypeApproximatorConfiguration, Int) -> KotlinTypeMarker?
|
||||
get() = this::approximateSimpleToSubType
|
||||
|
||||
companion object {
|
||||
const val CACHE_FOR_INCORPORATION_MAX_SIZE = 500
|
||||
}
|
||||
|
||||
// null means that this input type is the result, i.e. input type not contains not-allowed kind of types
|
||||
// type <: resultType
|
||||
fun approximateToSuperType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration): KotlinTypeMarker? =
|
||||
approximateToSuperType(type, conf, -type.typeDepth())
|
||||
|
||||
// resultType <: type
|
||||
fun approximateToSubType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration): KotlinTypeMarker? =
|
||||
approximateToSubType(type, conf, -type.typeDepth())
|
||||
|
||||
fun clearCache() {
|
||||
cacheForIncorporationConfigToSubtypeDirection.clear()
|
||||
cacheForIncorporationConfigToSuperDirection.clear()
|
||||
}
|
||||
|
||||
private fun checkExceptionalCases(
|
||||
type: KotlinTypeMarker, depth: Int, conf: TypeApproximatorConfiguration, toSuper: Boolean
|
||||
): ApproximationResult? {
|
||||
return when {
|
||||
type.isSpecial() ->
|
||||
null.toApproximationResult()
|
||||
|
||||
type.isError() ->
|
||||
// todo -- fix builtIns. Now builtIns here is DefaultBuiltIns
|
||||
(if (conf.errorType) null else type.defaultResult(toSuper)).toApproximationResult()
|
||||
|
||||
depth > 3 ->
|
||||
type.defaultResult(toSuper).toApproximationResult()
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinTypeMarker?.toApproximationResult(): ApproximationResult = ApproximationResult(this)
|
||||
|
||||
private inline fun cachedValue(
|
||||
type: KotlinTypeMarker,
|
||||
conf: TypeApproximatorConfiguration,
|
||||
toSuper: Boolean,
|
||||
approximate: () -> KotlinTypeMarker?
|
||||
): KotlinTypeMarker? {
|
||||
// Approximator depends on a configuration, so cache should take it into account
|
||||
// Here, we cache only types for configuration "from incorporation", which is used most intensively
|
||||
if (conf !is TypeApproximatorConfiguration.IncorporationConfiguration) return approximate()
|
||||
|
||||
val cache = if (toSuper) cacheForIncorporationConfigToSuperDirection else cacheForIncorporationConfigToSubtypeDirection
|
||||
|
||||
if (cache.size > CACHE_FOR_INCORPORATION_MAX_SIZE) return approximate()
|
||||
|
||||
return cache.getOrPut(type, { approximate().toApproximationResult() }).type
|
||||
}
|
||||
|
||||
private fun approximateToSuperType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration, depth: Int): KotlinTypeMarker? {
|
||||
checkExceptionalCases(type, depth, conf, toSuper = true)?.let { return it.type }
|
||||
|
||||
return cachedValue(type, conf, toSuper = true) {
|
||||
approximateTo(
|
||||
prepareType(type), conf, { upperBound() },
|
||||
referenceApproximateToSuperType, depth
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun approximateToSubType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration, depth: Int): KotlinTypeMarker? {
|
||||
checkExceptionalCases(type, depth, conf, toSuper = false)?.let { return it.type }
|
||||
|
||||
return cachedValue(type, conf, toSuper = false) {
|
||||
approximateTo(
|
||||
prepareType(type), conf, { lowerBound() },
|
||||
referenceApproximateToSubType, depth
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Don't call this method directly, it should be used only in approximateToSuperType/approximateToSubType (use these methods instead)
|
||||
// This method contains detailed implementation only for type approximation, it doesn't check exceptional cases and doesn't use cache
|
||||
private fun approximateTo(
|
||||
type: KotlinTypeMarker,
|
||||
conf: TypeApproximatorConfiguration,
|
||||
bound: FlexibleTypeMarker.() -> SimpleTypeMarker,
|
||||
approximateTo: (SimpleTypeMarker, TypeApproximatorConfiguration, depth: Int) -> KotlinTypeMarker?,
|
||||
depth: Int
|
||||
): KotlinTypeMarker? {
|
||||
when (type) {
|
||||
is SimpleTypeMarker -> return approximateTo(type, conf, depth)
|
||||
is FlexibleTypeMarker -> {
|
||||
if (type.isDynamic()) {
|
||||
return if (conf.dynamic) null else type.bound()
|
||||
} else if (type.asRawType() != null) {
|
||||
return if (conf.rawType) null else type.bound()
|
||||
}
|
||||
|
||||
// TODO: Restore check
|
||||
// TODO: currently we can lose information about enhancement, should be fixed later
|
||||
// assert(type is FlexibleTypeImpl || type is FlexibleTypeWithEnhancement) {
|
||||
// "Unexpected subclass of FlexibleType: ${type::class.java.canonicalName}, type = $type"
|
||||
// }
|
||||
|
||||
if (conf.flexible) {
|
||||
/**
|
||||
* Let inputType = L_1..U_1; resultType = L_2..U_2
|
||||
* We should create resultType such as inputType <: resultType.
|
||||
* It means that if A <: inputType, then A <: U_1. And, because inputType <: resultType,
|
||||
* A <: resultType => A <: U_2. I.e. for every type A such A <: U_1, A <: U_2 => U_1 <: U_2.
|
||||
*
|
||||
* Similar for L_1 <: L_2: Let B : resultType <: B. L_2 <: B and L_1 <: B.
|
||||
* I.e. for every type B such as L_2 <: B, L_1 <: B. For example B = L_2.
|
||||
*/
|
||||
val lowerBound = type.lowerBound()
|
||||
val upperBound = type.upperBound()
|
||||
|
||||
val lowerResult = approximateTo(lowerBound, conf, depth)
|
||||
|
||||
val upperResult = if (type !is RawTypeMarker && lowerBound.typeConstructor() == upperBound.typeConstructor())
|
||||
lowerResult?.withNullability(upperBound.isMarkedNullable())
|
||||
else
|
||||
approximateTo(upperBound, conf, depth)
|
||||
if (lowerResult == null && upperResult == null) return null
|
||||
|
||||
/**
|
||||
* If C <: L..U then C <: L.
|
||||
* inputType.lower <: lowerResult => inputType.lower <: lowerResult?.lowerIfFlexible()
|
||||
* i.e. this type is correct. We use this type, because this type more flexible.
|
||||
*
|
||||
* If U_1 <: U_2.lower .. U_2.upper, then we know only that U_1 <: U_2.upper.
|
||||
*/
|
||||
return createFlexibleType(
|
||||
lowerResult?.lowerBoundIfFlexible() ?: lowerBound,
|
||||
upperResult?.upperBoundIfFlexible() ?: upperBound
|
||||
)
|
||||
} else {
|
||||
return type.bound().let { approximateTo(it, conf, depth) ?: it }
|
||||
}
|
||||
}
|
||||
else -> error("sealed")
|
||||
}
|
||||
}
|
||||
|
||||
private fun isIntersectionTypeEffectivelyNothing(constructor: IntersectionTypeConstructorMarker): Boolean {
|
||||
// We consider intersection as Nothing only if one of it's component is a primitive number type
|
||||
// It's intentional we're not trying to prove population of some type as it was in OI
|
||||
|
||||
return constructor.supertypes().any {
|
||||
!it.isMarkedNullable() && it.isSignedOrUnsignedNumberType()
|
||||
}
|
||||
}
|
||||
|
||||
private fun approximateIntersectionType(
|
||||
type: SimpleTypeMarker,
|
||||
conf: TypeApproximatorConfiguration,
|
||||
toSuper: Boolean,
|
||||
depth: Int
|
||||
): KotlinTypeMarker? {
|
||||
val typeConstructor = type.typeConstructor()
|
||||
assert(typeConstructor.isIntersection()) {
|
||||
"Should be intersection type: $type, typeConstructor class: ${typeConstructor::class.java.canonicalName}"
|
||||
}
|
||||
assert(typeConstructor.supertypes().isNotEmpty()) {
|
||||
"Supertypes for intersection type should not be empty: $type"
|
||||
}
|
||||
|
||||
var thereIsApproximation = false
|
||||
val newTypes = typeConstructor.supertypes().map {
|
||||
val newType = if (toSuper) approximateToSuperType(it, conf, depth) else approximateToSubType(it, conf, depth)
|
||||
if (newType != null) {
|
||||
thereIsApproximation = true
|
||||
newType
|
||||
} else it
|
||||
}
|
||||
|
||||
/**
|
||||
* For case ALLOWED:
|
||||
* A <: A', B <: B' => A & B <: A' & B'
|
||||
*
|
||||
* For other case -- it's impossible to find some type except Nothing as subType for intersection type.
|
||||
*/
|
||||
val baseResult = when (conf.intersection) {
|
||||
TypeApproximatorConfiguration.IntersectionStrategy.ALLOWED -> if (!thereIsApproximation) return null else intersectTypes(newTypes)
|
||||
TypeApproximatorConfiguration.IntersectionStrategy.TO_FIRST -> if (toSuper) newTypes.first() else return type.defaultResult(toSuper = false)
|
||||
// commonSupertypeCalculator should handle flexible types correctly
|
||||
TypeApproximatorConfiguration.IntersectionStrategy.TO_COMMON_SUPERTYPE -> {
|
||||
if (!toSuper) return type.defaultResult(toSuper = false)
|
||||
val resultType = commonSuperType(newTypes)
|
||||
approximateToSuperType(resultType, conf) ?: resultType
|
||||
}
|
||||
}
|
||||
|
||||
return if (type.isMarkedNullable()) baseResult.withNullability(true) else baseResult
|
||||
}
|
||||
|
||||
private fun approximateCapturedType(
|
||||
type: CapturedTypeMarker,
|
||||
conf: TypeApproximatorConfiguration,
|
||||
toSuper: Boolean,
|
||||
depth: Int
|
||||
): KotlinTypeMarker? {
|
||||
val supertypes = type.typeConstructor().supertypes()
|
||||
val baseSuperType = when (supertypes.size) {
|
||||
0 -> nullableAnyType() // Let C = in Int, then superType for C and C? is Any?
|
||||
1 -> supertypes.single()
|
||||
|
||||
// Consider the following example:
|
||||
// A.getA()::class.java, where `getA()` returns some class from Java
|
||||
// From `::class` we are getting type KClass<Cap<out A!>>, where Cap<out A!> have two supertypes:
|
||||
// - Any (from declared upper bound of type parameter for KClass)
|
||||
// - (A..A?) -- from A!, projection type of captured type
|
||||
|
||||
// Now, after approximation we were getting type `KClass<out A>`, because { Any & (A..A?) } = A,
|
||||
// but in old inference type was equal to `KClass<out A!>`.
|
||||
|
||||
// Important note that from the point of type system first type is more specific:
|
||||
// Here, approximation of KClass<Cap<out A!>> is a type KClass<T> such that KClass<Cap<out A!>> <: KClass<out T> =>
|
||||
// So, the the more specific type for T would be "some non-null (because of declared upper bound type) subtype of A", which is `out A`
|
||||
|
||||
// But for now, to reduce differences in behaviour of old and new inference, we'll approximate such types to `KClass<out A!>`
|
||||
|
||||
// Once NI will be more stabilized, we'll use more specific type
|
||||
|
||||
else -> {
|
||||
val projection = type.typeConstructorProjection()
|
||||
if (projection.isStarProjection()) intersectTypes(supertypes.toList())
|
||||
else projection.getType()
|
||||
}
|
||||
}
|
||||
val baseSubType = type.lowerType() ?: nothingType()
|
||||
|
||||
if (conf.capturedType(ctx, type)) {
|
||||
/**
|
||||
* Here everything is ok if bounds for this captured type should not be approximated.
|
||||
* But. If such bounds contains some unauthorized types, then we cannot leave this captured type "as is".
|
||||
* And we cannot create new capture type, because meaning of new captured type is not clear.
|
||||
* So, we will just approximate such types
|
||||
*
|
||||
* todo handle flexible types
|
||||
*/
|
||||
if (approximateToSuperType(baseSuperType, conf, depth) == null && approximateToSubType(baseSubType, conf, depth) == null) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
val baseResult = if (toSuper) approximateToSuperType(baseSuperType, conf, depth) ?: baseSuperType else approximateToSubType(
|
||||
baseSubType,
|
||||
conf,
|
||||
depth
|
||||
) ?: baseSubType
|
||||
|
||||
// C = in Int, Int <: C => Int? <: C?
|
||||
// C = out Number, C <: Number => C? <: Number?
|
||||
return when {
|
||||
type.isMarkedNullable() -> baseResult.withNullability(true)
|
||||
type.isProjectionNotNull() -> baseResult.withNullability(false)
|
||||
else -> baseResult
|
||||
}
|
||||
}
|
||||
|
||||
private fun approximateSimpleToSuperType(type: SimpleTypeMarker, conf: TypeApproximatorConfiguration, depth: Int) =
|
||||
approximateTo(type, conf, toSuper = true, depth = depth)
|
||||
|
||||
private fun approximateSimpleToSubType(type: SimpleTypeMarker, conf: TypeApproximatorConfiguration, depth: Int) =
|
||||
approximateTo(type, conf, toSuper = false, depth = depth)
|
||||
|
||||
private fun approximateTo(
|
||||
type: SimpleTypeMarker,
|
||||
conf: TypeApproximatorConfiguration,
|
||||
toSuper: Boolean,
|
||||
depth: Int
|
||||
): KotlinTypeMarker? {
|
||||
if (type.argumentsCount() != 0) {
|
||||
return approximateParametrizedType(type, conf, toSuper, depth + 1)
|
||||
}
|
||||
|
||||
val definitelyNotNullType = type.asDefinitelyNotNullType()
|
||||
if (definitelyNotNullType != null) {
|
||||
return approximateDefinitelyNotNullType(definitelyNotNullType, conf, toSuper, depth)
|
||||
}
|
||||
|
||||
val typeConstructor = type.typeConstructor()
|
||||
|
||||
if (typeConstructor.isCapturedTypeConstructor()) {
|
||||
val capturedType = type.asCapturedType()
|
||||
require(capturedType != null) {
|
||||
// KT-16147
|
||||
"Type is inconsistent -- somewhere we create type with typeConstructor = $typeConstructor " +
|
||||
"and class: ${type::class.java.canonicalName}. type.toString() = $type"
|
||||
}
|
||||
return approximateCapturedType(capturedType, conf, toSuper, depth)
|
||||
}
|
||||
|
||||
if (typeConstructor.isIntersection()) {
|
||||
return approximateIntersectionType(type, conf, toSuper, depth)
|
||||
}
|
||||
|
||||
if (typeConstructor is TypeVariableTypeConstructorMarker) {
|
||||
return if (conf.typeVariable(typeConstructor)) null else type.defaultResult(toSuper)
|
||||
}
|
||||
|
||||
if (typeConstructor.isIntegerLiteralTypeConstructor()) {
|
||||
return if (conf.integerLiteralType)
|
||||
typeConstructor.getApproximatedIntegerLiteralType().withNullability(type.isMarkedNullable())
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
return null // simple classifier type
|
||||
}
|
||||
|
||||
private fun approximateDefinitelyNotNullType(
|
||||
type: DefinitelyNotNullTypeMarker,
|
||||
conf: TypeApproximatorConfiguration,
|
||||
toSuper: Boolean,
|
||||
depth: Int
|
||||
): KotlinTypeMarker? {
|
||||
val originalType = type.original()
|
||||
val approximatedOriginalType =
|
||||
if (toSuper) approximateToSuperType(originalType, conf, depth) else approximateToSubType(originalType, conf, depth)
|
||||
|
||||
return if (conf.definitelyNotNullType) {
|
||||
approximatedOriginalType?.makeDefinitelyNotNullOrNotNull()
|
||||
} else {
|
||||
if (toSuper)
|
||||
(approximatedOriginalType ?: originalType).withNullability(false)
|
||||
else
|
||||
type.defaultResult(toSuper)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isApproximateDirectionToSuper(effectiveVariance: TypeVariance, toSuper: Boolean) =
|
||||
when (effectiveVariance) {
|
||||
TypeVariance.OUT -> toSuper
|
||||
TypeVariance.IN -> !toSuper
|
||||
TypeVariance.INV -> throw AssertionError("Incorrect variance $effectiveVariance")
|
||||
}
|
||||
|
||||
private fun approximateParametrizedType(
|
||||
type: SimpleTypeMarker,
|
||||
conf: TypeApproximatorConfiguration,
|
||||
toSuper: Boolean,
|
||||
depth: Int
|
||||
): SimpleTypeMarker? {
|
||||
val typeConstructor = type.typeConstructor()
|
||||
if (typeConstructor.parametersCount() != type.argumentsCount()) {
|
||||
return if (conf.errorType) {
|
||||
createErrorType("Inconsistent type: $type (parameters.size = ${typeConstructor.parametersCount()}, arguments.size = ${type.argumentsCount()})")
|
||||
} else type.defaultResult(toSuper)
|
||||
}
|
||||
|
||||
val newArguments = arrayOfNulls<TypeArgumentMarker?>(type.argumentsCount())
|
||||
|
||||
loop@ for (index in 0 until type.argumentsCount()) {
|
||||
val parameter = typeConstructor.getParameter(index)
|
||||
val argument = type.getArgument(index)
|
||||
|
||||
if (argument.isStarProjection()) continue
|
||||
|
||||
val effectiveVariance = AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
|
||||
|
||||
val argumentType = newArguments[index]?.getType() ?: argument.getType()
|
||||
|
||||
val capturedType = argumentType.lowerBoundIfFlexible().asCapturedType()
|
||||
val capturedStarProjectionOrNull =
|
||||
capturedType?.typeConstructorProjection()?.takeIf { it.isStarProjection() }
|
||||
|
||||
if (capturedStarProjectionOrNull != null &&
|
||||
(effectiveVariance == TypeVariance.OUT || effectiveVariance == TypeVariance.INV) &&
|
||||
toSuper &&
|
||||
capturedType.typeParameter() == parameter
|
||||
) {
|
||||
newArguments[index] = capturedStarProjectionOrNull
|
||||
continue@loop
|
||||
}
|
||||
|
||||
when (effectiveVariance) {
|
||||
null -> {
|
||||
return if (conf.errorType) {
|
||||
createErrorType(
|
||||
"Inconsistent type: $type ($index parameter has declared variance: ${parameter.getVariance()}, " +
|
||||
"but argument variance is ${argument.getVariance()})"
|
||||
)
|
||||
} else type.defaultResult(toSuper)
|
||||
}
|
||||
TypeVariance.OUT, TypeVariance.IN -> {
|
||||
if (
|
||||
conf.intersectionTypesInContravariantPositions &&
|
||||
effectiveVariance == TypeVariance.IN &&
|
||||
argumentType.typeConstructor().isIntersection()
|
||||
) {
|
||||
val argumentTypeConstructor = argumentType.typeConstructor()
|
||||
if (argumentTypeConstructor.isIntersection() && isIntersectionTypeEffectivelyNothing(argumentTypeConstructor as IntersectionTypeConstructorMarker)) {
|
||||
newArguments[index] = createStarProjection(parameter)
|
||||
continue@loop
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Out<Foo> <: Out<superType(Foo)>
|
||||
* Inv<out Foo> <: Inv<out superType(Foo)>
|
||||
|
||||
* In<Foo> <: In<subType(Foo)>
|
||||
* Inv<in Foo> <: Inv<in subType(Foo)>
|
||||
*/
|
||||
val approximatedArgument = argumentType.let {
|
||||
if (isApproximateDirectionToSuper(effectiveVariance, toSuper)) {
|
||||
approximateToSuperType(it, conf, depth)
|
||||
} else {
|
||||
approximateToSubType(it, conf, depth)
|
||||
}
|
||||
} ?: continue@loop
|
||||
|
||||
if (
|
||||
conf.intersection != TypeApproximatorConfiguration.IntersectionStrategy.ALLOWED &&
|
||||
effectiveVariance == TypeVariance.OUT &&
|
||||
argumentType.typeConstructor().isIntersection()
|
||||
) {
|
||||
var shouldReplaceWithStar = false
|
||||
for (upperBoundIndex in 0 until parameter.upperBoundCount()) {
|
||||
if (!AbstractTypeChecker.isSubtypeOf(ctx, approximatedArgument, parameter.getUpperBound(upperBoundIndex))) {
|
||||
shouldReplaceWithStar = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (shouldReplaceWithStar) {
|
||||
newArguments[index] = createStarProjection(parameter)
|
||||
continue@loop
|
||||
}
|
||||
}
|
||||
|
||||
if (parameter.getVariance() == TypeVariance.INV) {
|
||||
newArguments[index] = createTypeArgument(approximatedArgument, effectiveVariance)
|
||||
} else {
|
||||
newArguments[index] = approximatedArgument.asTypeArgument()
|
||||
}
|
||||
}
|
||||
TypeVariance.INV -> {
|
||||
if (!toSuper) {
|
||||
// Inv<Foo> cannot be approximated to subType
|
||||
val toSubType = approximateToSubType(argumentType, conf, depth) ?: continue@loop
|
||||
|
||||
// Inv<Foo!> is supertype for Inv<Foo?>
|
||||
if (!AbstractTypeChecker.equalTypes(
|
||||
this,
|
||||
argumentType,
|
||||
toSubType
|
||||
)
|
||||
) return type.defaultResult(toSuper)
|
||||
|
||||
// also Captured(out Nothing) = Nothing
|
||||
newArguments[index] = toSubType.asTypeArgument()
|
||||
continue@loop
|
||||
}
|
||||
|
||||
/**
|
||||
* Example with non-trivial both type approximations:
|
||||
* Inv<In<C>> where C = in Int
|
||||
* Inv<In<C>> <: Inv<out In<Int>>
|
||||
* Inv<In<C>> <: Inv<in In<Any?>>
|
||||
*
|
||||
* So such case is rare and we will chose Inv<out In<Int>> for now.
|
||||
*
|
||||
* Note that for case Inv<C> we will chose Inv<in Int>, because it is more informative then Inv<out Any?>.
|
||||
* May be we should do the same for deeper types, but not now.
|
||||
*/
|
||||
if (argumentType.typeConstructor().isCapturedTypeConstructor()) {
|
||||
val subType = approximateToSubType(argumentType, conf, depth) ?: continue@loop
|
||||
if (!subType.isTrivialSub()) {
|
||||
newArguments[index] = createTypeArgument(subType, TypeVariance.IN)
|
||||
continue@loop
|
||||
}
|
||||
}
|
||||
|
||||
val approximatedSuperType =
|
||||
approximateToSuperType(argumentType, conf, depth) ?: continue@loop // null means that this type we can leave as is
|
||||
if (approximatedSuperType.isTrivialSuper()) {
|
||||
val approximatedSubType =
|
||||
approximateToSubType(argumentType, conf, depth) ?: continue@loop // seems like this is never null
|
||||
if (!approximatedSubType.isTrivialSub()) {
|
||||
newArguments[index] = createTypeArgument(approximatedSubType, TypeVariance.IN)
|
||||
continue@loop
|
||||
}
|
||||
}
|
||||
|
||||
if (AbstractTypeChecker.equalTypes(this, argumentType, approximatedSuperType)) {
|
||||
newArguments[index] = approximatedSuperType.asTypeArgument()
|
||||
} else {
|
||||
newArguments[index] = createTypeArgument(approximatedSuperType, TypeVariance.OUT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newArguments.all { it == null }) return null
|
||||
|
||||
val newArgumentsList = List(type.argumentsCount()) { index -> newArguments[index] ?: type.getArgument(index) }
|
||||
return type.replaceArguments(newArgumentsList)
|
||||
}
|
||||
|
||||
private fun KotlinTypeMarker.defaultResult(toSuper: Boolean) = if (toSuper) nullableAnyType() else {
|
||||
if (this is SimpleTypeMarker && isMarkedNullable()) nullableNothingType() else nothingType()
|
||||
}
|
||||
|
||||
// Any? or Any!
|
||||
private fun KotlinTypeMarker.isTrivialSuper() = upperBoundIfFlexible().isNullableAny()
|
||||
|
||||
// Nothing or Nothing!
|
||||
private fun KotlinTypeMarker.isTrivialSub() = lowerBoundIfFlexible().isNothing()
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.types
|
||||
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
|
||||
open class TypeApproximatorConfiguration {
|
||||
enum class IntersectionStrategy {
|
||||
ALLOWED,
|
||||
TO_FIRST,
|
||||
TO_COMMON_SUPERTYPE
|
||||
}
|
||||
|
||||
open val flexible: Boolean get() = false // simple flexible types (FlexibleTypeImpl)
|
||||
open val dynamic: Boolean get() = false // DynamicType
|
||||
open val rawType: Boolean get() = false // RawTypeImpl
|
||||
open val errorType: Boolean get() = false
|
||||
open val integerLiteralType: Boolean = false // IntegerLiteralTypeConstructor
|
||||
open val definitelyNotNullType: Boolean get() = true
|
||||
open val intersection: IntersectionStrategy = IntersectionStrategy.TO_COMMON_SUPERTYPE
|
||||
open val intersectionTypesInContravariantPositions = false
|
||||
|
||||
open val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean = { false }
|
||||
open fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean =
|
||||
false // true means that this type we can leave as is
|
||||
|
||||
abstract class AllFlexibleSameValue : TypeApproximatorConfiguration() {
|
||||
abstract val allFlexible: Boolean
|
||||
|
||||
override val flexible: Boolean get() = allFlexible
|
||||
override val dynamic: Boolean get() = allFlexible
|
||||
override val rawType: Boolean get() = allFlexible
|
||||
}
|
||||
|
||||
object LocalDeclaration : AllFlexibleSameValue() {
|
||||
override val allFlexible: Boolean get() = true
|
||||
override val intersection: IntersectionStrategy get() = IntersectionStrategy.ALLOWED
|
||||
override val errorType: Boolean get() = true
|
||||
override val integerLiteralType: Boolean get() = true
|
||||
override val intersectionTypesInContravariantPositions: Boolean get() = true
|
||||
}
|
||||
|
||||
object PublicDeclaration : AllFlexibleSameValue() {
|
||||
override val allFlexible: Boolean get() = true
|
||||
override val errorType: Boolean get() = true
|
||||
override val definitelyNotNullType: Boolean get() = false
|
||||
override val integerLiteralType: Boolean get() = true
|
||||
override val intersectionTypesInContravariantPositions: Boolean get() = true
|
||||
}
|
||||
|
||||
abstract class AbstractCapturedTypesApproximation(val approximatedCapturedStatus: CaptureStatus) :
|
||||
AllFlexibleSameValue() {
|
||||
override val allFlexible: Boolean get() = true
|
||||
override val errorType: Boolean get() = true
|
||||
|
||||
// i.e. will be approximated only approximatedCapturedStatus captured types
|
||||
override fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean =
|
||||
type.captureStatus(ctx) != approximatedCapturedStatus
|
||||
|
||||
override val intersection: IntersectionStrategy get() = IntersectionStrategy.ALLOWED
|
||||
override val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean get() = { true }
|
||||
}
|
||||
|
||||
object IncorporationConfiguration : AbstractCapturedTypesApproximation(CaptureStatus.FOR_INCORPORATION)
|
||||
object SubtypeCapturedTypesApproximation : AbstractCapturedTypesApproximation(CaptureStatus.FOR_SUBTYPING)
|
||||
object InternalTypesApproximation : AbstractCapturedTypesApproximation(CaptureStatus.FROM_EXPRESSION) {
|
||||
override val integerLiteralType: Boolean get() = true
|
||||
override val intersectionTypesInContravariantPositions: Boolean get() = true
|
||||
}
|
||||
|
||||
object FinalApproximationAfterResolutionAndInference :
|
||||
AbstractCapturedTypesApproximation(CaptureStatus.FROM_EXPRESSION) {
|
||||
override val integerLiteralType: Boolean get() = true
|
||||
override val intersectionTypesInContravariantPositions: Boolean get() = true
|
||||
}
|
||||
|
||||
object IntegerLiteralsTypesApproximation : AllFlexibleSameValue() {
|
||||
override val integerLiteralType: Boolean get() = true
|
||||
override val allFlexible: Boolean get() = true
|
||||
override val intersection: IntersectionStrategy get() = IntersectionStrategy.ALLOWED
|
||||
override val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean get() = { true }
|
||||
override val errorType: Boolean get() = true
|
||||
|
||||
override fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean = true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user