[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:
Mikhail Zarechenskiy
2019-05-24 14:58:59 +03:00
parent bbec3bf001
commit 8910859fd1
22 changed files with 239 additions and 48 deletions
@@ -158,9 +158,8 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext,
}
*/
val simpleType = this.asSimpleType() ?: return false
repeat(simpleType.argumentsCount()) { index ->
val argument = simpleType.getArgument(index)
repeat(argumentsCount()) { index ->
val argument = getArgument(index)
if (!argument.isStarProjection() && argument.getType().containsInternal(predicate, visited)) return true
}
@@ -144,13 +144,13 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext {
return typeConstructor
}
override fun SimpleTypeMarker.argumentsCount(): Int {
override fun KotlinTypeMarker.argumentsCount(): Int {
require(this is ConeKotlinType)
return this.typeArguments.size
}
override fun SimpleTypeMarker.getArgument(index: Int): TypeArgumentMarker {
override fun KotlinTypeMarker.getArgument(index: Int): TypeArgumentMarker {
require(this is ConeKotlinType)
return this.typeArguments.getOrNull(index)
@@ -10059,6 +10059,16 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok
runTest("compiler/testData/diagnostics/tests/inference/constraints/kt8879.kt");
}
@TestMetadata("manyConstraintsDueToFlexibleRawTypes.kt")
public void testManyConstraintsDueToFlexibleRawTypes() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/constraints/manyConstraintsDueToFlexibleRawTypes.kt");
}
@TestMetadata("manyConstraintsDueToRecursiveFlexibleTypesWithWildcards.kt")
public void testManyConstraintsDueToRecursiveFlexibleTypesWithWildcards() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/constraints/manyConstraintsDueToRecursiveFlexibleTypesWithWildcards.kt");
}
@TestMetadata("notNullConstraintOnNullableType.kt")
public void testNotNullConstraintOnNullableType() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/constraints/notNullConstraintOnNullableType.kt");
@@ -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()
@@ -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
}
@@ -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>)
@@ -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
@@ -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 }
+1 -1
View File
@@ -7,7 +7,7 @@ package boundsWithSubstitutors
class C : A<C>()
val a = B<C>()
val a1 = B<<!NI;UPPER_BOUND_VIOLATED, NI;UPPER_BOUND_VIOLATED, NI;UPPER_BOUND_VIOLATED, NI;UPPER_BOUND_VIOLATED, UPPER_BOUND_VIOLATED!>Int<!>>()
val a1 = B<<!NI;UPPER_BOUND_VIOLATED, UPPER_BOUND_VIOLATED!>Int<!>>()
class X<A, B : A>()
@@ -3,7 +3,7 @@ package c
import java.util.ArrayList
fun Array<Int>.toIntArray(): IntArray = this.<!NI;TYPE_MISMATCH!><!OI;TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>mapTo<!>(<!NI;TYPE_MISMATCH!>IntArray(size)<!>, {<!NI;TYPE_MISMATCH!>it<!>})<!>
fun Array<Int>.toIntArray(): IntArray = this.<!NI;TYPE_MISMATCH!><!OI;TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>mapTo<!>(<!NI;TYPE_MISMATCH!>IntArray(size)<!>, {it})<!>
fun Array<Int>.toArrayList(): ArrayList<Int> = this.mapTo(ArrayList<Int>(size), {it})
@@ -9,5 +9,5 @@ fun <T: Inv2<T>> foo(klass: Inv<T>): String? = null
fun <X> bar(): Inv<X> = null!!
fun test() {
<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>foo<!>(<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>bar<!>())
<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>foo<!>(<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>bar<!>())
}
@@ -0,0 +1,32 @@
// !WITH_NEW_INFERENCE
// SKIP_JAVAC
// FILE: MySettings.java
import java.util.Collection;
public class MySettings<
SS extends MySettings<SS, PS, L>,
PS extends MyComparableSettings,
L extends MySettingsListener<PS>
>
{
public Collection<PS> getLinkedProjectsSettings() {
return null;
}
public static MySettings getSettings() {
return null;
}
}
abstract class MyComparableSettings implements Comparable<MyComparableSettings> {}
abstract class MySettingsListener<S extends MyComparableSettings> {}
// FILE: test.kt
fun test() {
val a = MySettings.getSettings()
a.getLinkedProjectsSettings()
a.<!NI;DEBUG_INFO_UNRESOLVED_WITH_TARGET, NI;UNRESOLVED_REFERENCE_WRONG_RECEIVER, OI;DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE, OI;TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>linkedProjectsSettings<!>
}
@@ -0,0 +1,14 @@
package
public fun test(): kotlin.Unit
public open class MySettings</*0*/ SS : MySettings<SS!, PS!, L!>!, /*1*/ PS : MyComparableSettings!, /*2*/ L : MySettingsListener<PS!>!> {
public constructor MySettings</*0*/ SS : MySettings<SS!, PS!, L!>!, /*1*/ PS : MyComparableSettings!, /*2*/ L : MySettingsListener<PS!>!>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open fun getLinkedProjectsSettings(): kotlin.collections.(Mutable)Collection<PS!>!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun getSettings(): MySettings<(raw) MySettings<*, *, *>!, (raw) MyComparableSettings!, (raw) MySettingsListener<*>!>!
}
@@ -0,0 +1,32 @@
// !WITH_NEW_INFERENCE
// SKIP_JAVAC
// FILE: MySettings.java
import java.util.Collection
class MySettings<
SS extends MySettings<SS, PS, L>,
PS extends MyComparableSettings,
L extends MySettingsListener<PS>
>
{
public Collection<PS> getLinkedProjectsSettings() {
return null;
}
public static MySettings<?, ?, ?> getSettings() {
return null;
}
}
abstract class MyComparableSettings implements Comparable<MyComparableSettings> {}
abstract class MySettingsListener<S extends MyComparableSettings> {}
// FILE: test.kt
fun test() {
val a = MySettings.getSettings()
a.getLinkedProjectsSettings()
a.<!OI;DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE, OI;TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>linkedProjectsSettings<!>
}
@@ -0,0 +1,14 @@
package
public fun test(): kotlin.Unit
public/*package*/ open class MySettings</*0*/ SS : MySettings<SS!, PS!, L!>!, /*1*/ PS : MyComparableSettings!, /*2*/ L : MySettingsListener<PS!>!> {
public/*package*/ constructor MySettings</*0*/ SS : MySettings<SS!, PS!, L!>!, /*1*/ PS : MyComparableSettings!, /*2*/ L : MySettingsListener<PS!>!>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open fun getLinkedProjectsSettings(): kotlin.collections.(Mutable)Collection<PS!>!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun getSettings(): MySettings<*, *, *>!
}
@@ -7,7 +7,7 @@ fun <T> g (<!UNUSED_PARAMETER!>f<!>: () -> List<T>) : T {<!NO_RETURN_IN_FUNCTION
fun test() {
//here possibly can be a cycle on constraints
val <!UNUSED_VARIABLE!>x<!> = g { Collections.<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>emptyList<!>() }
val <!UNUSED_VARIABLE!>x<!> = <!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>g<!> { Collections.<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>emptyList<!>() }
val <!UNUSED_VARIABLE!>y<!> = g<Int> { Collections.emptyList() }
val <!UNUSED_VARIABLE!>z<!> : List<Int> = g { Collections.<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>emptyList<!>() }
@@ -1,7 +1,7 @@
package
public val test1: [ERROR : Type for BOutIn(listOf(), null!!)]
public val test2: BInIn<kotlin.collections.List<kotlin.collections.List<kotlin.Any?>>> /* = Bound<in kotlin.collections.List<kotlin.collections.List<kotlin.collections.List<kotlin.Any?>>>, in kotlin.collections.List<kotlin.collections.List<kotlin.Any?>>> */
public val test2: BInIn<kotlin.Nothing> /* = Bound<in kotlin.collections.List<kotlin.Nothing>, in kotlin.Nothing> */
public fun </*0*/ T> listOf(): kotlin.collections.List<T>
public final class Bound</*0*/ X, /*1*/ Y : X> {
@@ -10066,6 +10066,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/inference/constraints/kt8879.kt");
}
@TestMetadata("manyConstraintsDueToFlexibleRawTypes.kt")
public void testManyConstraintsDueToFlexibleRawTypes() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/constraints/manyConstraintsDueToFlexibleRawTypes.kt");
}
@TestMetadata("manyConstraintsDueToRecursiveFlexibleTypesWithWildcards.kt")
public void testManyConstraintsDueToRecursiveFlexibleTypesWithWildcards() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/constraints/manyConstraintsDueToRecursiveFlexibleTypesWithWildcards.kt");
}
@TestMetadata("notNullConstraintOnNullableType.kt")
public void testNotNullConstraintOnNullableType() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/constraints/notNullConstraintOnNullableType.kt");
@@ -10061,6 +10061,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/inference/constraints/kt8879.kt");
}
@TestMetadata("manyConstraintsDueToFlexibleRawTypes.kt")
public void testManyConstraintsDueToFlexibleRawTypes() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/constraints/manyConstraintsDueToFlexibleRawTypes.kt");
}
@TestMetadata("manyConstraintsDueToRecursiveFlexibleTypesWithWildcards.kt")
public void testManyConstraintsDueToRecursiveFlexibleTypesWithWildcards() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/constraints/manyConstraintsDueToRecursiveFlexibleTypesWithWildcards.kt");
}
@TestMetadata("notNullConstraintOnNullableType.kt")
public void testNotNullConstraintOnNullableType() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/constraints/notNullConstraintOnNullableType.kt");
@@ -127,12 +127,12 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext {
return this.constructor
}
override fun SimpleTypeMarker.argumentsCount(): Int {
require(this is SimpleType, this::errorMessage)
override fun KotlinTypeMarker.argumentsCount(): Int {
require(this is KotlinType, this::errorMessage)
return this.arguments.size
}
override fun SimpleTypeMarker.getArgument(index: Int): TypeArgumentMarker {
override fun KotlinTypeMarker.getArgument(index: Int): TypeArgumentMarker {
require(this is SimpleType, this::errorMessage)
return this.arguments[index]
}
@@ -167,8 +167,8 @@ interface TypeSystemContext : TypeSystemOptimizationContext {
fun SimpleTypeMarker.withNullability(nullable: Boolean): SimpleTypeMarker
fun SimpleTypeMarker.typeConstructor(): TypeConstructorMarker
fun SimpleTypeMarker.argumentsCount(): Int
fun SimpleTypeMarker.getArgument(index: Int): TypeArgumentMarker
fun KotlinTypeMarker.argumentsCount(): Int
fun KotlinTypeMarker.getArgument(index: Int): TypeArgumentMarker
fun SimpleTypeMarker.getArgumentOrNull(index: Int): TypeArgumentMarker? {
if (index in 0 until argumentsCount()) return getArgument(index)