[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:
Stanislav Erokhin
2017-07-13 19:09:24 +03:00
parent 3cf240340c
commit e011e443cc
18 changed files with 622 additions and 509 deletions
@@ -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
}
@@ -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
}
}
@@ -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) {
@@ -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
)
}
@@ -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) }
}
}
@@ -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
}
}
}
@@ -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
@@ -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
@@ -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
@@ -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)
}
}
@@ -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
}
}
}
@@ -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)
}
@@ -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(
@@ -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)
}
}
}
}
}
@@ -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
}
}
@@ -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
}
@@ -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)
@@ -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
}