From e011e443ccfb70b28e2d1d166ebad5a24e601177 Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Thu, 13 Jul 2017 19:09:24 +0300 Subject: [PATCH] [NI] Implement next call completer. Type inference completer features: - type variables depended from result type will be fixed in the end - type variables with proper constraints will be fixed first - fixation via groups "accessible" via constraints is supported TODO: - stable order via PSI order - argument constraint should rewrite position if constraint is the same as upper bound for type parameter --- .../calls/KotlinResolutionConfiguration.kt | 6 - .../components/CallableReferenceResolution.kt | 28 +- .../components/CallableReferenceResolver.kt | 4 +- .../components/ConstraintSystemCompleter.kt | 53 ---- .../InitialConstraintSystemCompleterImpl.kt | 79 ------ .../calls/components/KotlinCallCompleter.kt | 42 +-- .../calls/components/SimpleArgumentsChecks.kt | 3 +- .../inference/ConstraintSystemBuilder.kt | 2 + .../calls/inference/NewConstraintSystem.kt | 2 +- .../ConstraintSystemCompleterImpl.kt | 139 ++++++++++ .../components/FixationOrderCalculator.kt | 258 ------------------ .../components/InferenceStepResolver.kt | 59 ---- .../components/ResultTypeResolver.kt | 20 +- ...peVariableDependencyInformationProvider.kt | 132 +++++++++ .../TypeVariableDirectionCalculator.kt | 152 +++++++++++ .../components/VariableFixationFinder.kt | 100 +++++++ .../model/NewConstraintSystemImpl.kt | 11 +- .../model/PostponedKotlinCallArguments.kt | 41 ++- 18 files changed, 622 insertions(+), 509 deletions(-) delete mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ConstraintSystemCompleter.kt delete mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/InitialConstraintSystemCompleterImpl.kt create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompleterImpl.kt delete mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt delete mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/InferenceStepResolver.kt create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDirectionCalculator.kt create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinResolutionConfiguration.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinResolutionConfiguration.kt index 882b346c666..c7a276c08a6 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinResolutionConfiguration.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinResolutionConfiguration.kt @@ -19,9 +19,3 @@ package org.jetbrains.kotlin.resolve.calls val USE_NEW_INFERENCE = false val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC = false - -val USE_CS_COMPLETER_TYPE = CSCompleterType.INITIAL - -enum class CSCompleterType { - INITIAL -} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt index 2de5826fdd6..0f041f673d8 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt @@ -214,19 +214,7 @@ class CallableReferencesCandidateFactory( expectedType: UnwrappedType?, unboundReceiverCount: Int ): Triple, CoercionStrategy, Int>? { - if (expectedType == null) return null - - val functionType = - if (expectedType.isFunctionType) { - expectedType - } - else if (ReflectionTypes.isNumberedKFunction(expectedType)) { - expectedType.immediateSupertypes().first { it.isFunctionType } - } - else { - return null - } - + val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return null val expectedArgumentCount = functionType.arguments.size - unboundReceiverCount - 1 // 1 -- return type if (expectedArgumentCount < 0) return null @@ -334,3 +322,17 @@ class CallableReferencesCandidateFactory( } } } + +fun getFunctionTypeFromCallableReferenceExpectedType(expectedType: UnwrappedType?): UnwrappedType? { + if (expectedType == null) return null + + return if (expectedType.isFunctionType) { + expectedType + } + else if (ReflectionTypes.isNumberedKFunction(expectedType)) { + expectedType.immediateSupertypes().first { it.isFunctionType }.unwrap() + } + else { + null + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt index 9d8b0b32d15..f62e68064bb 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt @@ -65,8 +65,10 @@ class CallableReferenceResolver( csBuilder: ConstraintSystemBuilder, postponedArgument: PostponedCallableReferenceArgument ): KotlinCallDiagnostic? { + postponedArgument.analyzed = true + val argument = postponedArgument.argument - val expectedType = postponedArgument.expectedType + val expectedType = csBuilder.buildCurrentSubstitutor().safeSubstitute(postponedArgument.expectedType) val subLHSCall = argument.lhsResult.safeAs()?.lshCallArgument.safeAs() if (subLHSCall != null) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ConstraintSystemCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ConstraintSystemCompleter.kt deleted file mode 100644 index f1d8a9bca0f..00000000000 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ConstraintSystemCompleter.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2010-2017 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.components - -import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator -import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver -import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable -import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic -import org.jetbrains.kotlin.resolve.calls.model.PostponedKotlinCallArgument -import org.jetbrains.kotlin.types.UnwrappedType - -interface ConstraintSystemCompleter { - enum class CompletionType { - FULL, - PARTIAL - } - - interface Context { - val hasContradiction: Boolean - val postponedArguments: List - - // type can be proper if it not contains not fixed type variables - fun canBeProper(type: UnwrappedType): Boolean - fun asFixationOrderCalculatorContext(): FixationOrderCalculator.Context - fun asResultTypeResolverContext(): ResultTypeResolver.Context - - // mutable operations - fun addError(error: KotlinCallDiagnostic) - fun fixVariable(variable: NewTypeVariable, resultType: UnwrappedType) - } - - fun runCompletion( - c: Context, - type: CompletionType, - topLevelType: UnwrappedType, - analyze: (PostponedKotlinCallArgument) -> Unit - ) - -} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/InitialConstraintSystemCompleterImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/InitialConstraintSystemCompleterImpl.kt deleted file mode 100644 index b3b30660207..00000000000 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/InitialConstraintSystemCompleterImpl.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2010-2017 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.components - -import org.jetbrains.kotlin.resolve.calls.components.ConstraintSystemCompleter.Context -import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator -import org.jetbrains.kotlin.resolve.calls.inference.components.InferenceStepResolver -import org.jetbrains.kotlin.resolve.calls.model.PostponedCallableReferenceArgument -import org.jetbrains.kotlin.resolve.calls.model.PostponedKotlinCallArgument -import org.jetbrains.kotlin.resolve.calls.model.PostponedLambdaArgument -import org.jetbrains.kotlin.types.UnwrappedType - -class InitialConstraintSystemCompleterImpl( - private val fixationOrderCalculator: FixationOrderCalculator, - private val inferenceStepResolver: InferenceStepResolver -) : ConstraintSystemCompleter { - - override fun runCompletion( - c: Context, - type: ConstraintSystemCompleter.CompletionType, - topLevelType: UnwrappedType, - analyze: (PostponedKotlinCallArgument) -> Unit - ) { - if (type == ConstraintSystemCompleter.CompletionType.PARTIAL) return - - resolveCallableReferenceArguments(c, analyze) - - while (!oneStepToEndOrLambda(c, topLevelType, analyze)) { - // do nothing -- be happy - } - } - - private fun resolveCallableReferenceArguments(c: Context, analyze: (PostponedKotlinCallArgument) -> Unit) { - c.postponedArguments.filterIsInstance().forEach(analyze) - } - - // true if it is the end (happy or not) - // every step we fix type variable or analyzeLambda - private fun oneStepToEndOrLambda( - c: Context, - topLevelType: UnwrappedType, - analyze: (PostponedKotlinCallArgument) -> Unit - ): Boolean { - val lambda = c.postponedArguments.find { it is PostponedLambdaArgument && canWeAnalyzeIt(c, it) } - if (lambda != null) { - analyze(lambda) - return false - } - - val completionOrder = fixationOrderCalculator.computeCompletionOrder(c.asFixationOrderCalculatorContext(), topLevelType) - return inferenceStepResolver.resolveVariables(c, completionOrder) - } - - - private fun canWeAnalyzeIt(c: Context, lambda: PostponedLambdaArgument): Boolean { - if (lambda.analyzed) return false - - if (c.hasContradiction) return true - - lambda.receiver?.let { - if (!c.canBeProper(it)) return false - } - return lambda.parameters.all { c.canBeProper(it) } - } -} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt index 6bca96c81ea..59e36c9aaf9 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -19,9 +19,8 @@ package org.jetbrains.kotlin.resolve.calls.components import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.resolve.calls.CSCompleterType -import org.jetbrains.kotlin.resolve.calls.USE_CS_COMPLETER_TYPE -import org.jetbrains.kotlin.resolve.calls.components.ConstraintSystemCompleter.CompletionType +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompleter +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompleter.ConstraintSystemCompletionMode import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing @@ -37,11 +36,8 @@ import org.jetbrains.kotlin.types.typeUtil.contains class KotlinCallCompleter( private val additionalDiagnosticReporter: AdditionalDiagnosticReporter, private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer, - initialConstraintSystemCompleter: InitialConstraintSystemCompleterImpl + private val constraintSystemCompleter: ConstraintSystemCompleter ) { - private val constraintSystemCompleter: ConstraintSystemCompleter = when(USE_CS_COMPLETER_TYPE) { - CSCompleterType.INITIAL -> initialConstraintSystemCompleter - } interface Context { val innerCalls: List @@ -66,18 +62,26 @@ class KotlinCallCompleter( else -> candidate as SimpleKotlinResolutionCandidate } - val completionType = topLevelCall.prepareForCompletion(expectedType) - val constraintSystem = candidate.lastCall.constraintSystem + var completionType = topLevelCall.prepareForCompletion(expectedType) + val lastCall = candidate.lastCall + lastCall.runCompletion(completionType, resolutionCallbacks) - constraintSystemCompleter.runCompletion( - constraintSystem.asConstraintSystemCompleterContext(), completionType, candidate.lastCall.descriptorWithFreshTypes.returnTypeOrNothing - ) { - postponedArgumentsAnalyzer.analyze(constraintSystem.asPostponedArgumentsAnalyzerContext(), resolutionCallbacks, it) + if (lastCall.constraintSystem.asConstraintSystemCompleterContext().canBeProper(lastCall.descriptorWithFreshTypes.returnTypeOrNothing)) { + completionType = ConstraintSystemCompletionMode.FULL + lastCall.runCompletion(completionType, resolutionCallbacks) } return when (completionType) { - CompletionType.FULL -> toCompletedBaseResolvedCall(constraintSystem.asCallCompleterContext(), candidate, resolutionCallbacks) - CompletionType.PARTIAL -> ResolvedKotlinCall.OnlyResolvedKotlinCall(candidate) + ConstraintSystemCompletionMode.FULL -> toCompletedBaseResolvedCall(lastCall.constraintSystem.asCallCompleterContext(), candidate, resolutionCallbacks) + ConstraintSystemCompletionMode.PARTIAL -> ResolvedKotlinCall.OnlyResolvedKotlinCall(candidate) + } + } + + private fun SimpleKotlinResolutionCandidate.runCompletion(completionMode: ConstraintSystemCompletionMode, resolutionCallbacks: KotlinResolutionCallbacks) { + constraintSystemCompleter.runCompletion( + constraintSystem.asConstraintSystemCompleterContext(), completionMode, descriptorWithFreshTypes.returnTypeOrNothing + ) { + postponedArgumentsAnalyzer.analyze(constraintSystem.asPostponedArgumentsAnalyzerContext(), resolutionCallbacks, it) } } @@ -154,17 +158,17 @@ class KotlinCallCompleter( } // true if we should complete this call - private fun SimpleKotlinResolutionCandidate.prepareForCompletion(expectedType: UnwrappedType?): CompletionType { - val returnType = descriptorWithFreshTypes.returnType?.unwrap() ?: return CompletionType.PARTIAL + private fun SimpleKotlinResolutionCandidate.prepareForCompletion(expectedType: UnwrappedType?): ConstraintSystemCompletionMode { + val returnType = descriptorWithFreshTypes.returnType?.unwrap() ?: return ConstraintSystemCompletionMode.PARTIAL if (expectedType != null && !TypeUtils.noExpectedType(expectedType)) { csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(kotlinCall)) } return if (expectedType != null || csBuilder.isProperType(returnType)) { - CompletionType.FULL + ConstraintSystemCompletionMode.FULL } else { - CompletionType.PARTIAL + ConstraintSystemCompletionMode.PARTIAL } } } \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt index a7e8ce224a0..5de6297407f 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt @@ -141,7 +141,8 @@ private fun checkSubCallArgument( csBuilder.addInnerCall(resolvedCall) // subArgument cannot has stable smartcast - val currentReturnType = subCallArgument.receiver.receiverValue.type.unwrap() + // return type can contains fixed type variables + val currentReturnType = csBuilder.buildCurrentSubstitutor().safeSubstitute(subCallArgument.receiver.receiverValue.type.unwrap()) if (subCallArgument.isSafeCall) { csBuilder.addSubtypeConstraint(currentReturnType, expectedNullableType, position) return null diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt index b974c8a0051..12cd7fa2b8a 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt @@ -42,6 +42,8 @@ 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(): NewTypeSubstitutor + /** * This function removes variables for which we know exact type. * @return substitutor from typeVariable to result diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt index 78bac0781fe..3fe87c64538 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt @@ -16,9 +16,9 @@ package org.jetbrains.kotlin.resolve.calls.inference -import org.jetbrains.kotlin.resolve.calls.components.ConstraintSystemCompleter import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompleter import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompleterImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompleterImpl.kt new file mode 100644 index 00000000000..4f48a1c3e44 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompleterImpl.kt @@ -0,0 +1,139 @@ +/* + * Copyright 2010-2017 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.inference.components + +import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable +import org.jetbrains.kotlin.resolve.calls.inference.model.NotEnoughInformationForTypeParameter +import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull + +class ConstraintSystemCompleter( + private val resultTypeResolver: ResultTypeResolver, + private val variableFixationFinder: VariableFixationFinder +) { + enum class ConstraintSystemCompletionMode { + FULL, + PARTIAL + } + + interface Context : VariableFixationFinder.Context, ResultTypeResolver.Context { + override val postponedArguments: List + override val notFixedTypeVariables: Map + + // type can be proper if it not contains not fixed type variables + fun canBeProper(type: UnwrappedType): Boolean + + // mutable operations + fun addError(error: KotlinCallDiagnostic) + fun fixVariable(variable: NewTypeVariable, resultType: UnwrappedType) + } + + fun runCompletion( + c: Context, + completionMode: ConstraintSystemCompletionMode, + topLevelType: UnwrappedType, + analyze: (PostponedKotlinCallArgument) -> Unit + ) { + while (true) { + if (analyzePostponeArgumentIfPossible(c, analyze)) continue + + val variableForFixation = variableFixationFinder.findFirstVariableForFixation(c, completionMode, topLevelType) + + if (shouldWeForceCallableReferenceResolution(completionMode, variableForFixation)) { + if (forceCallableReferenceResolution(c, analyze)) continue + } + + if (variableForFixation != null) { + if (variableForFixation.hasProperConstraint || completionMode == ConstraintSystemCompletionMode.FULL) { + val variableWithConstraints = c.notFixedTypeVariables[variableForFixation.variable]!! + + fixVariable(c, topLevelType, variableWithConstraints) + + if (!variableForFixation.hasProperConstraint) { + c.addError(NotEnoughInformationForTypeParameter(variableWithConstraints.typeVariable)) + } + continue + } + } + break + } + + if (completionMode == ConstraintSystemCompletionMode.FULL) { + // force resolution for all not-analyzed argument's + c.postponedArguments.filterNot { it.analyzed }.forEach(analyze) + } + } + + private fun shouldWeForceCallableReferenceResolution( + completionMode: ConstraintSystemCompletionMode, + variableForFixation: VariableFixationFinder.VariableForFixation? + ): Boolean { + if (completionMode == ConstraintSystemCompletionMode.PARTIAL) return false + if (variableForFixation != null && variableForFixation.hasProperConstraint) return false + + return true + } + + // true if we do analyze + private fun analyzePostponeArgumentIfPossible(c: Context, analyze: (PostponedKotlinCallArgument) -> Unit): Boolean { + for (argument in getOrderedNotAnalyzedPostponedArguments(c)) { + if (canWeAnalyzeIt(c, argument)) { + analyze(argument) + return true + } + } + return false + } + + // true if we find some callable reference and run resolution for it. Note that such resolution can be unsuccessful + private fun forceCallableReferenceResolution(c: Context, analyze: (PostponedKotlinCallArgument) -> Unit): Boolean { + val callableReferenceArgument = getOrderedNotAnalyzedPostponedArguments(c). + firstIsInstanceOrNull() ?: return false + + analyze(callableReferenceArgument) + return true + } + + private fun getOrderedNotAnalyzedPostponedArguments(c: Context): List { + val notAnalyzedArguments = c.postponedArguments.filterNot { it.analyzed } + + // todo insert logic here + return notAnalyzedArguments + } + + + private fun canWeAnalyzeIt(c: Context, argument: PostponedKotlinCallArgument): Boolean { + if (argument is PostponedCollectionLiteralArgument || argument.analyzed) return false + + return argument.inputTypes.all { c.canBeProper(it) } + } + + private fun fixVariable( + c: Context, + topLevelType: UnwrappedType, + variableWithConstraints: VariableWithConstraints + ) { + val direction = TypeVariableDirectionCalculator(c, topLevelType).getDirection(variableWithConstraints) + + val resultType = resultTypeResolver.findResultType(c, variableWithConstraints, direction) + + c.fixVariable(variableWithConstraints.typeVariable, resultType) + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt deleted file mode 100644 index fcff43c59f8..00000000000 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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.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.PostponedKotlinCallArgument -import org.jetbrains.kotlin.resolve.calls.model.PostponedLambdaArgument -import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker -import org.jetbrains.kotlin.types.checker.isIntersectionType -import org.jetbrains.kotlin.types.typeUtil.contains -import org.jetbrains.kotlin.utils.DFS -import org.jetbrains.kotlin.utils.SmartList - -private typealias Variable = VariableWithConstraints - -class FixationOrderCalculator { - enum class ResolveDirection { - TO_SUBTYPE, - TO_SUPERTYPE, - UNKNOWN - } - - data class NodeWithDirection(val variableWithConstraints: VariableWithConstraints, val direction: ResolveDirection) { - override fun toString() = "$variableWithConstraints to $direction" - } - - interface Context { - val notFixedTypeVariables: Map - val lambdaArguments: List - val postponedArguments: List - } - - fun computeCompletionOrder( - c: Context, - topReturnType: UnwrappedType - ): List = DependencyGraph(c).getCompletionOrder(topReturnType) - - /** - * U depends-on V if one of the following conditions is met: - * - * LAMBDA - * result type U depends-on all parameters types V of the corresponding lambda - * - * LAMBDA-RESULT - * Since there is no separate type variables for lambda such edges removed for now - * - * V is a lambda result type variable, - * V <: T constraint exists for V, - * U is a constituent type of T in position matching approximation direction for U - * - * STRONG-CONSTRAINT - * 'U T' constraint exists for U, - * is a constraint operator relevant to U approximation direction, - * V is a proper constituent type of T - * - * WEAK-CONSTRAINT - * 'U V' constraint exists for U, - * is a constraint operator relevant to U approximation direction - */ - private class DependencyGraph(val c: Context) { - private val directions = HashMap() - - private val lambdaEdges = HashMap>() - - // first in the list -- first fix - fun getCompletionOrder(topReturnType: UnwrappedType): List { - setupDirections(topReturnType) - - buildLambdaEdges() - - return topologicalOrderWith0Priority().map { NodeWithDirection(it, directions[it] ?: ResolveDirection.UNKNOWN) } - } - - private fun buildLambdaEdges() { - for (lambdaArgument in c.lambdaArguments) { - if (lambdaArgument.analyzed) continue // optimization - - val typeVariablesInReturnType = SmartList() - lambdaArgument.outputType.findTypeVariables(typeVariablesInReturnType) - - if (typeVariablesInReturnType.isEmpty()) continue // optimization - - val typeVariablesInParameters = SmartList() - lambdaArgument.inputTypes.forEach { it.findTypeVariables(typeVariablesInParameters) } - - for (returnTypeVariable in typeVariablesInReturnType) { - lambdaEdges.getOrPut(returnTypeVariable) { LinkedHashSet() }.addAll(typeVariablesInParameters) - } - } - } - - private fun UnwrappedType.findTypeVariables(to: MutableCollection) = - contains { - c.notFixedTypeVariables[it.constructor]?.let { variable -> to.add(variable) } - false - } - - private fun topologicalOrderWith0Priority(): List { - val handler = object : DFS.CollectingNodeHandler>(LinkedHashSet()) { - override fun afterChildren(current: Variable) { - // LAMBDA dependency edges should always be satisfied - // Note that cyclic by lambda edges are possible - result.addAll(getLambdaDependencies(current)) - - result.add(current) - } - } - - for (typeVariable in c.notFixedTypeVariables.values.sortByTypeVariable()) { - DFS.doDfs(typeVariable, DFS.Neighbors(this::getEdges), DFS.VisitedWithSet(), handler) - } - return handler.result().toList() - } - - - private fun setupDirections(topReturnType: UnwrappedType) { - topReturnType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction -> - enterToNode(variableWithConstraints, direction) - } - for (resolvedLambdaArgument in c.lambdaArguments) { - inner@ for (inputType in resolvedLambdaArgument.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 getEdges(variable: Variable): List { - val direction = directions[variable] ?: ResolveDirection.UNKNOWN - val constraintEdges = - LinkedHashSet().also { set -> - getConstraintDependencies(variable, direction).mapTo(set) { it.variableWithConstraints } - }.toList().sortByTypeVariable() - val lambdaEdges = getLambdaDependencies(variable).sortByTypeVariable() - return constraintEdges + lambdaEdges - } - - private fun Collection.sortByTypeVariable() = - // TODO hack, provide some reasonable stable order - sortedBy { it.typeVariable.toString() } - - private enum class ConstraintDependencyKind { STRONG, WEAK } - - private fun getConstraintDependencies( - variableWithConstraints: Variable, - direction: ResolveDirection, - filterByDependencyKind: ConstraintDependencyKind? = null - ): List = - SmartList().also { result -> - for (constraint in variableWithConstraints.constraints) { - if (!isInterestingConstraint(direction, constraint)) continue - - if (filterByDependencyKind == null || filterByDependencyKind == getConstraintDependencyKind(constraint)) { - constraint.type.visitType(direction) { nodeVariable, nodeDirection -> - result.add(NodeWithDirection(nodeVariable, nodeDirection)) - } - } - } - } - - private fun getConstraintDependencyKind(constraint: Constraint): ConstraintDependencyKind = - if (c.notFixedTypeVariables.containsKey(constraint.type.constructor)) - ConstraintDependencyKind.WEAK - else - ConstraintDependencyKind.STRONG - - 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 getLambdaDependencies(variable: Variable): List = lambdaEdges[variable]?.toList() ?: emptyList() - - private fun UnwrappedType.visitType(startDirection: ResolveDirection, action: (variable: Variable, direction: ResolveDirection) -> Unit) = - when (this) { - is SimpleType -> visitType(startDirection, action) - is FlexibleType -> { - lowerBound.visitType(startDirection, action) - upperBound.visitType(startDirection, action) - } - } - - private fun SimpleType.visitType(startDirection: ResolveDirection, action: (variable: Variable, direction: ResolveDirection) -> Unit) { - if (isIntersectionType) { - constructor.supertypes.forEach { - it.unwrap().visitType(startDirection, action) - } - return - } - - if (arguments.isEmpty()) { - c.notFixedTypeVariables[constructor]?.let { - action(it, startDirection) - } - return - } - - val parameters = constructor.parameters - if (parameters.size != arguments.size) return // incorrect type - - for ((argument, parameter) in arguments.zip(parameters)) { - if (argument.isStarProjection) continue - - val variance = NewKotlinTypeChecker.effectiveVariance(parameter.variance, argument.projectionKind) ?: Variance.INVARIANT - val innerDirection = when (variance) { - Variance.INVARIANT -> ResolveDirection.UNKNOWN - Variance.OUT_VARIANCE -> startDirection - Variance.IN_VARIANCE -> startDirection.opposite() - } - - argument.type.unwrap().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 - } - } - -} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/InferenceStepResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/InferenceStepResolver.kt deleted file mode 100644 index b2f93b35542..00000000000 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/InferenceStepResolver.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010-2017 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.inference.components - -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.resolve.calls.components.ConstraintSystemCompleter -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.NotEnoughInformationForTypeParameter -import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints - -typealias VariableResolutionNode = FixationOrderCalculator.NodeWithDirection - -class InferenceStepResolver( - private val resultTypeResolver: ResultTypeResolver -) { - /** - * Resolves one or more of the `variables`. - * Returns `true` if type variable resolution should stop. - */ - fun resolveVariables(c: ConstraintSystemCompleter.Context, variables: List): Boolean { - if (variables.isEmpty()) return true - if (c.hasContradiction) return true - - val nodeToResolve = variables.firstOrNull { it.variableWithConstraints.hasProperConstraint(c) } ?: - variables.first() - val (variableWithConstraints, direction) = nodeToResolve - val variable = variableWithConstraints.typeVariable - - val resultType = resultTypeResolver.findResultType(c.asResultTypeResolverContext(), variableWithConstraints, direction) - if (resultType == null) { - c.addError(NotEnoughInformationForTypeParameter(variable)) - return true - } - c.fixVariable(variable, resultType) - return false - } - - private fun VariableWithConstraints.hasProperConstraint(c: ConstraintSystemCompleter.Context) = - constraints.any { !it.isTrivial() && c.canBeProper(it.type) } - - private fun Constraint.isTrivial() = - kind == ConstraintKind.LOWER && KotlinBuiltIns.isNothing(type) || - kind == ConstraintKind.UPPER && KotlinBuiltIns.isNullableAny(type) -} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt index f51e0719cdd..5ca8e698daa 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -17,7 +17,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator -import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator.ResolveDirection +import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator.ResolveDirection import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints import org.jetbrains.kotlin.resolve.calls.inference.model.checkConstraint @@ -33,7 +33,16 @@ class ResultTypeResolver( fun isProperType(type: UnwrappedType): Boolean } - fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): UnwrappedType? { + fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): UnwrappedType { + findResultTypeOrNull(c, variableWithConstraints, direction)?.let { return it } + + // no proper constraints + return variableWithConstraints.typeVariable.freshTypeConstructor.builtIns.run { + if (direction == ResolveDirection.TO_SUBTYPE) nothingType else nullableAnyType + } + } + + fun findResultTypeOrNull(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): UnwrappedType? { findResultIfThereIsEqualsConstraint(c, variableWithConstraints, allowedFixToNotProperType = false)?.let { return it } val subType = findSubType(c, variableWithConstraints) @@ -45,12 +54,7 @@ class ResultTypeResolver( c.resultType(superType, subType, variableWithConstraints) } - if (result != null) return result - - // no proper constraints - return variableWithConstraints.typeVariable.freshTypeConstructor.builtIns.run { - if (direction == ResolveDirection.TO_SUBTYPE) nothingType else nullableAnyType - } + return result } private fun Context.resultType( diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt new file mode 100644 index 00000000000..4710a7129db --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt @@ -0,0 +1,132 @@ +/* + * Copyright 2010-2017 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.inference.components + +import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints +import org.jetbrains.kotlin.resolve.calls.model.PostponedKotlinCallArgument +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.typeUtil.contains +import org.jetbrains.kotlin.utils.SmartSet + +class TypeVariableDependencyInformationProvider( + private val notFixedTypeVariables: Map, + private val postponedArguments: List, + private val topLevelType: UnwrappedType? +) { + // not oriented edges + private val constrainEdges: MutableMap> = hashMapOf() + + // oriented edges + private val postponeArgumentsEdges: MutableMap> = hashMapOf() + + private val relatedToAllOutputTypes: MutableSet = hashSetOf() + private val relatedToTopLevelType: MutableSet = hashSetOf() + + init { + computeConstraintEdges() + computePostponeArgumentsEdges() + computeRelatedToAllOutputTypes() + computeRelatedToTopLevelType() + } + + fun isVariableRelatedToTopLevelType(variable: TypeConstructor) = relatedToTopLevelType.contains(variable) + fun isVariableRelatedToAnyOutputType(variable: TypeConstructor) = relatedToAllOutputTypes.contains(variable) + + private fun computeConstraintEdges() { + fun addConstraintEdge(from: TypeConstructor, to: TypeConstructor) { + constrainEdges.getOrPut(from) { hashSetOf() }.add(to) + constrainEdges.getOrPut(to) { hashSetOf() }.add(from) + } + + for (variableWithConstraints in notFixedTypeVariables.values) { + val from = variableWithConstraints.typeVariable.freshTypeConstructor + + for (constraint in variableWithConstraints.constraints) { + constraint.type.forAllMyTypeVariables { + if (isMyTypeVariable(it)) { + addConstraintEdge(from, it) + } + } + } + } + } + + private fun computePostponeArgumentsEdges() { + fun addPostponeArgumentsEdges(from: TypeConstructor, to: TypeConstructor) { + postponeArgumentsEdges.getOrPut(from) { hashSetOf() }.add(to) + } + + for (argument in postponedArguments) { + if (argument.analyzed) continue + + val typeVariablesInOutputType = SmartSet.create() + (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 postponedArguments) { + 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: TypeConstructor) = notFixedTypeVariables.containsKey(typeConstructor) + + private fun UnwrappedType.forAllMyTypeVariables(action: (TypeConstructor) -> Unit) = this.contains { + if (isMyTypeVariable(it.constructor)) action(it.constructor) + + false + } + + private fun getConstraintEdges(from: TypeConstructor): Set = constrainEdges[from] ?: emptySet() + private fun getPostponeEdges(from: TypeConstructor): Set = postponeArgumentsEdges[from] ?: emptySet() + + private fun addAllRelatedNodes(to: MutableSet, node: TypeConstructor, 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) + } + } + } + } + + +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDirectionCalculator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDirectionCalculator.kt new file mode 100644 index 00000000000..c73fe05b8b9 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDirectionCalculator.kt @@ -0,0 +1,152 @@ +/* + * Copyright 2010-2017 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.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.types.FlexibleType +import org.jetbrains.kotlin.types.SimpleType +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker +import org.jetbrains.kotlin.types.checker.isIntersectionType +import org.jetbrains.kotlin.utils.SmartList + + +private typealias Variable = VariableWithConstraints + +class TypeVariableDirectionCalculator( + val c: VariableFixationFinder.Context, + topLevelType: UnwrappedType +) { + 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() + + init { + setupDirections(topLevelType) + } + + fun getDirection(typeVariable: Variable): ResolveDirection = + directions.getOrDefault(typeVariable, ResolveDirection.UNKNOWN) + + private fun setupDirections(topReturnType: UnwrappedType) { + topReturnType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction -> + enterToNode(variableWithConstraints, direction) + } + for (postponedArgument in c.postponedArguments) { + 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 = + SmartList().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 UnwrappedType.visitType(startDirection: ResolveDirection, action: (variable: Variable, direction: ResolveDirection) -> Unit) = + when (this) { + is SimpleType -> visitType(startDirection, action) + is FlexibleType -> { + lowerBound.visitType(startDirection, action) + upperBound.visitType(startDirection, action) + } + } + + private fun SimpleType.visitType(startDirection: ResolveDirection, action: (variable: Variable, direction: ResolveDirection) -> Unit) { + if (isIntersectionType) { + constructor.supertypes.forEach { + it.unwrap().visitType(startDirection, action) + } + return + } + + if (arguments.isEmpty()) { + c.notFixedTypeVariables[constructor]?.let { + action(it, startDirection) + } + return + } + + val parameters = constructor.parameters + if (parameters.size != arguments.size) return // incorrect type + + for ((argument, parameter) in arguments.zip(parameters)) { + if (argument.isStarProjection) continue + + val variance = NewKotlinTypeChecker.effectiveVariance(parameter.variance, argument.projectionKind) ?: Variance.INVARIANT + val innerDirection = when (variance) { + Variance.INVARIANT -> ResolveDirection.UNKNOWN + Variance.OUT_VARIANCE -> startDirection + Variance.IN_VARIANCE -> startDirection.opposite() + } + + argument.type.unwrap().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 + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt new file mode 100644 index 00000000000..90078f063cd --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2010-2017 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.inference.components + +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompleter.ConstraintSystemCompletionMode +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompleter.ConstraintSystemCompletionMode.* +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.PostponedKotlinCallArgument +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.typeUtil.contains + +class VariableFixationFinder { + interface Context { + val notFixedTypeVariables: Map + val postponedArguments: List + } + + data class VariableForFixation(val variable: TypeConstructor, val hasProperConstraint: Boolean) + + fun findFirstVariableForFixation( + c: Context, + completionMode: ConstraintSystemCompletionMode, + topLevelType: UnwrappedType + ): VariableForFixation? = c.findTypeVariableForFixation(completionMode, topLevelType) + + private enum class TypeVariableFixationReadiness { + FORBIDDEN, + WITHOUT_PROPER_ARGUMENT_CONSTRAINT, // proper constraint from arguments -- not from upper bound for type parameters + RELATED_TO_ANY_OUTPUT_TYPE, + WITH_COMPLEX_DEPENDENCY, // if type variable T has constraint with non fixed type variable inside (non-top-level): T <: Foo + READY_FOR_FIXATION, + } + + private fun Context.getTypeVariableReadiness( + variable: TypeConstructor, + dependencyProvider: TypeVariableDependencyInformationProvider + ): TypeVariableFixationReadiness = when { + dependencyProvider.isVariableRelatedToTopLevelType(variable) -> TypeVariableFixationReadiness.FORBIDDEN + !variableHasProperArgumentConstraints(variable) -> TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT + dependencyProvider.isVariableRelatedToAnyOutputType(variable) -> TypeVariableFixationReadiness.RELATED_TO_ANY_OUTPUT_TYPE + hasDependencyToOtherTypeVariables(variable) -> TypeVariableFixationReadiness.WITH_COMPLEX_DEPENDENCY + else -> TypeVariableFixationReadiness.READY_FOR_FIXATION + } + + private fun Context.findTypeVariableForFixation( + completionMode: ConstraintSystemCompletionMode, + topLevelType: UnwrappedType + ): VariableForFixation? { + val dependencyProvider = TypeVariableDependencyInformationProvider(notFixedTypeVariables, postponedArguments, + topLevelType.takeIf { completionMode == PARTIAL }) + + val initialOrder = notFixedTypeVariables.keys.sortByInitialOrder() + val candidate = initialOrder.maxBy { getTypeVariableReadiness(it, dependencyProvider) } ?: return null + val candidateReadiness = getTypeVariableReadiness(candidate, dependencyProvider) + return when (candidateReadiness) { + TypeVariableFixationReadiness.FORBIDDEN -> null + TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT -> VariableForFixation(candidate, false) + else -> VariableForFixation(candidate, true) + } + } + + private fun Context.hasDependencyToOtherTypeVariables(typeVariable: TypeConstructor): Boolean { + for (constraint in notFixedTypeVariables[typeVariable]?.constraints ?: return false) { + if (constraint.type.arguments.isNotEmpty() && constraint.type.contains { notFixedTypeVariables.containsKey(it.constructor) }) { + return true + } + } + return false + } + + private fun Context.variableHasProperArgumentConstraints(variable: TypeConstructor): Boolean = + notFixedTypeVariables[variable]?.constraints?.any { isProperArgumentConstraint(it) } ?: false + + private fun Context.isProperArgumentConstraint(c: Constraint) = + isProperType(c.type) && c.position.initialConstraint.position !is DeclaredUpperBoundConstraintPosition + + private fun Context.isProperType(type: UnwrappedType): Boolean = + !type.contains { notFixedTypeVariables.containsKey(it.constructor) } + + private fun Collection.sortByInitialOrder(): List = + sortedBy { toString() } // todo + +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index 2526685d18a..11eebb892bf 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -16,12 +16,14 @@ package org.jetbrains.kotlin.resolve.calls.inference.model -import org.jetbrains.kotlin.resolve.calls.components.ConstraintSystemCompleter import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer import org.jetbrains.kotlin.resolve.calls.inference.* import org.jetbrains.kotlin.resolve.calls.inference.components.* -import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic +import org.jetbrains.kotlin.resolve.calls.model.PostponedKotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.model.PostponedLambdaArgument +import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.TypeConstructor @@ -36,7 +38,6 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re ConstraintInjector.Context, ResultTypeResolver.Context, KotlinCallCompleter.Context, - FixationOrderCalculator.Context, ConstraintSystemCompleter.Context, PostponedArgumentsAnalyzer.Context { @@ -256,10 +257,6 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re get() = storage.postponedArguments.apply { checkState(State.BUILDING, State.COMPLETION) } // ConstraintSystemCompleter.Context - override fun asResultTypeResolverContext() = apply { checkState(State.BUILDING, State.COMPLETION) } - - override fun asFixationOrderCalculatorContext() = apply { checkState(State.BUILDING, State.COMPLETION) } - override fun fixVariable(variable: NewTypeVariable, resultType: UnwrappedType) { checkState(State.BUILDING, State.COMPLETION) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/PostponedKotlinCallArguments.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/PostponedKotlinCallArguments.kt index 33926f4d87e..25d9d668451 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/PostponedKotlinCallArguments.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/PostponedKotlinCallArguments.kt @@ -17,8 +17,12 @@ package org.jetbrains.kotlin.resolve.calls.model import org.jetbrains.kotlin.builtins.createFunctionType +import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType +import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType +import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceCandidate +import org.jetbrains.kotlin.resolve.calls.components.getFunctionTypeFromCallableReferenceExpectedType import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.UnwrappedType @@ -27,6 +31,11 @@ import org.jetbrains.kotlin.types.typeUtil.builtIns sealed class PostponedKotlinCallArgument { abstract val argument: PostponableKotlinCallArgument + + abstract val analyzed: Boolean + + abstract val inputTypes: Collection + abstract val outputType: UnwrappedType? } class PostponedLambdaArgument( @@ -36,12 +45,12 @@ class PostponedLambdaArgument( val parameters: List, val returnType: UnwrappedType ) : PostponedKotlinCallArgument() { - var analyzed: Boolean = false + override var analyzed: Boolean = false val type: SimpleType = createFunctionType(returnType.builtIns, Annotations.EMPTY, receiver, parameters, null, returnType, isSuspend) // todo support annotations - val inputTypes: Collection get() = receiver?.let { parameters + it } ?: parameters - val outputType: UnwrappedType get() = returnType + override val inputTypes: Collection get() = receiver?.let { parameters + it } ?: parameters + override val outputType: UnwrappedType get() = returnType lateinit var resultArguments: List lateinit var finalReturnType: UnwrappedType @@ -51,6 +60,22 @@ class PostponedCallableReferenceArgument( override val argument: CallableReferenceKotlinCallArgument, val expectedType: UnwrappedType ) : PostponedKotlinCallArgument() { + override var analyzed: Boolean = false + + override val inputTypes: Collection + get() { + val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return emptyList() + val parameters = functionType.getValueParameterTypesFromFunctionType().map { it.type.unwrap() } + val receiver = functionType.getReceiverTypeFromFunctionType()?.unwrap() + return receiver?.let { parameters + it } ?: parameters + } + + override val outputType: UnwrappedType? + get() { + val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return null + return functionType.getReturnTypeFromFunctionType().unwrap() + } + var analyzedAndThereIsResult: Boolean = false lateinit var myTypeVariables: List @@ -60,4 +85,12 @@ class PostponedCallableReferenceArgument( class PostponedCollectionLiteralArgument( override val argument: CollectionLiteralKotlinCallArgument, val expectedType: UnwrappedType -) : PostponedKotlinCallArgument() \ No newline at end of file +) : PostponedKotlinCallArgument() { + // for now we consider all such arguments as analyzed because they processed via special logic anyway + override val analyzed get() = true + + override val inputTypes: Collection + get() = emptyList() + override val outputType: UnwrappedType? + get() = null +} \ No newline at end of file