[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
This commit is contained in:
-6
@@ -19,9 +19,3 @@ package org.jetbrains.kotlin.resolve.calls
|
|||||||
val USE_NEW_INFERENCE = false
|
val USE_NEW_INFERENCE = false
|
||||||
|
|
||||||
val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC = false
|
val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC = false
|
||||||
|
|
||||||
val USE_CS_COMPLETER_TYPE = CSCompleterType.INITIAL
|
|
||||||
|
|
||||||
enum class CSCompleterType {
|
|
||||||
INITIAL
|
|
||||||
}
|
|
||||||
|
|||||||
+15
-13
@@ -214,19 +214,7 @@ class CallableReferencesCandidateFactory(
|
|||||||
expectedType: UnwrappedType?,
|
expectedType: UnwrappedType?,
|
||||||
unboundReceiverCount: Int
|
unboundReceiverCount: Int
|
||||||
): Triple<Array<KotlinType>, CoercionStrategy, Int>? {
|
): Triple<Array<KotlinType>, CoercionStrategy, Int>? {
|
||||||
if (expectedType == null) return null
|
val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return null
|
||||||
|
|
||||||
val functionType =
|
|
||||||
if (expectedType.isFunctionType) {
|
|
||||||
expectedType
|
|
||||||
}
|
|
||||||
else if (ReflectionTypes.isNumberedKFunction(expectedType)) {
|
|
||||||
expectedType.immediateSupertypes().first { it.isFunctionType }
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val expectedArgumentCount = functionType.arguments.size - unboundReceiverCount - 1 // 1 -- return type
|
val expectedArgumentCount = functionType.arguments.size - unboundReceiverCount - 1 // 1 -- return type
|
||||||
if (expectedArgumentCount < 0) return null
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-1
@@ -65,8 +65,10 @@ class CallableReferenceResolver(
|
|||||||
csBuilder: ConstraintSystemBuilder,
|
csBuilder: ConstraintSystemBuilder,
|
||||||
postponedArgument: PostponedCallableReferenceArgument
|
postponedArgument: PostponedCallableReferenceArgument
|
||||||
): KotlinCallDiagnostic? {
|
): KotlinCallDiagnostic? {
|
||||||
|
postponedArgument.analyzed = true
|
||||||
|
|
||||||
val argument = postponedArgument.argument
|
val argument = postponedArgument.argument
|
||||||
val expectedType = postponedArgument.expectedType
|
val expectedType = csBuilder.buildCurrentSubstitutor().safeSubstitute(postponedArgument.expectedType)
|
||||||
|
|
||||||
val subLHSCall = argument.lhsResult.safeAs<LHSResult.Expression>()?.lshCallArgument.safeAs<SubKotlinCallArgument>()
|
val subLHSCall = argument.lhsResult.safeAs<LHSResult.Expression>()?.lshCallArgument.safeAs<SubKotlinCallArgument>()
|
||||||
if (subLHSCall != null) {
|
if (subLHSCall != null) {
|
||||||
|
|||||||
-53
@@ -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<PostponedKotlinCallArgument>
|
|
||||||
|
|
||||||
// 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
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
-79
@@ -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<PostponedCallableReferenceArgument>().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) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+23
-19
@@ -19,9 +19,8 @@ package org.jetbrains.kotlin.resolve.calls.components
|
|||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.calls.CSCompleterType
|
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompleter
|
||||||
import org.jetbrains.kotlin.resolve.calls.USE_CS_COMPLETER_TYPE
|
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompleter.ConstraintSystemCompletionMode
|
||||||
import org.jetbrains.kotlin.resolve.calls.components.ConstraintSystemCompleter.CompletionType
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
|
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.model.ExpectedTypeConstraintPosition
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing
|
import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing
|
||||||
@@ -37,11 +36,8 @@ import org.jetbrains.kotlin.types.typeUtil.contains
|
|||||||
class KotlinCallCompleter(
|
class KotlinCallCompleter(
|
||||||
private val additionalDiagnosticReporter: AdditionalDiagnosticReporter,
|
private val additionalDiagnosticReporter: AdditionalDiagnosticReporter,
|
||||||
private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer,
|
private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer,
|
||||||
initialConstraintSystemCompleter: InitialConstraintSystemCompleterImpl
|
private val constraintSystemCompleter: ConstraintSystemCompleter
|
||||||
) {
|
) {
|
||||||
private val constraintSystemCompleter: ConstraintSystemCompleter = when(USE_CS_COMPLETER_TYPE) {
|
|
||||||
CSCompleterType.INITIAL -> initialConstraintSystemCompleter
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Context {
|
interface Context {
|
||||||
val innerCalls: List<ResolvedKotlinCall.OnlyResolvedKotlinCall>
|
val innerCalls: List<ResolvedKotlinCall.OnlyResolvedKotlinCall>
|
||||||
@@ -66,18 +62,26 @@ class KotlinCallCompleter(
|
|||||||
else -> candidate as SimpleKotlinResolutionCandidate
|
else -> candidate as SimpleKotlinResolutionCandidate
|
||||||
}
|
}
|
||||||
|
|
||||||
val completionType = topLevelCall.prepareForCompletion(expectedType)
|
var completionType = topLevelCall.prepareForCompletion(expectedType)
|
||||||
val constraintSystem = candidate.lastCall.constraintSystem
|
val lastCall = candidate.lastCall
|
||||||
|
lastCall.runCompletion(completionType, resolutionCallbacks)
|
||||||
|
|
||||||
constraintSystemCompleter.runCompletion(
|
if (lastCall.constraintSystem.asConstraintSystemCompleterContext().canBeProper(lastCall.descriptorWithFreshTypes.returnTypeOrNothing)) {
|
||||||
constraintSystem.asConstraintSystemCompleterContext(), completionType, candidate.lastCall.descriptorWithFreshTypes.returnTypeOrNothing
|
completionType = ConstraintSystemCompletionMode.FULL
|
||||||
) {
|
lastCall.runCompletion(completionType, resolutionCallbacks)
|
||||||
postponedArgumentsAnalyzer.analyze(constraintSystem.asPostponedArgumentsAnalyzerContext(), resolutionCallbacks, it)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return when (completionType) {
|
return when (completionType) {
|
||||||
CompletionType.FULL -> toCompletedBaseResolvedCall(constraintSystem.asCallCompleterContext(), candidate, resolutionCallbacks)
|
ConstraintSystemCompletionMode.FULL -> toCompletedBaseResolvedCall(lastCall.constraintSystem.asCallCompleterContext(), candidate, resolutionCallbacks)
|
||||||
CompletionType.PARTIAL -> ResolvedKotlinCall.OnlyResolvedKotlinCall(candidate)
|
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
|
// true if we should complete this call
|
||||||
private fun SimpleKotlinResolutionCandidate.prepareForCompletion(expectedType: UnwrappedType?): CompletionType {
|
private fun SimpleKotlinResolutionCandidate.prepareForCompletion(expectedType: UnwrappedType?): ConstraintSystemCompletionMode {
|
||||||
val returnType = descriptorWithFreshTypes.returnType?.unwrap() ?: return CompletionType.PARTIAL
|
val returnType = descriptorWithFreshTypes.returnType?.unwrap() ?: return ConstraintSystemCompletionMode.PARTIAL
|
||||||
if (expectedType != null && !TypeUtils.noExpectedType(expectedType)) {
|
if (expectedType != null && !TypeUtils.noExpectedType(expectedType)) {
|
||||||
csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(kotlinCall))
|
csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(kotlinCall))
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (expectedType != null || csBuilder.isProperType(returnType)) {
|
return if (expectedType != null || csBuilder.isProperType(returnType)) {
|
||||||
CompletionType.FULL
|
ConstraintSystemCompletionMode.FULL
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
CompletionType.PARTIAL
|
ConstraintSystemCompletionMode.PARTIAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -141,7 +141,8 @@ private fun checkSubCallArgument(
|
|||||||
csBuilder.addInnerCall(resolvedCall)
|
csBuilder.addInnerCall(resolvedCall)
|
||||||
|
|
||||||
// subArgument cannot has stable smartcast
|
// 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) {
|
if (subCallArgument.isSafeCall) {
|
||||||
csBuilder.addSubtypeConstraint(currentReturnType, expectedNullableType, position)
|
csBuilder.addSubtypeConstraint(currentReturnType, expectedNullableType, position)
|
||||||
return null
|
return null
|
||||||
|
|||||||
+2
@@ -42,6 +42,8 @@ interface ConstraintSystemBuilder : ConstraintSystemOperation {
|
|||||||
// if runOperations return true, then this operation will be applied, and function return true
|
// if runOperations return true, then this operation will be applied, and function return true
|
||||||
fun runTransaction(runOperations: ConstraintSystemOperation.() -> Boolean): Boolean
|
fun runTransaction(runOperations: ConstraintSystemOperation.() -> Boolean): Boolean
|
||||||
|
|
||||||
|
fun buildCurrentSubstitutor(): NewTypeSubstitutor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function removes variables for which we know exact type.
|
* This function removes variables for which we know exact type.
|
||||||
* @return substitutor from typeVariable to result
|
* @return substitutor from typeVariable to result
|
||||||
|
|||||||
+1
-1
@@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.resolve.calls.inference
|
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.KotlinCallCompleter
|
||||||
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer
|
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.inference.model.ConstraintStorage
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
|
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
|
||||||
|
|
||||||
|
|||||||
+139
@@ -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<PostponedKotlinCallArgument>
|
||||||
|
override val notFixedTypeVariables: Map<TypeConstructor, VariableWithConstraints>
|
||||||
|
|
||||||
|
// 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<PostponedCallableReferenceArgument>() ?: return false
|
||||||
|
|
||||||
|
analyze(callableReferenceArgument)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOrderedNotAnalyzedPostponedArguments(c: Context): List<PostponedKotlinCallArgument> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
-258
@@ -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<TypeConstructor, VariableWithConstraints>
|
|
||||||
val lambdaArguments: List<PostponedLambdaArgument>
|
|
||||||
val postponedArguments: List<PostponedKotlinCallArgument>
|
|
||||||
}
|
|
||||||
|
|
||||||
fun computeCompletionOrder(
|
|
||||||
c: Context,
|
|
||||||
topReturnType: UnwrappedType
|
|
||||||
): List<NodeWithDirection> = 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 <op> T' constraint exists for U,
|
|
||||||
* <op> is a constraint operator relevant to U approximation direction,
|
|
||||||
* V is a proper constituent type of T
|
|
||||||
*
|
|
||||||
* WEAK-CONSTRAINT
|
|
||||||
* 'U <op> V' constraint exists for U,
|
|
||||||
* <op> is a constraint operator relevant to U approximation direction
|
|
||||||
*/
|
|
||||||
private class DependencyGraph(val c: Context) {
|
|
||||||
private val directions = HashMap<Variable, ResolveDirection>()
|
|
||||||
|
|
||||||
private val lambdaEdges = HashMap<Variable, MutableSet<Variable>>()
|
|
||||||
|
|
||||||
// first in the list -- first fix
|
|
||||||
fun getCompletionOrder(topReturnType: UnwrappedType): List<NodeWithDirection> {
|
|
||||||
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<Variable>()
|
|
||||||
lambdaArgument.outputType.findTypeVariables(typeVariablesInReturnType)
|
|
||||||
|
|
||||||
if (typeVariablesInReturnType.isEmpty()) continue // optimization
|
|
||||||
|
|
||||||
val typeVariablesInParameters = SmartList<Variable>()
|
|
||||||
lambdaArgument.inputTypes.forEach { it.findTypeVariables(typeVariablesInParameters) }
|
|
||||||
|
|
||||||
for (returnTypeVariable in typeVariablesInReturnType) {
|
|
||||||
lambdaEdges.getOrPut(returnTypeVariable) { LinkedHashSet() }.addAll(typeVariablesInParameters)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun UnwrappedType.findTypeVariables(to: MutableCollection<Variable>) =
|
|
||||||
contains {
|
|
||||||
c.notFixedTypeVariables[it.constructor]?.let { variable -> to.add(variable) }
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun topologicalOrderWith0Priority(): List<Variable> {
|
|
||||||
val handler = object : DFS.CollectingNodeHandler<Variable, Variable, LinkedHashSet<Variable>>(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<Variable>(), 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<Variable> {
|
|
||||||
val direction = directions[variable] ?: ResolveDirection.UNKNOWN
|
|
||||||
val constraintEdges =
|
|
||||||
LinkedHashSet<Variable>().also { set ->
|
|
||||||
getConstraintDependencies(variable, direction).mapTo(set) { it.variableWithConstraints }
|
|
||||||
}.toList().sortByTypeVariable()
|
|
||||||
val lambdaEdges = getLambdaDependencies(variable).sortByTypeVariable()
|
|
||||||
return constraintEdges + lambdaEdges
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Collection<Variable>.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<NodeWithDirection> =
|
|
||||||
SmartList<NodeWithDirection>().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<Variable> = 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-59
@@ -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<VariableResolutionNode>): 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)
|
|
||||||
}
|
|
||||||
+12
-8
@@ -17,7 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||||
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
|
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.ConstraintKind
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.checkConstraint
|
import org.jetbrains.kotlin.resolve.calls.inference.model.checkConstraint
|
||||||
@@ -33,7 +33,16 @@ class ResultTypeResolver(
|
|||||||
fun isProperType(type: UnwrappedType): Boolean
|
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 }
|
findResultIfThereIsEqualsConstraint(c, variableWithConstraints, allowedFixToNotProperType = false)?.let { return it }
|
||||||
|
|
||||||
val subType = findSubType(c, variableWithConstraints)
|
val subType = findSubType(c, variableWithConstraints)
|
||||||
@@ -45,12 +54,7 @@ class ResultTypeResolver(
|
|||||||
c.resultType(superType, subType, variableWithConstraints)
|
c.resultType(superType, subType, variableWithConstraints)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != null) return result
|
return result
|
||||||
|
|
||||||
// no proper constraints
|
|
||||||
return variableWithConstraints.typeVariable.freshTypeConstructor.builtIns.run {
|
|
||||||
if (direction == ResolveDirection.TO_SUBTYPE) nothingType else nullableAnyType
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Context.resultType(
|
private fun Context.resultType(
|
||||||
|
|||||||
+132
@@ -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<TypeConstructor, VariableWithConstraints>,
|
||||||
|
private val postponedArguments: List<PostponedKotlinCallArgument>,
|
||||||
|
private val topLevelType: UnwrappedType?
|
||||||
|
) {
|
||||||
|
// not oriented edges
|
||||||
|
private val constrainEdges: MutableMap<TypeConstructor, MutableSet<TypeConstructor>> = hashMapOf()
|
||||||
|
|
||||||
|
// oriented edges
|
||||||
|
private val postponeArgumentsEdges: MutableMap<TypeConstructor, MutableSet<TypeConstructor>> = hashMapOf()
|
||||||
|
|
||||||
|
private val relatedToAllOutputTypes: MutableSet<TypeConstructor> = hashSetOf()
|
||||||
|
private val relatedToTopLevelType: MutableSet<TypeConstructor> = 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<TypeConstructor>()
|
||||||
|
(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<TypeConstructor> = constrainEdges[from] ?: emptySet()
|
||||||
|
private fun getPostponeEdges(from: TypeConstructor): Set<TypeConstructor> = postponeArgumentsEdges[from] ?: emptySet()
|
||||||
|
|
||||||
|
private fun addAllRelatedNodes(to: MutableSet<TypeConstructor>, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+152
@@ -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<Variable, ResolveDirection>()
|
||||||
|
|
||||||
|
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<NodeWithDirection> =
|
||||||
|
SmartList<NodeWithDirection>().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
|
||||||
|
}
|
||||||
|
}
|
||||||
+100
@@ -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<TypeConstructor, VariableWithConstraints>
|
||||||
|
val postponedArguments: List<PostponedKotlinCallArgument>
|
||||||
|
}
|
||||||
|
|
||||||
|
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<S>
|
||||||
|
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<TypeConstructor>.sortByInitialOrder(): List<TypeConstructor> =
|
||||||
|
sortedBy { toString() } // todo
|
||||||
|
|
||||||
|
}
|
||||||
+4
-7
@@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.resolve.calls.inference.model
|
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.KotlinCallCompleter
|
||||||
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer
|
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.*
|
import org.jetbrains.kotlin.resolve.calls.inference.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.components.*
|
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.resolve.calls.tower.isSuccess
|
||||||
import org.jetbrains.kotlin.types.ErrorUtils
|
import org.jetbrains.kotlin.types.ErrorUtils
|
||||||
import org.jetbrains.kotlin.types.TypeConstructor
|
import org.jetbrains.kotlin.types.TypeConstructor
|
||||||
@@ -36,7 +38,6 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
|
|||||||
ConstraintInjector.Context,
|
ConstraintInjector.Context,
|
||||||
ResultTypeResolver.Context,
|
ResultTypeResolver.Context,
|
||||||
KotlinCallCompleter.Context,
|
KotlinCallCompleter.Context,
|
||||||
FixationOrderCalculator.Context,
|
|
||||||
ConstraintSystemCompleter.Context,
|
ConstraintSystemCompleter.Context,
|
||||||
PostponedArgumentsAnalyzer.Context
|
PostponedArgumentsAnalyzer.Context
|
||||||
{
|
{
|
||||||
@@ -256,10 +257,6 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
|
|||||||
get() = storage.postponedArguments.apply { checkState(State.BUILDING, State.COMPLETION) }
|
get() = storage.postponedArguments.apply { checkState(State.BUILDING, State.COMPLETION) }
|
||||||
|
|
||||||
// ConstraintSystemCompleter.Context
|
// 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) {
|
override fun fixVariable(variable: NewTypeVariable, resultType: UnwrappedType) {
|
||||||
checkState(State.BUILDING, State.COMPLETION)
|
checkState(State.BUILDING, State.COMPLETION)
|
||||||
|
|
||||||
|
|||||||
+37
-4
@@ -17,8 +17,12 @@
|
|||||||
package org.jetbrains.kotlin.resolve.calls.model
|
package org.jetbrains.kotlin.resolve.calls.model
|
||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.createFunctionType
|
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.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceCandidate
|
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.resolve.calls.inference.model.NewTypeVariable
|
||||||
import org.jetbrains.kotlin.types.SimpleType
|
import org.jetbrains.kotlin.types.SimpleType
|
||||||
import org.jetbrains.kotlin.types.UnwrappedType
|
import org.jetbrains.kotlin.types.UnwrappedType
|
||||||
@@ -27,6 +31,11 @@ import org.jetbrains.kotlin.types.typeUtil.builtIns
|
|||||||
|
|
||||||
sealed class PostponedKotlinCallArgument {
|
sealed class PostponedKotlinCallArgument {
|
||||||
abstract val argument: PostponableKotlinCallArgument
|
abstract val argument: PostponableKotlinCallArgument
|
||||||
|
|
||||||
|
abstract val analyzed: Boolean
|
||||||
|
|
||||||
|
abstract val inputTypes: Collection<UnwrappedType>
|
||||||
|
abstract val outputType: UnwrappedType?
|
||||||
}
|
}
|
||||||
|
|
||||||
class PostponedLambdaArgument(
|
class PostponedLambdaArgument(
|
||||||
@@ -36,12 +45,12 @@ class PostponedLambdaArgument(
|
|||||||
val parameters: List<UnwrappedType>,
|
val parameters: List<UnwrappedType>,
|
||||||
val returnType: UnwrappedType
|
val returnType: UnwrappedType
|
||||||
) : PostponedKotlinCallArgument() {
|
) : 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 type: SimpleType = createFunctionType(returnType.builtIns, Annotations.EMPTY, receiver, parameters, null, returnType, isSuspend) // todo support annotations
|
||||||
|
|
||||||
val inputTypes: Collection<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
|
override val inputTypes: Collection<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
|
||||||
val outputType: UnwrappedType get() = returnType
|
override val outputType: UnwrappedType get() = returnType
|
||||||
|
|
||||||
lateinit var resultArguments: List<SimpleKotlinCallArgument>
|
lateinit var resultArguments: List<SimpleKotlinCallArgument>
|
||||||
lateinit var finalReturnType: UnwrappedType
|
lateinit var finalReturnType: UnwrappedType
|
||||||
@@ -51,6 +60,22 @@ class PostponedCallableReferenceArgument(
|
|||||||
override val argument: CallableReferenceKotlinCallArgument,
|
override val argument: CallableReferenceKotlinCallArgument,
|
||||||
val expectedType: UnwrappedType
|
val expectedType: UnwrappedType
|
||||||
) : PostponedKotlinCallArgument() {
|
) : PostponedKotlinCallArgument() {
|
||||||
|
override var analyzed: Boolean = false
|
||||||
|
|
||||||
|
override val inputTypes: Collection<UnwrappedType>
|
||||||
|
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
|
var analyzedAndThereIsResult: Boolean = false
|
||||||
|
|
||||||
lateinit var myTypeVariables: List<NewTypeVariable>
|
lateinit var myTypeVariables: List<NewTypeVariable>
|
||||||
@@ -60,4 +85,12 @@ class PostponedCallableReferenceArgument(
|
|||||||
class PostponedCollectionLiteralArgument(
|
class PostponedCollectionLiteralArgument(
|
||||||
override val argument: CollectionLiteralKotlinCallArgument,
|
override val argument: CollectionLiteralKotlinCallArgument,
|
||||||
val expectedType: UnwrappedType
|
val expectedType: UnwrappedType
|
||||||
) : PostponedKotlinCallArgument()
|
) : 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<UnwrappedType>
|
||||||
|
get() = emptyList()
|
||||||
|
override val outputType: UnwrappedType?
|
||||||
|
get() = null
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user