[NI] Implement various optimizations for incorporation algorithm
Mostly, these optimisations are picked from the old inference. Also, remove exponential complexity for flexible types in approximation, note that more correct fix for this would be to introduce new types that corresponds just to platform types to avoid nullability problems, but due to complexity it will be done later #KT-31415 Fixed
This commit is contained in:
+3
-5
@@ -22,10 +22,8 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.ClassicTypeSystemContext
|
||||
import org.jetbrains.kotlin.types.model.StubTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import java.util.*
|
||||
|
||||
fun ConstraintStorage.buildCurrentSubstitutor(
|
||||
context: TypeSystemInferenceExtensionContext,
|
||||
@@ -84,4 +82,4 @@ fun CallableDescriptor.substituteAndApproximateCapturedTypes(substitutor: NewTyp
|
||||
return substitute(TypeSubstitutor.create(wrappedSubstitution))
|
||||
}
|
||||
|
||||
internal fun <E> MutableList<E>.trimToSize(newSize: Int) = subList(newSize, size).clear()
|
||||
internal fun <E> MutableList<E>.trimToSize(newSize: Int) = subList(newSize, size).clear()
|
||||
|
||||
+59
-12
@@ -29,6 +29,8 @@ class ConstraintIncorporator(
|
||||
fun getConstraintsForVariable(typeVariable: TypeVariableMarker): Collection<Constraint>
|
||||
|
||||
fun addNewIncorporatedConstraint(lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker)
|
||||
|
||||
fun addNewIncorporatedConstraint(typeVariable: TypeVariableMarker, type: KotlinTypeMarker, constraintContext: ConstraintContext)
|
||||
}
|
||||
|
||||
// \alpha is typeVariable, \beta -- other type variable registered in ConstraintStorage
|
||||
@@ -138,24 +140,49 @@ class ConstraintIncorporator(
|
||||
|
||||
if (baseConstraint.kind != ConstraintKind.UPPER) {
|
||||
val generatedConstraintType = approximateCapturedTypes(typeForApproximation, toSuper = false)
|
||||
if (!trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial(
|
||||
otherConstraint, generatedConstraintType, isSubtype = true
|
||||
)
|
||||
) {
|
||||
addNewIncorporatedConstraint(generatedConstraintType, targetVariable.defaultType())
|
||||
}
|
||||
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, generatedConstraintType, isSubtype = true)
|
||||
}
|
||||
if (baseConstraint.kind != ConstraintKind.LOWER) {
|
||||
val generatedConstraintType = approximateCapturedTypes(typeForApproximation, toSuper = true)
|
||||
if (!trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial(
|
||||
otherConstraint, generatedConstraintType, isSubtype = false
|
||||
)
|
||||
) {
|
||||
addNewIncorporatedConstraint(targetVariable.defaultType(), generatedConstraintType)
|
||||
}
|
||||
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, generatedConstraintType, isSubtype = false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.addNewConstraint(
|
||||
targetVariable: TypeVariableMarker,
|
||||
baseConstraint: Constraint,
|
||||
otherVariable: TypeVariableMarker,
|
||||
otherConstraint: Constraint,
|
||||
newConstraint: KotlinTypeMarker,
|
||||
isSubtype: Boolean
|
||||
) {
|
||||
if (targetVariable in getNestedTypeVariables(newConstraint)) return
|
||||
if (!containsConstrainingTypeWithoutProjection(newConstraint, otherConstraint)) return
|
||||
if (trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial(otherConstraint, newConstraint, isSubtype)) return
|
||||
|
||||
val derivedFrom = (baseConstraint.derivedFrom + otherConstraint.derivedFrom).toMutableSet()
|
||||
if (otherVariable in derivedFrom) return
|
||||
|
||||
derivedFrom.add(otherVariable)
|
||||
|
||||
val kind = if (isSubtype) ConstraintKind.LOWER else ConstraintKind.UPPER
|
||||
|
||||
addNewIncorporatedConstraint(targetVariable, newConstraint, ConstraintContext(kind, derivedFrom))
|
||||
}
|
||||
|
||||
fun Context.containsConstrainingTypeWithoutProjection(
|
||||
newConstraint: KotlinTypeMarker,
|
||||
otherConstraint: Constraint
|
||||
): Boolean {
|
||||
return getNestedArguments(newConstraint).any {
|
||||
it.getType().typeConstructor() == otherConstraint.type.typeConstructor() && it.getVariance() == TypeVariance.INV
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getNestedTypeVariables(type: KotlinTypeMarker): List<TypeVariableMarker> =
|
||||
getNestedArguments(type).mapNotNull { 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)
|
||||
@@ -166,3 +193,23 @@ class ConstraintIncorporator(
|
||||
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 = ArrayList<TypeArgumentMarker>()
|
||||
|
||||
val stack = ArrayDeque<TypeArgumentMarker>()
|
||||
stack.push(createTypeArgument(type, TypeVariance.INV))
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
val typeProjection = stack.pop()
|
||||
if (typeProjection.isStarProjection()) continue
|
||||
|
||||
result.add(typeProjection)
|
||||
|
||||
val projectedType = typeProjection.getType()
|
||||
for (argumentIndex in 0 until projectedType.argumentsCount()) {
|
||||
stack.add(projectedType.getArgument(argumentIndex))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
+22
-10
@@ -163,7 +163,24 @@ class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator, val
|
||||
|
||||
private fun addConstraint(typeVariableConstructor: TypeConstructorMarker, type: KotlinTypeMarker, kind: ConstraintKind) {
|
||||
val typeVariable = c.allTypeVariables[typeVariableConstructor]
|
||||
?: error("Should by type variableConstructor: $typeVariableConstructor. ${c.allTypeVariables.values}")
|
||||
?: error("Should by type variableConstructor: $typeVariableConstructor. ${c.allTypeVariables.values}")
|
||||
|
||||
addNewIncorporatedConstraint(typeVariable, type, ConstraintContext(kind, emptySet()))
|
||||
}
|
||||
|
||||
// from ConstraintIncorporator.Context
|
||||
override fun addNewIncorporatedConstraint(lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker) {
|
||||
if (c.isAllowedType(lowerType) && c.isAllowedType(upperType)) {
|
||||
runIsSubtypeOf(lowerType, upperType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addNewIncorporatedConstraint(
|
||||
typeVariable: TypeVariableMarker,
|
||||
type: KotlinTypeMarker,
|
||||
constraintContext: ConstraintContext
|
||||
) {
|
||||
val (kind, derivedFrom) = constraintContext
|
||||
|
||||
var targetType = type
|
||||
if (targetType.isUninferredParameter()) {
|
||||
@@ -200,14 +217,7 @@ class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator, val
|
||||
}
|
||||
}
|
||||
|
||||
possibleNewConstraints.add(typeVariable to Constraint(kind, targetType, position))
|
||||
}
|
||||
|
||||
// from ConstraintIncorporator.Context
|
||||
override fun addNewIncorporatedConstraint(lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker) {
|
||||
if (c.isAllowedType(lowerType) && c.isAllowedType(upperType)) {
|
||||
runIsSubtypeOf(lowerType, upperType)
|
||||
}
|
||||
possibleNewConstraints.add(typeVariable to Constraint(kind, targetType, position, derivedFrom = derivedFrom))
|
||||
}
|
||||
|
||||
override val allTypeVariablesWithConstraints: Collection<VariableWithConstraints>
|
||||
@@ -234,4 +244,6 @@ class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator, val
|
||||
|
||||
private fun renderBaseConstraint() = "Base constraint: $baseLowerType <: $baseUpperType from position: $position"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ConstraintContext(val kind: ConstraintKind, val derivedFrom: Set<TypeVariableMarker>)
|
||||
+2
-1
@@ -67,7 +67,8 @@ class Constraint(
|
||||
val kind: ConstraintKind,
|
||||
val type: KotlinTypeMarker, // flexible types here is allowed
|
||||
val position: IncorporationConstraintPosition,
|
||||
val typeHashCode: Int = type.hashCode()
|
||||
val typeHashCode: Int = type.hashCode(),
|
||||
val derivedFrom: Set<TypeVariableMarker>
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
|
||||
+7
-1
@@ -47,7 +47,13 @@ class MutableVariableWithConstraints(
|
||||
}
|
||||
|
||||
val actualConstraint = if (addAsEqualityConstraint)
|
||||
Constraint(ConstraintKind.EQUALITY, constraint.type, constraint.position, constraint.typeHashCode)
|
||||
Constraint(
|
||||
ConstraintKind.EQUALITY,
|
||||
constraint.type,
|
||||
constraint.position,
|
||||
constraint.typeHashCode,
|
||||
derivedFrom = constraint.derivedFrom
|
||||
)
|
||||
else
|
||||
constraint
|
||||
|
||||
|
||||
@@ -189,9 +189,15 @@ abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionCon
|
||||
* 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(type.lowerBound(), conf, depth)
|
||||
val upperResult = approximateTo(type.upperBound(), conf, depth)
|
||||
val lowerResult = approximateTo(lowerBound, conf, depth)
|
||||
|
||||
val upperResult = if (lowerBound.typeConstructor() == upperBound.typeConstructor())
|
||||
lowerResult?.withNullability(upperBound.isMarkedNullable())
|
||||
else
|
||||
approximateTo(upperBound, conf, depth)
|
||||
if (lowerResult == null && upperResult == null) return null
|
||||
|
||||
/**
|
||||
@@ -202,8 +208,8 @@ abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionCon
|
||||
* If U_1 <: U_2.lower .. U_2.upper, then we know only that U_1 <: U_2.upper.
|
||||
*/
|
||||
return createFlexibleType(
|
||||
lowerResult?.lowerBoundIfFlexible() ?: type.lowerBound(),
|
||||
upperResult?.upperBoundIfFlexible() ?: type.upperBound()
|
||||
lowerResult?.lowerBoundIfFlexible() ?: lowerBound,
|
||||
upperResult?.upperBoundIfFlexible() ?: upperBound
|
||||
)
|
||||
} else {
|
||||
return type.bound().let { approximateTo(it, conf, depth) ?: it }
|
||||
|
||||
Reference in New Issue
Block a user