[NI] Fix poor performance of recursive types approximation
#KT-32407 Fixed
This commit is contained in:
+5
@@ -10212,6 +10212,11 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok
|
||||
runTest("compiler/testData/diagnostics/tests/inference/constraints/operationsOnIntegerValueTypes.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveJavaTypeWithStarProjection.kt")
|
||||
public void testRecursiveJavaTypeWithStarProjection() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/constraints/recursiveJavaTypeWithStarProjection.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("returnLambdaFromLambda.kt")
|
||||
public void testReturnLambdaFromLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/constraints/returnLambdaFromLambda.kt");
|
||||
|
||||
@@ -106,7 +106,10 @@ class PSICallResolver(
|
||||
return OverloadResolutionResultsImpl.nameNotFound()
|
||||
}
|
||||
|
||||
return convertToOverloadResolutionResults(context, result, tracingStrategy)
|
||||
val overloadResolutionResults = convertToOverloadResolutionResults<D>(context, result, tracingStrategy)
|
||||
return overloadResolutionResults.also {
|
||||
clearCacheForApproximationResults()
|
||||
}
|
||||
}
|
||||
|
||||
// actually, `D` is at least FunctionDescriptor, but right now because of CallResolver it isn't possible change upper bound for `D`
|
||||
@@ -133,7 +136,16 @@ class PSICallResolver(
|
||||
val result = kotlinCallResolver.resolveGivenCandidates(
|
||||
scopeTower, resolutionCallbacks, kotlinCall, calculateExpectedType(context), givenCandidates, context.collectAllCandidates
|
||||
)
|
||||
return convertToOverloadResolutionResults(context, result, tracingStrategy)
|
||||
val overloadResolutionResults = convertToOverloadResolutionResults<D>(context, result, tracingStrategy)
|
||||
return overloadResolutionResults.also {
|
||||
clearCacheForApproximationResults()
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearCacheForApproximationResults() {
|
||||
// Mostly, we approximate captured or some other internal types that don't live longer than resolve for a call,
|
||||
// so it's quite useless to preserve cache for longer time
|
||||
typeApproximator.clearCache()
|
||||
}
|
||||
|
||||
private fun resolveToDeprecatedMod(
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.types.TypeApproximatorConfiguration.IntersectionStra
|
||||
import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import org.jetbrains.kotlin.types.model.CaptureStatus.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
|
||||
open class TypeApproximatorConfiguration {
|
||||
@@ -119,6 +120,10 @@ class TypeApproximator(builtIns: KotlinBuiltIns) : AbstractTypeApproximator(Clas
|
||||
|
||||
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 = this::approximateSimpleToSuperType
|
||||
private val referenceApproximateToSubType = this::approximateSimpleToSubType
|
||||
@@ -136,23 +141,69 @@ abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionCon
|
||||
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 is TypeUtils.SpecialType ->
|
||||
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
|
||||
return cache.getOrPut(type, { approximate().toApproximationResult() }).type
|
||||
}
|
||||
|
||||
private fun approximateToSuperType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration, depth: Int): KotlinTypeMarker? {
|
||||
if (type is TypeUtils.SpecialType) return null
|
||||
return approximateTo(
|
||||
prepareType(type), conf, { upperBound() },
|
||||
referenceApproximateToSuperType, depth
|
||||
)
|
||||
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? {
|
||||
if (type is TypeUtils.SpecialType) return null
|
||||
return approximateTo(
|
||||
prepareType(type), conf, { lowerBound() },
|
||||
referenceApproximateToSubType, depth
|
||||
)
|
||||
checkExceptionalCases(type, depth, conf, toSuper = false)?.let { return it.type }
|
||||
|
||||
return cachedValue(type, conf, toSuper = false) {
|
||||
approximateTo(
|
||||
prepareType(type), conf, { lowerBound() },
|
||||
referenceApproximateToSubType, depth
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// comments for case bound = upperBound, approximateTo = toSuperType
|
||||
// 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,
|
||||
@@ -330,13 +381,6 @@ abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionCon
|
||||
toSuper: Boolean,
|
||||
depth: Int
|
||||
): KotlinTypeMarker? {
|
||||
if (type.isError()) {
|
||||
// todo -- fix builtIns. Now builtIns here is DefaultBuiltIns
|
||||
return if (conf.errorType) null else type.defaultResult(toSuper)
|
||||
}
|
||||
|
||||
if (depth > 3) return type.defaultResult(toSuper)
|
||||
|
||||
if (type.argumentsCount() != 0) {
|
||||
return approximateParametrizedType(type, conf, toSuper, depth + 1)
|
||||
}
|
||||
@@ -382,12 +426,15 @@ abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionCon
|
||||
toSuper: Boolean,
|
||||
depth: Int
|
||||
): KotlinTypeMarker? {
|
||||
val approximatedOriginalType = approximateTo(type.original(), conf, toSuper, depth)
|
||||
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 ?: type.original()).withNullability(false)
|
||||
(approximatedOriginalType ?: originalType).withNullability(false)
|
||||
else
|
||||
type.defaultResult(toSuper)
|
||||
}
|
||||
@@ -519,8 +566,8 @@ abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionCon
|
||||
return type.replaceArguments(newArgumentsList)
|
||||
}
|
||||
|
||||
private fun SimpleTypeMarker.defaultResult(toSuper: Boolean) = if (toSuper) nullableAnyType() else {
|
||||
if (isMarkedNullable()) nullableNothingType() else nothingType()
|
||||
private fun KotlinTypeMarker.defaultResult(toSuper: Boolean) = if (toSuper) nullableAnyType() else {
|
||||
if (this is SimpleTypeMarker && isMarkedNullable()) nullableNothingType() else nothingType()
|
||||
}
|
||||
|
||||
// Any? or Any!
|
||||
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
// !LANGUAGE: +NewInference
|
||||
|
||||
// FILE: MenuItemBase.java
|
||||
|
||||
public class MenuItemBase<C extends ContextMenuBase<C, I, S>, I extends MenuItemBase<C, I, S>, S extends SubMenuBase<C, I, S>> {
|
||||
public String getText() { return null; }
|
||||
}
|
||||
|
||||
// FILE: ContextMenuBase.java
|
||||
|
||||
public class ContextMenuBase<C extends ContextMenuBase<C, I, S>, I extends MenuItemBase<C, I, S>, S extends SubMenuBase<C, I, S>> {}
|
||||
|
||||
// FILE: SubMenuBase.java
|
||||
|
||||
public class SubMenuBase<C extends ContextMenuBase<C, I, S>, I extends MenuItemBase<C, I, S>, S extends SubMenuBase<C, I, S>> {}
|
||||
|
||||
// FILE: test.kt
|
||||
|
||||
fun test(m: MenuItemBase<*, *, *>) {
|
||||
m.text
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
package
|
||||
|
||||
public fun test(/*0*/ m: MenuItemBase<*, *, *>): kotlin.Unit
|
||||
|
||||
public open class ContextMenuBase</*0*/ C : ContextMenuBase<C!, I!, S!>!, /*1*/ I : MenuItemBase<C!, I!, S!>!, /*2*/ S : SubMenuBase<C!, I!, S!>!> {
|
||||
public constructor ContextMenuBase</*0*/ C : ContextMenuBase<C!, I!, S!>!, /*1*/ I : MenuItemBase<C!, I!, S!>!, /*2*/ S : SubMenuBase<C!, I!, S!>!>()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public open class MenuItemBase</*0*/ C : ContextMenuBase<C!, I!, S!>!, /*1*/ I : MenuItemBase<C!, I!, S!>!, /*2*/ S : SubMenuBase<C!, I!, S!>!> {
|
||||
public constructor MenuItemBase</*0*/ C : ContextMenuBase<C!, I!, S!>!, /*1*/ I : MenuItemBase<C!, I!, S!>!, /*2*/ S : SubMenuBase<C!, I!, S!>!>()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open fun getText(): kotlin.String!
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public open class SubMenuBase</*0*/ C : ContextMenuBase<C!, I!, S!>!, /*1*/ I : MenuItemBase<C!, I!, S!>!, /*2*/ S : SubMenuBase<C!, I!, S!>!> {
|
||||
public constructor SubMenuBase</*0*/ C : ContextMenuBase<C!, I!, S!>!, /*1*/ I : MenuItemBase<C!, I!, S!>!, /*2*/ S : SubMenuBase<C!, I!, S!>!>()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -10219,6 +10219,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/constraints/operationsOnIntegerValueTypes.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveJavaTypeWithStarProjection.kt")
|
||||
public void testRecursiveJavaTypeWithStarProjection() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/constraints/recursiveJavaTypeWithStarProjection.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("returnLambdaFromLambda.kt")
|
||||
public void testReturnLambdaFromLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/constraints/returnLambdaFromLambda.kt");
|
||||
|
||||
Generated
+5
@@ -10214,6 +10214,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
||||
runTest("compiler/testData/diagnostics/tests/inference/constraints/operationsOnIntegerValueTypes.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveJavaTypeWithStarProjection.kt")
|
||||
public void testRecursiveJavaTypeWithStarProjection() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/constraints/recursiveJavaTypeWithStarProjection.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("returnLambdaFromLambda.kt")
|
||||
public void testReturnLambdaFromLambda() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/constraints/returnLambdaFromLambda.kt");
|
||||
|
||||
Reference in New Issue
Block a user