From 2656eeb1641814cca77e9d4fac692976de51433c Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Sun, 7 Jun 2020 18:14:45 +0200 Subject: [PATCH] NI: Optimize some potential hot places --- .../calls/components/ResolutionParts.kt | 34 ++--- .../components/ConstraintIncorporator.kt | 9 +- .../PostponedArgumentInputTypesResolver.kt | 128 ++++++++++-------- .../model/MutableConstraintStorage.kt | 22 +-- 4 files changed, 106 insertions(+), 87 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt index 19a6decf14a..adf7e5a71b8 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.types.model.typeConstructor import org.jetbrains.kotlin.types.typeUtil.contains import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.makeNullable +import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.addToStdlib.safeAs internal object CheckVisibility : ResolutionPart() { @@ -314,27 +315,28 @@ internal object CollectionTypeVariableUsagesInfo : ResolutionPart() { dependentTypeParametersSeen: List> = listOf() ): List> { val context = asConstraintSystemCompleterContext() - val dependentTypeParameters = getBuilder().currentStorage().notFixedTypeVariables.mapNotNull { (typeConstructor, constraints) -> - val upperBounds = constraints.constraints.filter { - it.position.from is DeclaredUpperBoundConstraintPosition && it.kind == ConstraintKind.UPPER - } + val dependentTypeParameters = getBuilder().currentStorage().notFixedTypeVariables.asSequence() + .flatMap { (typeConstructor, constraints) -> + val upperBounds = constraints.constraints.filter { + it.position.from is DeclaredUpperBoundConstraintPosition && it.kind == ConstraintKind.UPPER + } - upperBounds.mapNotNull { constraint -> - if (constraint.type.typeConstructor(context) != variable) { - val suitableUpperBound = upperBounds.find { upperBound -> - with(context) { upperBound.type.contains { it.typeConstructor() == variable } } - }?.type + upperBounds.mapNotNull { constraint -> + if (constraint.type.typeConstructor(context) != variable) { + val suitableUpperBound = upperBounds.find { upperBound -> + with(context) { upperBound.type.contains { it.typeConstructor() == variable } } + }?.type - if (suitableUpperBound != null) typeConstructor to suitableUpperBound else null - } else typeConstructor to null - } - }.flatten().filter { it !in dependentTypeParametersSeen && it.first != variable } + if (suitableUpperBound != null) typeConstructor to suitableUpperBound else null + } else typeConstructor to null + } + }.filter { it !in dependentTypeParametersSeen && it.first != variable }.toList() - return dependentTypeParameters + dependentTypeParameters.mapNotNull { (typeConstructor, _) -> + return dependentTypeParameters + dependentTypeParameters.flatMapTo(SmartList()) { (typeConstructor, _) -> if (typeConstructor != variable) { getDependentTypeParameters(typeConstructor, dependentTypeParameters + dependentTypeParametersSeen) - } else null - }.flatten() + } else emptyList() + } } private fun NewConstraintSystem.isContainedInInvariantOrContravariantPositionsAmongUpperBound( diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt index 15776a2b2b4..f51a414b39d 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.resolve.calls.components.CreateFreshVariablesSubstit import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.types.* 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 org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -98,7 +99,7 @@ class ConstraintIncorporator( for (otherTypeVariable in otherInMyConstraint) { // to avoid ConcurrentModificationException - val otherConstraints = ArrayList(this.getConstraintsForVariable(otherTypeVariable)) + val otherConstraints = SmartList(this.getConstraintsForVariable(otherTypeVariable)) for (otherConstraint in otherConstraints) { generateNewConstraint(typeVariable, constraint, otherTypeVariable, otherConstraint) } @@ -230,7 +231,7 @@ class ConstraintIncorporator( ) ) return - val derivedFrom = (baseConstraint.derivedFrom + otherConstraint.derivedFrom).toMutableSet() + val derivedFrom = SmartSet.create(baseConstraint.derivedFrom).also { it.addAll(otherConstraint.derivedFrom) } if (otherVariable in derivedFrom) return derivedFrom.add(otherVariable) @@ -273,7 +274,7 @@ class ConstraintIncorporator( } fun Context.getNestedTypeVariables(type: KotlinTypeMarker): List = - getNestedArguments(type).mapNotNull { getTypeVariable(it.getType().typeConstructor()) } + getNestedArguments(type).mapNotNullTo(SmartList()) { getTypeVariable(it.getType().typeConstructor()) } private fun KotlinTypeMarker.substitute(c: Context, typeVariable: TypeVariableMarker, value: KotlinTypeMarker): KotlinTypeMarker { @@ -288,7 +289,7 @@ class ConstraintIncorporator( } private fun TypeSystemInferenceExtensionContext.getNestedArguments(type: KotlinTypeMarker): List { - val result = ArrayList() + val result = SmartList() val stack = ArrayDeque() when (type) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt index 7bf47656283..4a288a28568 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.types.typeUtil.builtIns import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.addToStdlib.safeAs class PostponedArgumentInputTypesResolver( @@ -21,7 +22,7 @@ class PostponedArgumentInputTypesResolver( ) { interface Context : KotlinConstraintSystemCompleter.Context - data class ParameterTypesInfo( + private class ParameterTypesInfo( val parametersFromDeclaration: List?, val parametersFromDeclarationOfRelatedLambdas: Set>?, val parametersFromConstraints: Set>?, @@ -48,11 +49,11 @@ class PostponedArgumentInputTypesResolver( val dependentVariables = variableDependencyProvider.getShallowlyDependentVariables(typeVariableTypeConstructor).orEmpty() + typeVariableTypeConstructor - return dependentVariables.mapNotNull { type -> - val constraints = notFixedTypeVariables[type]?.constraints ?: return@mapNotNull null + return dependentVariables.flatMap { type -> + val constraints = notFixedTypeVariables[type]?.constraints ?: return@flatMap emptyList() val constraintsWithFunctionalType = constraints.filter { (it.type as? KotlinType)?.isBuiltinFunctionalTypeOrSubtype == true } constraintsWithFunctionalType.extractFunctionalTypes() - }.flatten() + } } private fun extractParameterTypesFromDeclaration(atom: ResolutionAtom) = @@ -81,32 +82,42 @@ class PostponedArgumentInputTypesResolver( val parameterTypesFromDeclaration = if (argument is LambdaWithTypeVariableAsExpectedTypeAtom) argument.parameterTypesFromDeclaration else null - val parameterTypesFromConstraints = functionalTypesFromConstraints?.map { typeWithKind -> + val parameterTypesFromConstraints = functionalTypesFromConstraints?.mapTo(SmartSet.create()) { typeWithKind -> typeWithKind.type.getPureArgumentsForFunctionalTypeOrSubtype().map { // We should use opposite kind as lambda's parameters are contravariant TypeWithKind(it, typeWithKind.direction.opposite()) } - }?.toSet() + } // An extension function flag can only come from a declaration of anonymous function: `select({ this + it }, fun Int.(x: Int) = 10)` val (parameterTypesFromDeclarationOfRelatedLambdas, isThereExtensionFunctionAmongRelatedLambdas) = getDeclaredParametersFromRelatedLambdas(argument, postponedArguments, variableDependencyProvider) val annotationsFromConstraints = functionalTypesFromConstraints?.run { - Annotations.create(map { it.type.annotations }.flatten()) + Annotations.create(flatMap { it.type.annotations }) } ?: Annotations.EMPTY val annotations = if (isThereExtensionFunctionAmongRelatedLambdas) { annotationsFromConstraints.withExtensionFunctionAnnotation(expectedType.builtIns) } else annotationsFromConstraints + var isSuspend = false + var isNullable = false + if (!functionalTypesFromConstraints.isNullOrEmpty()) { + isNullable = true + for (funType in functionalTypesFromConstraints) { + if (!isSuspend && funType.type.isSuspendFunctionTypeOrSubtype) isSuspend = true + if (isNullable && !funType.type.isMarkedNullable) isNullable = false + if (isSuspend && !isNullable) break + } + } return ParameterTypesInfo( parameterTypesFromDeclaration, parameterTypesFromDeclarationOfRelatedLambdas, parameterTypesFromConstraints, annotations, - isSuspend = !functionalTypesFromConstraints.isNullOrEmpty() && functionalTypesFromConstraints.any { it.type.isSuspendFunctionTypeOrSubtype }, - isNullable = !functionalTypesFromConstraints.isNullOrEmpty() && functionalTypesFromConstraints.all { it.type.isMarkedNullable } + isSuspend = isSuspend, + isNullable = isNullable ) } @@ -116,24 +127,28 @@ class PostponedArgumentInputTypesResolver( dependencyProvider: TypeVariableDependencyInformationProvider ): Pair>?, Boolean> { val parameterTypesFromDeclarationOfRelatedLambdas = postponedArguments - .filterIsInstance() - .filter { it.parameterTypesFromDeclaration != null && it != argument } .mapNotNull { anotherArgument -> - val argumentExpectedTypeConstructor = argument.expectedType?.typeConstructor() ?: return@mapNotNull null - val anotherArgumentExpectedTypeConstructor = anotherArgument.expectedType.typeConstructor() - val areTypeVariablesRelated = dependencyProvider.areVariablesDependentShallowly( - argumentExpectedTypeConstructor, anotherArgumentExpectedTypeConstructor - ) - val anotherAtom = anotherArgument.atom - val isAnonymousExtensionFunction = anotherAtom is FunctionExpression && anotherAtom.receiverType != null - val parameterTypesFromDeclarationOfRelatedLambda = anotherArgument.parameterTypesFromDeclaration + when { + anotherArgument !is LambdaWithTypeVariableAsExpectedTypeAtom -> null + anotherArgument.parameterTypesFromDeclaration == null || anotherArgument == argument -> null + else -> { + val argumentExpectedTypeConstructor = argument.expectedType?.typeConstructor() ?: return@mapNotNull null + val anotherArgumentExpectedTypeConstructor = anotherArgument.expectedType.typeConstructor() + val areTypeVariablesRelated = dependencyProvider.areVariablesDependentShallowly( + argumentExpectedTypeConstructor, anotherArgumentExpectedTypeConstructor + ) + val anotherAtom = anotherArgument.atom + val isAnonymousExtensionFunction = anotherAtom is FunctionExpression && anotherAtom.receiverType != null + val parameterTypesFromDeclarationOfRelatedLambda = anotherArgument.parameterTypesFromDeclaration - if (areTypeVariablesRelated && parameterTypesFromDeclarationOfRelatedLambda != null) { - parameterTypesFromDeclarationOfRelatedLambda to isAnonymousExtensionFunction - } else null + if (areTypeVariablesRelated && parameterTypesFromDeclarationOfRelatedLambda != null) { + parameterTypesFromDeclarationOfRelatedLambda to isAnonymousExtensionFunction + } else null + } + } } - return parameterTypesFromDeclarationOfRelatedLambdas.run { map { it.first }.toSet() to any { it.second } } + return parameterTypesFromDeclarationOfRelatedLambdas.run { mapTo(SmartSet.create()) { it.first } to any { it.second } } } private fun Context.createTypeVariableForReturnType(argument: PostponedAtomWithRevisableExpectedType): NewTypeVariable { @@ -186,7 +201,8 @@ class PostponedArgumentInputTypesResolver( return allGroupedParameterTypes.mapIndexed { index, types -> val parameterTypeVariable = createTypeVariableForParameterType(argument, index) - for (typeWithKind in types.filterNotNull()) { + for (typeWithKind in types) { + if (typeWithKind == null) continue when (typeWithKind.direction) { ConstraintKind.EQUALITY -> csBuilder.addEqualityConstraint( parameterTypeVariable.defaultType, typeWithKind.type, ArgumentConstraintPosition(atom) @@ -327,29 +343,23 @@ class PostponedArgumentInputTypesResolver( dependencyProvider: TypeVariableDependencyInformationProvider ): Boolean { // We can collect parameter types from declaration in any mode, they can't change during completion. - val postponedArgumentsToCollectTypesFromDeclaredParameters = postponedArguments - .filterIsInstance() - .filter { it.parameterTypesFromDeclaration == null } - - for (argument in postponedArgumentsToCollectTypesFromDeclaredParameters) { + for (argument in postponedArguments) { + if (argument !is LambdaWithTypeVariableAsExpectedTypeAtom) continue + if (argument.parameterTypesFromDeclaration != null) continue argument.parameterTypesFromDeclaration = extractParameterTypesFromDeclaration(argument.atom) } - /* - * We can build new functional expected types in partial mode only for anonymous functions, - * because more exact type can't appear from constraints in full mode (anonymous functions have fully explicit declaration). - * It can be so for lambdas: for instance, an extension function type can appear in full mode (it may not be known in partial mode). - * - * TODO: investigate why we can't do it for anonymous functions in full mode always (see `diagnostics/tests/resolve/resolveWithSpecifiedFunctionLiteralWithId.kt`) - */ - val postponedArgumentsToCollectParameterTypesAndBuildNewExpectedType = - if (completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL) { - postponedArguments.filter(::isAnonymousFunction) - } else { - postponedArguments - } - - return postponedArgumentsToCollectParameterTypesAndBuildNewExpectedType.filter { it.revisedExpectedType == null }.any { argument -> + return postponedArguments.any { argument -> + /* + * We can build new functional expected types in partial mode only for anonymous functions, + * because more exact type can't appear from constraints in full mode (anonymous functions have fully explicit declaration). + * It can be so for lambdas: for instance, an extension function type can appear in full mode (it may not be known in partial mode). + * + * TODO: investigate why we can't do it for anonymous functions in full mode always (see `diagnostics/tests/resolve/resolveWithSpecifiedFunctionLiteralWithId.kt`) + */ + if (completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL && !isAnonymousFunction(argument)) + return@any false + if (argument.revisedExpectedType != null) return@any false val parameterTypesInfo = c.extractParameterTypesInfo(argument, postponedArguments, dependencyProvider) ?: return@any false val newExpectedType = @@ -393,26 +403,28 @@ class PostponedArgumentInputTypesResolver( listOf(typeConstructor) + relatedVariables.filterIsInstance() } type.arguments.isNotEmpty() -> { - type.arguments.map { getAllDeeplyRelatedTypeVariables(it.type, variableDependencyProvider) }.flatten() + type.arguments.flatMap { getAllDeeplyRelatedTypeVariables(it.type, variableDependencyProvider) } } - else -> listOf() + else -> emptyList() } } - private fun getDeclaredParametersConsideringExtensionFunctionsPresence(parameterTypesInfo: ParameterTypesInfo): List? { - val (parametersFromDeclaration, _, parametersFromConstraints, annotations) = parameterTypesInfo + private fun getDeclaredParametersConsideringExtensionFunctionsPresence(parameterTypesInfo: ParameterTypesInfo): List? = + with (parameterTypesInfo) { - if (parametersFromConstraints.isNullOrEmpty() || parametersFromDeclaration.isNullOrEmpty()) - return parametersFromDeclaration + if (parametersFromConstraints.isNullOrEmpty() || parametersFromDeclaration.isNullOrEmpty()) + parametersFromDeclaration + else { + val oneLessParameterInDeclarationThanInConstraints = + parametersFromConstraints.first().size == parametersFromDeclaration.size + 1 - val oneLessParameterInDeclarationThanInConstraints = parametersFromConstraints.first().size == parametersFromDeclaration.size + 1 - - return if (oneLessParameterInDeclarationThanInConstraints && annotations.hasExtensionFunctionAnnotation()) { - listOf(null) + parametersFromDeclaration - } else { - parametersFromDeclaration + if (oneLessParameterInDeclarationThanInConstraints && annotations.hasExtensionFunctionAnnotation()) { + listOf(null) + parametersFromDeclaration + } else { + parametersFromDeclaration + } + } } - } fun fixNextReadyVariableForParameterTypeIfNeeded( c: Context, @@ -443,7 +455,7 @@ class PostponedArgumentInputTypesResolver( dependencyProvider: TypeVariableDependencyInformationProvider ): Boolean { val relatedVariables = type.getPureArgumentsForFunctionalTypeOrSubtype() - .map { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) }.flatten() + .flatMap { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) } val variableForFixation = variableFixationFinder.findFirstVariableForFixation( this, relatedVariables, postponedArguments, KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL, topLevelType ) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt index 256324ce33e..7a0619c027a 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker import org.jetbrains.kotlin.types.typeUtil.unCapture +import org.jetbrains.kotlin.utils.SmartList class MutableVariableWithConstraints private constructor( @@ -36,12 +37,15 @@ class MutableVariableWithConstraints private constructor( // see @OnlyInputTypes annotation val projectedInputCallTypes: Collection get() = mutableConstraints - .filter { it.position.from is OnlyInputTypeConstraintPosition || it.inputTypePositionBeforeIncorporation != null } - .map { (it.type as KotlinType).unCapture().unwrap() } + .mapNotNullTo(SmartList()) { + if (it.position.from is OnlyInputTypeConstraintPosition || it.inputTypePositionBeforeIncorporation != null) + (it.type as KotlinType).unCapture().unwrap() + else null + } - private val mutableConstraints = if (constraints == null) ArrayList() else ArrayList(constraints) + private val mutableConstraints = if (constraints == null) SmartList() else SmartList(constraints) - private var simplifiedConstraints: ArrayList? = mutableConstraints + private var simplifiedConstraints: SmartList? = mutableConstraints // return new actual constraint, if this constraint is new fun addConstraint(constraint: Constraint): Constraint? { @@ -110,13 +114,13 @@ class MutableVariableWithConstraints private constructor( } } - private fun ArrayList.simplifyConstraints(): ArrayList { + private fun SmartList.simplifyConstraints(): SmartList { val equalityConstraints = filter { it.kind == ConstraintKind.EQUALITY } .groupBy { it.typeHashCode } return when { equalityConstraints.isEmpty() -> this - else -> filterTo(ArrayList()) { isUsefulConstraint(it, equalityConstraints) } + else -> filterTo(SmartList()) { isUsefulConstraint(it, equalityConstraints) } } } @@ -134,10 +138,10 @@ class MutableVariableWithConstraints private constructor( internal class MutableConstraintStorage : ConstraintStorage { override val allTypeVariables: MutableMap = LinkedHashMap() override val notFixedTypeVariables: MutableMap = LinkedHashMap() - override val initialConstraints: MutableList = ArrayList() + override val initialConstraints: MutableList = SmartList() override var maxTypeDepthFromInitialConstraints: Int = 1 - override val errors: MutableList = ArrayList() + override val errors: MutableList = SmartList() override val hasContradiction: Boolean get() = errors.any { !it.candidateApplicability.isSuccess } override val fixedTypeVariables: MutableMap = LinkedHashMap() - override val postponedTypeVariables: ArrayList = ArrayList() + override val postponedTypeVariables: MutableList = SmartList() }