[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 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?,
|
||||
unboundReceiverCount: Int
|
||||
): Triple<Array<KotlinType>, 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
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -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<LHSResult.Expression>()?.lshCallArgument.safeAs<SubKotlinCallArgument>()
|
||||
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.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<ResolvedKotlinCall.OnlyResolvedKotlinCall>
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
+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
|
||||
|
||||
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(
|
||||
|
||||
+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
|
||||
|
||||
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)
|
||||
|
||||
|
||||
+37
-4
@@ -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<UnwrappedType>
|
||||
abstract val outputType: UnwrappedType?
|
||||
}
|
||||
|
||||
class PostponedLambdaArgument(
|
||||
@@ -36,12 +45,12 @@ class PostponedLambdaArgument(
|
||||
val parameters: List<UnwrappedType>,
|
||||
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<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
|
||||
val outputType: UnwrappedType get() = returnType
|
||||
override val inputTypes: Collection<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
|
||||
override val outputType: UnwrappedType get() = returnType
|
||||
|
||||
lateinit var resultArguments: List<SimpleKotlinCallArgument>
|
||||
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<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
|
||||
|
||||
lateinit var myTypeVariables: List<NewTypeVariable>
|
||||
@@ -60,4 +85,12 @@ class PostponedCallableReferenceArgument(
|
||||
class PostponedCollectionLiteralArgument(
|
||||
override val argument: CollectionLiteralKotlinCallArgument,
|
||||
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