NI: Improve postponed arguments analysis

Introduce seven stages:
1) Analyze postponed arguments with fixed parameter types
2) Collect parameter types from constraints and lambda parameters' declaration
3) Fix not postponed variables for parameter types of all postponed arguments
4) Create atoms with revised expected types if needed
5) Analyze the first ready postponed argument and rerun stages if it has been analyzed
6) Force fixation remaining type variables: fix if possible or report not enough information
7) Force analysis remaining not analyzed postponed arguments and rerun stages if there are

^KT-37952 Fixed
^KT-32156 Fixed
^KT-37249 Fixed
^KT-37341 Fixed
This commit is contained in:
Victor Petukhov
2020-03-28 20:40:29 +03:00
parent 50151e0e01
commit 260683c20e
70 changed files with 2619 additions and 352 deletions
@@ -93,7 +93,7 @@ private fun extraLambdaInfo(
val isFunctionSupertype = expectedType != null && KotlinBuiltIns.isNotNullOrNullableFunctionSupertype(expectedType)
val argumentAsFunctionExpression = argument.safeAs<FunctionExpression>()
val typeVariable = TypeVariableForLambdaReturnType(argument, builtIns, "_L")
val typeVariable = TypeVariableForLambdaReturnType(builtIns, "_L")
val receiverType = argumentAsFunctionExpression?.receiverType
val returnType =
@@ -150,10 +150,7 @@ class PostponedArgumentsAnalyzer(
val subResolvedKtPrimitives = allReturnArguments.map {
resolveKtPrimitive(
c.getBuilder(), it, lambda.returnType.let(::substitute), diagnosticHolder, ReceiverInfo.notReceiver, convertedType = null
).apply {
if (this is LambdaWithTypeVariableAsExpectedTypeAtom)
isReturnArgumentOfAnotherLambda = true
}
)
}
if (!returnArgumentsInfo.returnArgumentsExist) {
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer
import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter
import org.jetbrains.kotlin.resolve.calls.inference.components.PostponedArgumentInputTypesResolver
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
@@ -35,4 +36,5 @@ interface NewConstraintSystem {
fun asConstraintSystemCompleterContext(): KotlinConstraintSystemCompleter.Context
fun asPostponedArgumentsAnalyzerContext(): PostponedArgumentsAnalyzer.Context
fun asPostponedArgumentInputTypesResolverContext(): PostponedArgumentInputTypesResolver.Context
}
@@ -5,28 +5,23 @@
package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.TypeVariableMarker
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import kotlin.collections.LinkedHashSet
class KotlinConstraintSystemCompleter(
private val resultTypeResolver: ResultTypeResolver,
val variableFixationFinder: VariableFixationFinder,
) {
private val postponedArgumentInputTypesResolver = PostponedArgumentInputTypesResolver(resultTypeResolver, variableFixationFinder)
enum class ConstraintSystemCompletionMode {
FULL,
PARTIAL
@@ -37,6 +32,8 @@ class KotlinConstraintSystemCompleter(
override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints>
override val postponedTypeVariables: List<TypeVariableMarker>
fun getBuilder(): ConstraintSystemBuilder
// type can be proper if it not contains not fixed type variables
fun canBeProper(type: KotlinTypeMarker): Boolean
@@ -46,6 +43,8 @@ class KotlinConstraintSystemCompleter(
fun addError(error: KotlinCallDiagnostic)
fun fixVariable(variable: TypeVariableMarker, resultType: KotlinTypeMarker, atom: ResolvedAtom?)
fun asPostponedArgumentInputTypesResolverContext(): PostponedArgumentInputTypesResolver.Context
}
fun runCompletion(
@@ -56,8 +55,7 @@ class KotlinConstraintSystemCompleter(
diagnosticsHolder: KotlinDiagnosticsHolder,
analyze: (PostponedResolvedAtom) -> Unit
) {
runCompletion(
c,
c.runCompletion(
completionMode,
topLevelAtoms,
topLevelType,
@@ -73,8 +71,7 @@ class KotlinConstraintSystemCompleter(
topLevelAtoms: List<ResolvedAtom>,
diagnosticsHolder: KotlinDiagnosticsHolder
) {
runCompletion(
c,
c.runCompletion(
ConstraintSystemCompletionMode.FULL,
topLevelAtoms,
topLevelType,
@@ -85,8 +82,8 @@ class KotlinConstraintSystemCompleter(
}
}
private fun runCompletion(
c: Context,
// TODO: investigate type variables fixation order and incorporation depth (see `diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.kt`)
private fun Context.runCompletion(
completionMode: ConstraintSystemCompletionMode,
topLevelAtoms: List<ResolvedAtom>,
topLevelType: UnwrappedType,
@@ -94,241 +91,242 @@ class KotlinConstraintSystemCompleter(
collectVariablesFromContext: Boolean,
analyze: (PostponedResolvedAtom) -> Unit
) {
while (true) {
if (analyzePostponeArgumentIfPossible(c, topLevelAtoms, analyze)) continue
completion@ while (true) {
val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)
val allTypeVariables = getOrderedAllTypeVariables(c, collectVariablesFromContext, topLevelAtoms)
val postponedKtPrimitives = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)
val variableForFixation =
variableFixationFinder.findFirstVariableForFixation(
c, allTypeVariables, postponedKtPrimitives, completionMode, topLevelType
) ?: break
if (
completionMode == ConstraintSystemCompletionMode.FULL &&
c.resolveLambdaByAdditionalConditions(
variableForFixation,
topLevelAtoms,
diagnosticsHolder,
analyze,
variableFixationFinder
)
) {
// Stage 1: analyze postponed arguments with fixed parameter types
if (analyzeArgumentWithFixedParameterTypes(postponedArguments, analyze))
continue
val isThereAnyReadyForFixationVariable = isThereAnyReadyForFixationVariable(
completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, postponedArguments
)
// If there aren't any postponed arguments and ready for fixation variables, then completion isn't needed: nothing to do
if (postponedArguments.isEmpty() && !isThereAnyReadyForFixationVariable)
break
val postponedArgumentsWithRevisableType = postponedArguments.filterIsInstance<PostponedAtomWithRevisableExpectedType>()
val dependencyProvider =
TypeVariableDependencyInformationProvider(notFixedTypeVariables, postponedArguments, topLevelType, this)
// Stage 2: collect parameter types for postponed arguments
postponedArgumentInputTypesResolver.collectParameterTypesAndBuildNewExpectedTypes(
asPostponedArgumentInputTypesResolverContext(), postponedArgumentsWithRevisableType, completionMode, dependencyProvider
)
if (completionMode == ConstraintSystemCompletionMode.FULL) {
// Stage 3: fix variables for parameter types of all postponed arguments
for (argument in postponedArguments) {
val wasFixedSomeVariable = postponedArgumentInputTypesResolver.fixNextReadyVariableForParameterTypeIfNeeded(
asPostponedArgumentInputTypesResolverContext(),
argument,
postponedArguments,
topLevelType,
topLevelAtoms,
dependencyProvider
)
if (wasFixedSomeVariable)
continue@completion
}
// Stage 4: create atoms with revised expected types if needed
for (argument in postponedArgumentsWithRevisableType) {
postponedArgumentInputTypesResolver.transformToAtomWithNewFunctionalExpectedType(
asPostponedArgumentInputTypesResolverContext(), argument, diagnosticsHolder
)
}
}
if (variableForFixation.hasProperConstraint || completionMode == ConstraintSystemCompletionMode.FULL) {
val variableWithConstraints = c.notFixedTypeVariables.getValue(variableForFixation.variable)
if (variableForFixation.hasProperConstraint)
fixVariable(c, variableWithConstraints, topLevelAtoms)
else
processVariableWhenNotEnoughInformation(c, variableWithConstraints, topLevelAtoms)
/*
* We should get not analyzed postponed arguments again because they can be changed by
* the stage of fixation type variables for parameters or analysing postponed arguments with fixed parameter types (see stage #1 and #4)
*/
val revisedPostponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)
// Stage 5: analyze the next ready postponed argument
if (analyzeNextReadyPostponedArgument(revisedPostponedArguments, completionMode, analyze))
continue
// Stage 6: fix type variables fix if possible or report not enough information (if completion mode is full)
val wasFixedSomeVariable = fixVariablesOrReportNotEnoughInformation(
completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, revisedPostponedArguments, diagnosticsHolder
)
if (wasFixedSomeVariable)
continue
// Stage 7: force analysis of remaining not analyzed postponed arguments and rerun stages if there are
if (completionMode == ConstraintSystemCompletionMode.FULL) {
if (analyzeRemainingNotAnalyzedPostponedArgument(revisedPostponedArguments, analyze))
continue
}
break
}
if (completionMode == ConstraintSystemCompletionMode.FULL) {
// force resolution for all not-analyzed argument's
getOrderedNotAnalyzedPostponedArguments(topLevelAtoms).forEach(analyze)
if (c.notFixedTypeVariables.isNotEmpty() && c.postponedTypeVariables.isEmpty()) {
runCompletion(c, completionMode, topLevelAtoms, topLevelType, diagnosticsHolder, analyze)
}
}
}
/*
* returns true -> analyzed
*/
private fun Context.resolveLambdaByAdditionalConditions(
variableForFixation: VariableFixationFinder.VariableForFixation,
topLevelAtoms: List<ResolvedAtom>,
diagnosticsHolder: KotlinDiagnosticsHolder,
analyze: (PostponedResolvedAtom) -> Unit,
fixationFinder: VariableFixationFinder
): Boolean {
val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)
if (postponedArguments.isEmpty())
return false
val groupedPostponedArguments = postponedArguments.filterIsInstance<LambdaWithTypeVariableAsExpectedTypeAtom>()
.ifEmpty { postponedArguments.filter { it !is LambdaWithTypeVariableAsExpectedTypeAtom } }
if (groupedPostponedArguments.isEmpty())
return false
fun shouldAnalyzeLambdaWithTypeVariableAsExpectedType(argument: PostponedResolvedAtom): Boolean {
val hasProperAtom = when (argument) {
is LambdaWithTypeVariableAsExpectedTypeAtom, is PostponedCallableReferenceAtom ->
argument.expectedType?.constructor == variableForFixation.variable
else -> false
}
return hasProperAtom || !variableForFixation.hasProperConstraint || variableForFixation.hasOnlyTrivialProperConstraint
}
fun shouldAnalyzeLambdaWhichIsReturnArgument(argument: PostponedResolvedAtom): Boolean {
val isReturnArgumentOfAnotherLambda =
argument is LambdaWithTypeVariableAsExpectedTypeAtom && argument.isReturnArgumentOfAnotherLambda
val atomExpectedType = argument.expectedType
return isReturnArgumentOfAnotherLambda && atomExpectedType != null &&
with(fixationFinder) { variableHasTrivialOrNonProperConstraints(atomExpectedType.constructor) }
}
return resolveLambdaByCondition(
variableForFixation, groupedPostponedArguments, diagnosticsHolder, analyze, ::shouldAnalyzeLambdaWithTypeVariableAsExpectedType
) || resolveLambdaByCondition(
variableForFixation, groupedPostponedArguments, diagnosticsHolder, analyze, ::shouldAnalyzeLambdaWhichIsReturnArgument
)
}
/*
* returns true -> analyzed
*/
private fun Context.resolveLambdaByCondition(
variableForFixation: VariableFixationFinder.VariableForFixation,
private fun Context.analyzeArgumentWithFixedParameterTypes(
postponedArguments: List<PostponedResolvedAtom>,
diagnosticsHolder: KotlinDiagnosticsHolder,
analyze: (PostponedResolvedAtom) -> Unit,
shouldAnalyze: (PostponedResolvedAtom) -> Boolean
): Boolean {
val variable = variableForFixation.variable as? TypeConstructor ?: return false
return postponedArguments.all { argument ->
if (!shouldAnalyze(argument))
return@all false
val expectedTypeVariable = argument.expectedType?.constructor?.takeIf { it in allTypeVariables } ?: variable
val preparedAtom =
preparePostponedAtom(expectedTypeVariable, argument, expectedTypeVariable.builtIns, diagnosticsHolder)
?: return@all false
analyze(preparedAtom)
true
}
}
private fun Context.preparePostponedAtom(
expectedTypeVariable: TypeConstructor,
postponedAtom: PostponedResolvedAtom,
builtIns: KotlinBuiltIns,
diagnosticsHolder: KotlinDiagnosticsHolder
): PostponedResolvedAtom? {
val csBuilder = (this as? NewConstraintSystem)?.getBuilder() ?: return null
return when (postponedAtom) {
is PostponedCallableReferenceAtom -> postponedAtom.preparePostponedAtomWithTypeVariableAsExpectedType(
this, csBuilder, expectedTypeVariable,
parameterTypes = null,
isSuitable = KotlinType::isBuiltinFunctionalTypeOrSubtype,
typeVariableCreator = { TypeVariableForCallableReferenceReturnType(builtIns, "_Q") },
newAtomCreator = { returnVariable, expectedType ->
CallableReferenceWithTypeVariableAsExpectedTypeAtom(postponedAtom.atom, expectedType, returnVariable).also {
postponedAtom.setAnalyzedResults(null, listOf(it))
}
}
)
is LambdaWithTypeVariableAsExpectedTypeAtom -> postponedAtom.preparePostponedAtomWithTypeVariableAsExpectedType(
this, csBuilder, expectedTypeVariable,
parameterTypes = postponedAtom.atom.parametersTypes,
isSuitable = KotlinType::isBuiltinFunctionalType,
typeVariableCreator = { TypeVariableForLambdaReturnType(postponedAtom.atom, builtIns, "_R") },
newAtomCreator = { returnVariable, expectedType ->
postponedAtom.transformToResolvedLambda(csBuilder, diagnosticsHolder, expectedType, returnVariable)
}
)
else -> null
}
}
private inline fun <T : PostponedResolvedAtom, V : NewTypeVariable> T.preparePostponedAtomWithTypeVariableAsExpectedType(
c: Context,
csBuilder: ConstraintSystemBuilder,
variable: TypeConstructor,
parameterTypes: Array<out KotlinType?>?,
isSuitable: KotlinType.() -> Boolean,
typeVariableCreator: () -> V,
newAtomCreator: (V, SimpleType) -> PostponedResolvedAtom
): PostponedResolvedAtom {
val functionalType = resultTypeResolver.findResultType(
c,
c.notFixedTypeVariables.getValue(variable),
TypeVariableDirectionCalculator.ResolveDirection.TO_SUPERTYPE
) as KotlinType
val isExtensionFunction = functionalType.isExtensionFunctionType
val isExtensionFunctionWithReceiverAsDeclaredParameter =
isExtensionFunction && functionalType.arguments.size - 1 == parameterTypes?.count { it != null }
if (parameterTypes?.all { it != null } == true && (!isExtensionFunction || isExtensionFunctionWithReceiverAsDeclaredParameter)) return this
if (!functionalType.isSuitable()) return this
val returnVariable = typeVariableCreator()
csBuilder.registerVariable(returnVariable)
val expectedType = KotlinTypeFactory.simpleType(
functionalType.annotations,
functionalType.constructor,
functionalType.arguments.dropLast(1) + returnVariable.defaultType.asTypeProjection(),
functionalType.isMarkedNullable
)
csBuilder.addSubtypeConstraint(
expectedType,
variable.typeForTypeVariable(),
ArgumentConstraintPosition(atom as KotlinCallArgument)
)
return newAtomCreator(returnVariable, expectedType)
}
// true if we do analyze
private fun analyzePostponeArgumentIfPossible(
c: Context,
topLevelAtoms: List<ResolvedAtom>,
analyze: (PostponedResolvedAtom) -> Unit
): Boolean {
for (argument in getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
if (canWeAnalyzeIt(c, argument)) {
analyze(argument)
return true
}
val argumentWithFixedOrPostponedInputTypes = findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments)
if (argumentWithFixedOrPostponedInputTypes != null) {
analyze(argumentWithFixedOrPostponedInputTypes)
return true
}
return false
}
private fun getOrderedAllTypeVariables(
private fun Context.analyzeNextReadyPostponedArgument(
postponedArguments: List<PostponedResolvedAtom>,
completionMode: ConstraintSystemCompletionMode,
analyze: (PostponedResolvedAtom) -> Unit
): Boolean {
if (completionMode == ConstraintSystemCompletionMode.FULL) {
val argumentWithTypeVariableAsExpectedType = findPostponedArgumentWithRevisableExpectedType(postponedArguments)
if (argumentWithTypeVariableAsExpectedType != null) {
analyze(argumentWithTypeVariableAsExpectedType)
return true
}
}
return analyzeArgumentWithFixedParameterTypes(postponedArguments, analyze)
}
private fun analyzeRemainingNotAnalyzedPostponedArgument(
postponedArguments: List<PostponedResolvedAtom>,
analyze: (PostponedResolvedAtom) -> Unit
): Boolean {
val remainingNotAnalyzedPostponedArgument = postponedArguments.firstOrNull { !it.analyzed }
if (remainingNotAnalyzedPostponedArgument != null) {
analyze(remainingNotAnalyzedPostponedArgument)
return true
}
return false
}
private fun findPostponedArgumentWithRevisableExpectedType(postponedArguments: List<PostponedResolvedAtom>) =
postponedArguments.firstOrNull { argument -> argument is PostponedAtomWithRevisableExpectedType }
private fun Context.findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments: List<PostponedResolvedAtom>) =
postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedOrPostponedVariables(it) } }
private fun fixVariable(
c: Context,
variableWithConstraints: VariableWithConstraints,
topLevelAtoms: List<ResolvedAtom>
) {
fixVariable(c, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN, topLevelAtoms)
}
private fun fixVariable(
c: Context,
variableWithConstraints: VariableWithConstraints,
direction: TypeVariableDirectionCalculator.ResolveDirection,
topLevelAtoms: List<ResolvedAtom>
) {
val resultType = resultTypeResolver.findResultType(c, variableWithConstraints, direction)
val resolvedAtom = findResolvedAtomBy(variableWithConstraints.typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull()
c.fixVariable(variableWithConstraints.typeVariable, resultType, resolvedAtom)
}
private fun Context.fixVariablesOrReportNotEnoughInformation(
completionMode: ConstraintSystemCompletionMode,
topLevelAtoms: List<ResolvedAtom>,
topLevelType: UnwrappedType,
collectVariablesFromContext: Boolean,
postponedArguments: List<PostponedResolvedAtom>,
diagnosticsHolder: KotlinDiagnosticsHolder
): Boolean {
while (true) {
val variableForFixation = getVariableReadyForFixation(
completionMode,
topLevelAtoms,
topLevelType,
collectVariablesFromContext,
postponedArguments
) ?: break
if (!variableForFixation.hasProperConstraint && completionMode == ConstraintSystemCompletionMode.PARTIAL)
break
val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
if (variableForFixation.hasProperConstraint) {
fixVariable(this, variableWithConstraints, topLevelAtoms)
return true
} else {
processVariableWhenNotEnoughInformation(this, variableWithConstraints, topLevelAtoms, diagnosticsHolder)
}
}
return false
}
private fun processVariableWhenNotEnoughInformation(
c: Context,
variableWithConstraints: VariableWithConstraints,
topLevelAtoms: List<ResolvedAtom>,
diagnosticsHolder: KotlinDiagnosticsHolder
) {
val typeVariable = variableWithConstraints.typeVariable
val resolvedAtom = findResolvedAtomBy(typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull()
if (resolvedAtom != null) {
c.addError(NotEnoughInformationForTypeParameter(typeVariable, resolvedAtom))
}
val resultErrorType = when {
typeVariable is TypeVariableFromCallableDescriptor ->
ErrorUtils.createUninferredParameterType(typeVariable.originalTypeParameter)
typeVariable is TypeVariableForLambdaParameterType && typeVariable.atom is LambdaKotlinCallArgument -> {
diagnosticsHolder.addDiagnostic(
NotEnoughInformationForLambdaParameter(typeVariable.atom, typeVariable.index)
)
ErrorUtils.createErrorType("Cannot infer lambda parameter type")
}
else -> ErrorUtils.createErrorType("Cannot infer type variable $typeVariable")
}
c.fixVariable(typeVariable, resultErrorType, resolvedAtom)
}
private fun Context.getOrderedAllTypeVariables(
collectVariablesFromContext: Boolean,
topLevelAtoms: List<ResolvedAtom>
): List<TypeConstructorMarker> {
if (collectVariablesFromContext) return c.notFixedTypeVariables.keys.toList()
if (collectVariablesFromContext)
return notFixedTypeVariables.keys.toList()
fun getVariablesFromRevisedExpectedType(revisedExpectedType: KotlinType?) =
revisedExpectedType?.arguments?.map { it.type.constructor }?.filterIsInstance<TypeVariableTypeConstructor>()
fun ResolvedAtom.process(to: LinkedHashSet<TypeConstructor>) {
val typeVariables = when (this) {
is ResolvedCallAtom -> freshVariablesSubstitutor.freshVariables
is CallableReferenceWithTypeVariableAsExpectedTypeAtom -> mutableListOf<NewTypeVariable>().apply {
addIfNotNull(typeVariableForReturnType)
addAll(candidate?.freshSubstitutor?.freshVariables.orEmpty())
}
is ResolvedCallableReferenceAtom -> candidate?.freshSubstitutor?.freshVariables.orEmpty()
is ResolvedLambdaAtom -> listOfNotNull(typeVariableForLambdaReturnType)
is LambdaWithTypeVariableAsExpectedTypeAtom -> getVariablesFromRevisedExpectedType(revisedExpectedType).orEmpty()
is ResolvedCallAtom -> freshVariablesSubstitutor.freshVariables.map { it.freshTypeConstructor }
is PostponedCallableReferenceAtom ->
getVariablesFromRevisedExpectedType(revisedExpectedType).orEmpty() +
candidate?.freshSubstitutor?.freshVariables?.map { it.freshTypeConstructor }.orEmpty()
is ResolvedCallableReferenceAtom -> candidate?.freshSubstitutor?.freshVariables?.map { it.freshTypeConstructor }.orEmpty()
is ResolvedLambdaAtom -> listOfNotNull(typeVariableForLambdaReturnType?.freshTypeConstructor)
else -> emptyList()
}
typeVariables.mapNotNullTo(to) {
val typeConstructor = it.freshTypeConstructor
typeConstructor.takeIf { c.notFixedTypeVariables.containsKey(typeConstructor) }
it.takeIf { notFixedTypeVariables.containsKey(it) }
}
/*
* Hack for completing error candidates in delegate resolve
*/
if (this is StubResolvedAtom && typeVariable in c.notFixedTypeVariables) {
if (this is StubResolvedAtom && typeVariable in notFixedTypeVariables) {
to += typeVariable
}
@@ -343,86 +341,35 @@ class KotlinConstraintSystemCompleter(
primitive.process(result)
}
assert(result.size == c.notFixedTypeVariables.size) {
val notFoundTypeVariables = c.notFixedTypeVariables.keys.toMutableSet().apply { removeAll(result) }
assert(result.size == notFixedTypeVariables.size) {
val notFoundTypeVariables = notFixedTypeVariables.keys.toMutableSet().apply { removeAll(result) }
"Not all type variables found: $notFoundTypeVariables"
}
return result.toList()
}
private fun Context.getVariableReadyForFixation(
completionMode: ConstraintSystemCompletionMode,
topLevelAtoms: List<ResolvedAtom>,
topLevelType: UnwrappedType,
collectVariablesFromContext: Boolean,
postponedArguments: List<PostponedResolvedAtom>
) = variableFixationFinder.findFirstVariableForFixation(
this,
getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms),
postponedArguments,
completionMode,
topLevelType
)
private fun canWeAnalyzeIt(c: Context, argument: PostponedResolvedAtom): Boolean {
if (argument.analyzed) return false
return argument.inputTypes.all { c.containsOnlyFixedOrPostponedVariables(it) }
}
private fun fixVariable(
c: Context,
variableWithConstraints: VariableWithConstraints,
topLevelAtoms: List<ResolvedAtom>
) {
fixVariable(c, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN, topLevelAtoms)
}
fun fixVariable(
c: Context,
variableWithConstraints: VariableWithConstraints,
direction: TypeVariableDirectionCalculator.ResolveDirection,
topLevelAtoms: List<ResolvedAtom>
) {
val resultType = resultTypeResolver.findResultType(c, variableWithConstraints, direction)
val resolvedAtom = findResolvedAtomBy(variableWithConstraints.typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull()
c.fixVariable(variableWithConstraints.typeVariable, resultType, resolvedAtom)
}
private fun processVariableWhenNotEnoughInformation(
c: Context,
variableWithConstraints: VariableWithConstraints,
topLevelAtoms: List<ResolvedAtom>
) {
val typeVariable = variableWithConstraints.typeVariable
val resolvedAtom = findResolvedAtomBy(typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull()
if (resolvedAtom != null) {
c.addError(NotEnoughInformationForTypeParameter(typeVariable, resolvedAtom))
}
val resultErrorType = if (typeVariable is TypeVariableFromCallableDescriptor)
ErrorUtils.createUninferredParameterType(typeVariable.originalTypeParameter)
else
ErrorUtils.createErrorType("Cannot infer type variable $typeVariable")
c.fixVariable(typeVariable, resultErrorType, resolvedAtom)
}
private fun findResolvedAtomBy(typeVariable: TypeVariableMarker, topLevelAtoms: List<ResolvedAtom>): ResolvedAtom? {
fun ResolvedAtom.check(): ResolvedAtom? {
val suitableCall = when (this) {
is ResolvedCallAtom -> typeVariable in freshVariablesSubstitutor.freshVariables
is ResolvedCallableReferenceAtom -> candidate?.freshSubstitutor?.freshVariables?.let { typeVariable in it } ?: false
is ResolvedLambdaAtom -> typeVariable == typeVariableForLambdaReturnType
else -> false
}
if (suitableCall) {
return this
}
subResolvedAtoms?.forEach { subResolvedAtom ->
subResolvedAtom.check()?.let { result -> return@check result }
}
return null
}
for (topLevelAtom in topLevelAtoms) {
topLevelAtom.check()?.let { return it }
}
return null
}
private fun Context.isThereAnyReadyForFixationVariable(
completionMode: ConstraintSystemCompletionMode,
topLevelAtoms: List<ResolvedAtom>,
topLevelType: UnwrappedType,
collectVariablesFromContext: Boolean,
postponedArguments: List<PostponedResolvedAtom>
) = getVariableReadyForFixation(completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, postponedArguments) != null
companion object {
fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List<ResolvedAtom>): List<PostponedResolvedAtom> {
@@ -441,5 +388,32 @@ class KotlinConstraintSystemCompleter(
return notAnalyzedArguments
}
fun findResolvedAtomBy(typeVariable: TypeVariableMarker, topLevelAtoms: List<ResolvedAtom>): ResolvedAtom? {
fun ResolvedAtom.check(): ResolvedAtom? {
val suitableCall = when (this) {
is ResolvedCallAtom -> typeVariable in freshVariablesSubstitutor.freshVariables
is ResolvedCallableReferenceAtom -> candidate?.freshSubstitutor?.freshVariables?.let { typeVariable in it } ?: false
is ResolvedLambdaAtom -> typeVariable == typeVariableForLambdaReturnType
else -> false
}
if (suitableCall) {
return this
}
subResolvedAtoms?.forEach { subResolvedAtom ->
subResolvedAtom.check()?.let { result -> return@check result }
}
return null
}
for (topLevelAtom in topLevelAtoms) {
topLevelAtom.check()?.let { return it }
}
return null
}
}
}
}
@@ -0,0 +1,473 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.builtins.*
import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class PostponedArgumentInputTypesResolver(
private val resultTypeResolver: ResultTypeResolver,
private val variableFixationFinder: VariableFixationFinder
) {
interface Context : KotlinConstraintSystemCompleter.Context
data class ParameterTypesInfo(
val parametersFromDeclaration: List<UnwrappedType?>?,
val parametersFromDeclarationOfRelatedLambdas: Set<List<UnwrappedType?>>?,
val parametersFromConstraints: Set<List<TypeWithKind>>?,
val annotations: Annotations,
val isSuspend: Boolean,
val isNullable: Boolean
)
data class TypeWithKind(
val type: KotlinType,
val direction: ConstraintKind = ConstraintKind.UPPER
)
private fun Context.findFunctionalTypesInConstraints(
variable: VariableWithConstraints,
variableDependencyProvider: TypeVariableDependencyInformationProvider
): List<TypeWithKind>? {
fun List<Constraint>.extractFunctionalTypes() = mapNotNull { constraint ->
val type = constraint.type as? KotlinType ?: return@mapNotNull null
TypeWithKind(type.extractFunctionalTypeFromSupertypes(), constraint.kind)
}
val typeVariableTypeConstructor = variable.typeVariable.freshTypeConstructor() as? TypeVariableTypeConstructor ?: return null
val dependentVariables =
variableDependencyProvider.getShallowlyDependentVariables(typeVariableTypeConstructor).orEmpty() + typeVariableTypeConstructor
return dependentVariables.mapNotNull { type ->
val constraints = notFixedTypeVariables[type]?.constraints ?: return@mapNotNull null
val constraintsWithFunctionalType = constraints.filter { (it.type as? KotlinType)?.isBuiltinFunctionalTypeOrSubtype == true }
constraintsWithFunctionalType.extractFunctionalTypes()
}.flatten()
}
private fun extractParameterTypesFromDeclaration(atom: ResolutionAtom) =
when (atom) {
is FunctionExpression -> {
val receiverType = atom.receiverType
if (receiverType != null) listOf(receiverType) + atom.parametersTypes else atom.parametersTypes.toList()
}
is LambdaKotlinCallArgument -> atom.parametersTypes?.toList()
else -> null
}
private fun Context.extractParameterTypesInfo(
argument: PostponedAtomWithRevisableExpectedType,
postponedArguments: List<PostponedAtomWithRevisableExpectedType>,
variableDependencyProvider: TypeVariableDependencyInformationProvider
): ParameterTypesInfo? {
val expectedType = argument.expectedType ?: return null
val variableWithConstraints = notFixedTypeVariables[expectedType.constructor] ?: return null
// We shouldn't collect functional types from constraints for anonymous functions as they have fully explicit declaration form
val functionalTypesFromConstraints = if (!isAnonymousFunction(argument)) {
findFunctionalTypesInConstraints(variableWithConstraints, variableDependencyProvider)
} else null
// Don't create functional expected type for further error reporting about a different number of arguments
if (functionalTypesFromConstraints != null && functionalTypesFromConstraints.distinctBy { it.type.argumentsCount() }.size > 1)
return null
val parameterTypesFromDeclaration =
if (argument is LambdaWithTypeVariableAsExpectedTypeAtom) argument.parameterTypesFromDeclaration else null
val parameterTypesFromConstraints = functionalTypesFromConstraints?.map { typeWithKind ->
typeWithKind.type.getPureArgumentsForFunctionalTypeOrSubtype().map {
// We should use opposite kind as lambda's parameters are contravariant
TypeWithKind(it, typeWithKind.direction.opposite())
}
}?.toSet()
val annotations = functionalTypesFromConstraints?.run {
Annotations.create(map { it.type.annotations }.flatten())
}
val parameterTypesFromDeclarationOfRelatedLambdas =
getDeclaredParametersFromRelatedLambdas(argument, postponedArguments, variableDependencyProvider)
return ParameterTypesInfo(
parameterTypesFromDeclaration,
parameterTypesFromDeclarationOfRelatedLambdas,
parameterTypesFromConstraints,
annotations ?: Annotations.EMPTY,
isSuspend = !functionalTypesFromConstraints.isNullOrEmpty() && functionalTypesFromConstraints.any { it.type.isSuspendFunctionTypeOrSubtype },
isNullable = !functionalTypesFromConstraints.isNullOrEmpty() && functionalTypesFromConstraints.all { it.type.isMarkedNullable }
)
}
private fun Context.getDeclaredParametersFromRelatedLambdas(
argument: PostponedAtomWithRevisableExpectedType,
postponedArguments: List<PostponedAtomWithRevisableExpectedType>,
dependencyProvider: TypeVariableDependencyInformationProvider
): Set<List<UnwrappedType?>>? {
fun PostponedAtomWithRevisableExpectedType.getExpectedTypeConstructor() = expectedType?.typeConstructor()
val parameterTypesFromDeclarationOfRelatedLambdas = postponedArguments
.filterIsInstance<LambdaWithTypeVariableAsExpectedTypeAtom>()
.filter { it.parameterTypesFromDeclaration != null && it != argument }
.mapNotNull { anotherArgument ->
val argumentExpectedTypeConstructor = argument.getExpectedTypeConstructor() ?: return@mapNotNull null
val anotherArgumentExpectedTypeConstructor = anotherArgument.getExpectedTypeConstructor() ?: return@mapNotNull null
val areTypeVariablesRelated = dependencyProvider.areVariablesDependentShallowly(
argumentExpectedTypeConstructor,
anotherArgumentExpectedTypeConstructor
)
if (areTypeVariablesRelated) anotherArgument.parameterTypesFromDeclaration else null
}
return parameterTypesFromDeclarationOfRelatedLambdas.toSet().takeIf { it.isNotEmpty() }
}
private fun Context.createTypeVariableForReturnType(argument: PostponedAtomWithRevisableExpectedType): NewTypeVariable {
val expectedType = argument.expectedType
?: throw IllegalStateException("Postponed argument's expected type must not be null")
return when (argument) {
is LambdaWithTypeVariableAsExpectedTypeAtom -> TypeVariableForLambdaReturnType(
expectedType.builtIns,
TYPE_VARIABLE_NAME_FOR_LAMBDA_RETURN_TYPE
)
is PostponedCallableReferenceAtom -> TypeVariableForCallableReferenceReturnType(
expectedType.builtIns,
TYPE_VARIABLE_NAME_FOR_CR_RETURN_TYPE
)
else -> throw IllegalStateException("Unsupported postponed argument type of $argument")
}.also { getBuilder().registerVariable(it) }
}
private fun Context.createTypeVariableForParameterType(
argument: PostponedAtomWithRevisableExpectedType,
index: Int
): NewTypeVariable {
val expectedType = argument.expectedType
?: throw IllegalStateException("Postponed argument's expected type must not be null")
return when (argument) {
is LambdaWithTypeVariableAsExpectedTypeAtom -> TypeVariableForLambdaParameterType(
argument.atom,
index,
expectedType.builtIns,
TYPE_VARIABLE_NAME_PREFIX_FOR_LAMBDA_PARAMETER_TYPE + (index + 1)
)
is PostponedCallableReferenceAtom -> TypeVariableForCallableReferenceParameterType(
expectedType.builtIns,
TYPE_VARIABLE_NAME_PREFIX_FOR_CR_PARAMETER_TYPE + (index + 1)
)
else -> throw IllegalStateException("Unsupported postponed argument type of $argument")
}.also { getBuilder().registerVariable(it) }
}
private fun Context.createTypeVariablesForParameters(
argument: PostponedAtomWithRevisableExpectedType,
parameterTypes: List<List<TypeWithKind?>>
): List<TypeProjection> {
val atom = argument.atom
val csBuilder = getBuilder()
val allGroupedParameterTypes = parameterTypes.first().indices.map { i -> parameterTypes.map { it.getOrNull(i) } }
return allGroupedParameterTypes.mapIndexed { index, types ->
val parameterTypeVariable = createTypeVariableForParameterType(argument, index)
for (typeWithKind in types.filterNotNull()) {
when (typeWithKind.direction) {
ConstraintKind.EQUALITY -> csBuilder.addEqualityConstraint(
parameterTypeVariable.defaultType, typeWithKind.type, ArgumentConstraintPosition(atom)
)
ConstraintKind.UPPER -> csBuilder.addSubtypeConstraint(
parameterTypeVariable.defaultType, typeWithKind.type, ArgumentConstraintPosition(atom)
)
ConstraintKind.LOWER -> csBuilder.addSubtypeConstraint(
typeWithKind.type, parameterTypeVariable.defaultType, ArgumentConstraintPosition(atom)
)
}
}
parameterTypeVariable.defaultType.asTypeProjection()
}
}
private fun Context.computeResultingFunctionalConstructor(
argument: PostponedAtomWithRevisableExpectedType,
parametersNumber: Int,
isSuspend: Boolean,
resultTypeResolver: ResultTypeResolver
): TypeConstructor {
val expectedType = argument.expectedType
?: throw IllegalStateException("Postponed argument's expected type must not be null")
val expectedTypeConstructor = expectedType.constructor
return when (argument) {
is LambdaWithTypeVariableAsExpectedTypeAtom ->
getFunctionDescriptor(expectedTypeConstructor.builtIns, parametersNumber, isSuspend).typeConstructor
is PostponedCallableReferenceAtom -> {
val computedResultType = resultTypeResolver.findResultType(
this,
notFixedTypeVariables.getValue(expectedTypeConstructor),
TypeVariableDirectionCalculator.ResolveDirection.TO_SUPERTYPE
)
// Avoid KFunction<...>/Function<...> types
if (computedResultType.isBuiltinFunctionalTypeOrSubtype() && computedResultType.argumentsCount() > 1) {
computedResultType.typeConstructor() as TypeConstructor
} else {
getKFunctionDescriptor(expectedTypeConstructor.builtIns, parametersNumber, isSuspend).typeConstructor
}
}
else -> throw IllegalStateException("Unsupported postponed argument type of $argument")
}
}
private fun Context.buildNewFunctionalExpectedType(
argument: PostponedAtomWithRevisableExpectedType,
parameterTypesInfo: ParameterTypesInfo
): UnwrappedType? {
val expectedType = argument.expectedType
if (expectedType == null || expectedType.constructor !in notFixedTypeVariables)
return null
val atom = argument.atom
val parametersFromConstraints = parameterTypesInfo.parametersFromConstraints
val parametersFromDeclaration = getDeclaredParametersConsideringExtensionFunctionsPresence(parameterTypesInfo)
val areAllParameterTypesSpecified = !parametersFromDeclaration.isNullOrEmpty() && parametersFromDeclaration.all { it != null }
val isExtensionFunction = parameterTypesInfo.annotations.hasExtensionFunctionAnnotation()
val parametersFromDeclarations = parameterTypesInfo.parametersFromDeclarationOfRelatedLambdas.orEmpty() + parametersFromDeclaration
/*
* We shouldn't create synthetic functional type if all lambda's parameter types are specified explicitly
*
* TODO: regarding anonymous functions: see info about need for analysis in partial mode in `collectParameterTypesAndBuildNewExpectedTypes`
*/
if (areAllParameterTypesSpecified && !isExtensionFunction && !isAnonymousFunction(argument))
return null
val allParameterTypes =
(parametersFromConstraints.orEmpty() + parametersFromDeclarations.map { parameters -> parameters?.map { it.wrapToTypeWithKind() } }).filterNotNull()
if (allParameterTypes.isEmpty())
return null
val variablesForParameterTypes = createTypeVariablesForParameters(argument, allParameterTypes)
val variableForReturnType = createTypeVariableForReturnType(argument)
val functionalConstructor = computeResultingFunctionalConstructor(
argument,
variablesForParameterTypes.size,
parameterTypesInfo.isSuspend,
resultTypeResolver
)
val isExtensionFunctionType = parameterTypesInfo.annotations.hasExtensionFunctionAnnotation()
val areParametersNumberInDeclarationAndConstraintsEqual =
!parametersFromDeclaration.isNullOrEmpty() && !parametersFromConstraints.isNullOrEmpty()
&& parametersFromDeclaration.size == parametersFromConstraints.first().size
/*
* We need to exclude further considering a postponed argument as an extension function
* to support cases with explicitly specified receiver as a value parameter (only if all parameter types are specified)
*
* Example: `val x: String.() -> Int = id { x: String -> 42 }`
*/
val shouldDiscriminateExtensionFunctionAnnotation =
isExtensionFunctionType && areAllParameterTypesSpecified && areParametersNumberInDeclarationAndConstraintsEqual
/*
* We need to add an extension function annotation for anonymous functions with an explicitly specified receiver
*
* Example: `val x = id(fun String.() = this)`
*/
val shouldAddExtensionFunctionAnnotation = atom is FunctionExpression && atom.receiverType != null
val annotations = when {
shouldDiscriminateExtensionFunctionAnnotation ->
parameterTypesInfo.annotations.withoutExtensionFunctionAnnotation()
shouldAddExtensionFunctionAnnotation ->
parameterTypesInfo.annotations.withExtensionFunctionAnnotation(expectedType.builtIns)
else -> parameterTypesInfo.annotations
}
val nexExpectedType = KotlinTypeFactory.simpleType(
annotations,
functionalConstructor,
variablesForParameterTypes + variableForReturnType.defaultType.asTypeProjection(),
parameterTypesInfo.isNullable
)
getBuilder().addSubtypeConstraint(
nexExpectedType,
expectedType,
ArgumentConstraintPosition(argument.atom)
)
return nexExpectedType
}
fun collectParameterTypesAndBuildNewExpectedTypes(
c: Context,
postponedArguments: List<PostponedAtomWithRevisableExpectedType>,
completionMode: KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode,
dependencyProvider: TypeVariableDependencyInformationProvider
) {
// We can collect parameter types from declaration in any mode, they can't change during completion.
val postponedArgumentsToCollectTypesFromDeclaredParameters = postponedArguments
.filterIsInstance<LambdaWithTypeVariableAsExpectedTypeAtom>()
.filter { it.parameterTypesFromDeclaration == null }
for (argument in postponedArgumentsToCollectTypesFromDeclaredParameters) {
argument.parameterTypesFromDeclaration = extractParameterTypesFromDeclaration(argument.atom)
}
/*
* We can build new functional expected types in partial mode only for anonymous functions,
* because more exact type can't appear from constraints in full mode (anonymous functions have fully explicit declaration).
* It can be so for lambdas: for instance, an extension function type can appear in full mode (it may not be known in partial mode).
*
* TODO: investigate why we can't do it for anonymous functions in full mode always (see `diagnostics/tests/resolve/resolveWithSpecifiedFunctionLiteralWithId.kt`)
*/
val postponedArgumentsToCollectParameterTypesAndBuildNewExpectedType =
if (completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL) {
postponedArguments.filter(::isAnonymousFunction)
} else {
postponedArguments
}
do {
val wasTransformedSomePostponedArgument =
postponedArgumentsToCollectParameterTypesAndBuildNewExpectedType.filter { it.revisedExpectedType == null }.any { argument ->
val parameterTypesInfo =
c.extractParameterTypesInfo(argument, postponedArguments, dependencyProvider) ?: return@any false
val newExpectedType =
c.buildNewFunctionalExpectedType(argument, parameterTypesInfo) ?: return@any false
argument.revisedExpectedType = newExpectedType
true
}
} while (wasTransformedSomePostponedArgument)
}
fun transformToAtomWithNewFunctionalExpectedType(
c: Context,
argument: PostponedAtomWithRevisableExpectedType,
diagnosticsHolder: KotlinDiagnosticsHolder
) {
val revisedExpectedType = argument.revisedExpectedType?.takeIf { it.isFunctionOrKFunctionTypeWithAnySuspendability } ?: return
when (argument) {
is PostponedCallableReferenceAtom ->
CallableReferenceWithRevisedExpectedTypeAtom(argument.atom, revisedExpectedType).also {
argument.setAnalyzedResults(null, listOf(it))
}
is LambdaWithTypeVariableAsExpectedTypeAtom ->
argument.transformToResolvedLambda(c.getBuilder(), diagnosticsHolder, revisedExpectedType)
else -> throw IllegalStateException("Unsupported postponed argument type of $argument")
}
}
private fun getAllDeeplyRelatedTypeVariables(
type: KotlinType,
variableDependencyProvider: TypeVariableDependencyInformationProvider
): List<TypeVariableTypeConstructor> {
val typeConstructor = type.constructor
return when {
typeConstructor is TypeVariableTypeConstructor -> {
val relatedVariables = variableDependencyProvider.getDeeplyDependentVariables(typeConstructor).orEmpty()
listOf(typeConstructor) + relatedVariables.filterIsInstance<TypeVariableTypeConstructor>()
}
type.arguments.isNotEmpty() -> {
type.arguments.map { getAllDeeplyRelatedTypeVariables(it.type, variableDependencyProvider) }.flatten()
}
else -> listOf()
}
}
private fun getDeclaredParametersConsideringExtensionFunctionsPresence(parameterTypesInfo: ParameterTypesInfo): List<UnwrappedType?>? {
val (parametersFromDeclaration, _, parametersFromConstraints, annotations) = parameterTypesInfo
if (parametersFromConstraints.isNullOrEmpty() || parametersFromDeclaration.isNullOrEmpty())
return parametersFromDeclaration
val oneLessParameterInDeclarationThanInConstraints = parametersFromConstraints.first().size == parametersFromDeclaration.size + 1
return if (oneLessParameterInDeclarationThanInConstraints && annotations.hasExtensionFunctionAnnotation()) {
listOf(null) + parametersFromDeclaration
} else {
parametersFromDeclaration
}
}
fun fixNextReadyVariableForParameterTypeIfNeeded(
c: Context,
argument: PostponedResolvedAtom,
postponedArguments: List<PostponedResolvedAtom>,
topLevelType: UnwrappedType,
topLevelAtoms: List<ResolvedAtom>,
dependencyProvider: TypeVariableDependencyInformationProvider
): Boolean {
val expectedType = argument.run { safeAs<PostponedAtomWithRevisableExpectedType>()?.revisedExpectedType ?: expectedType }
if (expectedType != null && expectedType.isFunctionOrKFunctionTypeWithAnySuspendability) {
val wasFixedSomeVariable =
c.fixNextReadyVariableForParameterType(expectedType, postponedArguments, topLevelType, topLevelAtoms, dependencyProvider)
if (wasFixedSomeVariable)
return true
}
return false
}
private fun Context.fixNextReadyVariableForParameterType(
type: KotlinType,
postponedArguments: List<PostponedResolvedAtom>,
topLevelType: UnwrappedType,
topLevelAtoms: List<ResolvedAtom>,
dependencyProvider: TypeVariableDependencyInformationProvider
): Boolean {
val relatedVariables = type.getPureArgumentsForFunctionalTypeOrSubtype()
.map { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) }.flatten()
val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
this, relatedVariables, postponedArguments, KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL, topLevelType
)
if (variableForFixation == null || !variableForFixation.hasProperConstraint)
return false
val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable)
val resultType =
resultTypeResolver.findResultType(this, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN)
val resolvedAtom = KotlinConstraintSystemCompleter.findResolvedAtomBy(variableWithConstraints.typeVariable, topLevelAtoms)
?: topLevelAtoms.firstOrNull()
fixVariable(variableWithConstraints.typeVariable, resultType, resolvedAtom)
return true
}
private fun KotlinType?.wrapToTypeWithKind() = this?.let { TypeWithKind(it) }
private fun isAnonymousFunction(argument: PostponedAtomWithRevisableExpectedType) = argument.atom is FunctionExpression
companion object {
private const val TYPE_VARIABLE_NAME_PREFIX_FOR_LAMBDA_PARAMETER_TYPE = "_RP"
private const val TYPE_VARIABLE_NAME_FOR_LAMBDA_RETURN_TYPE = "_R"
private const val TYPE_VARIABLE_NAME_PREFIX_FOR_CR_PARAMETER_TYPE = "_QP"
private const val TYPE_VARIABLE_NAME_FOR_CR_RETURN_TYPE = "_Q"
}
}
@@ -18,10 +18,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext
import org.jetbrains.kotlin.types.model.freshTypeConstructor
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.utils.SmartSet
class TypeVariableDependencyInformationProvider(
@@ -30,10 +27,19 @@ class TypeVariableDependencyInformationProvider(
private val topLevelType: KotlinTypeMarker?,
private val typeSystemContext: TypeSystemInferenceExtensionContext
) {
// not oriented edges
private val constrainEdges: MutableMap<TypeConstructorMarker, MutableSet<TypeConstructorMarker>> = hashMapOf()
/*
* Not oriented edges
* TypeVariable(A) has UPPER(Function1<TypeVariable(B), R>) => A and B are related deeply
*/
private val deepTypeVariableDependencies: MutableMap<TypeConstructorMarker, MutableSet<TypeConstructorMarker>> = hashMapOf()
// oriented edges
/*
* Not oriented edges
* TypeVariable(A) has UPPER(TypeVariable(B)) => A and B are related shallowly
*/
private val shallowTypeVariableDependencies: MutableMap<TypeConstructorMarker, MutableSet<TypeConstructorMarker>> = hashMapOf()
// Oriented edges
private val postponeArgumentsEdges: MutableMap<TypeConstructorMarker, MutableSet<TypeConstructorMarker>> = hashMapOf()
private val relatedToAllOutputTypes: MutableSet<TypeConstructorMarker> = hashSetOf()
@@ -49,21 +55,43 @@ class TypeVariableDependencyInformationProvider(
fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) = relatedToTopLevelType.contains(variable)
fun isVariableRelatedToAnyOutputType(variable: TypeConstructorMarker) = relatedToAllOutputTypes.contains(variable)
fun getDeeplyDependentVariables(variable: TypeConstructorMarker) = deepTypeVariableDependencies[variable]
fun getShallowlyDependentVariables(variable: TypeConstructorMarker) = shallowTypeVariableDependencies[variable]
fun areVariablesDependentShallowly(a: TypeConstructorMarker, b: TypeConstructorMarker): Boolean {
if (a == b) return true
val shallowDependencies = shallowTypeVariableDependencies[a] ?: return false
return shallowDependencies.any { it == b } ||
shallowTypeVariableDependencies.values.any { dependencies -> a in dependencies && b in dependencies }
}
private fun computeConstraintEdges() {
fun addConstraintEdge(from: TypeConstructorMarker, to: TypeConstructorMarker) {
constrainEdges.getOrPut(from) { hashSetOf() }.add(to)
constrainEdges.getOrPut(to) { hashSetOf() }.add(from)
fun addConstraintEdgeForDeepDependency(from: TypeConstructorMarker, to: TypeConstructorMarker) {
deepTypeVariableDependencies.getOrPut(from) { linkedSetOf() }.add(to)
deepTypeVariableDependencies.getOrPut(to) { linkedSetOf() }.add(from)
}
fun addConstraintEdgeForShallowDependency(from: TypeConstructorMarker, to: TypeConstructorMarker) {
shallowTypeVariableDependencies.getOrPut(from) { linkedSetOf() }.add(to)
shallowTypeVariableDependencies.getOrPut(to) { linkedSetOf() }.add(from)
}
for (variableWithConstraints in notFixedTypeVariables.values) {
val from = variableWithConstraints.typeVariable.freshTypeConstructor(typeSystemContext)
for (constraint in variableWithConstraints.constraints) {
val constraintTypeConstructor = constraint.type.typeConstructor(typeSystemContext)
constraint.type.forAllMyTypeVariables {
if (isMyTypeVariable(it)) {
addConstraintEdge(from, it)
addConstraintEdgeForDeepDependency(from, it)
}
}
if (isMyTypeVariable(constraintTypeConstructor)) {
addConstraintEdgeForShallowDependency(from, constraintTypeConstructor)
}
}
}
}
@@ -118,7 +146,7 @@ class TypeVariableDependencyInformationProvider(
}
private fun getConstraintEdges(from: TypeConstructorMarker): Set<TypeConstructorMarker> = constrainEdges[from] ?: emptySet()
private fun getConstraintEdges(from: TypeConstructorMarker): Set<TypeConstructorMarker> = deepTypeVariableDependencies[from] ?: emptySet()
private fun getPostponeEdges(from: TypeConstructorMarker): Set<TypeConstructorMarker> = postponeArgumentsEdges[from] ?: emptySet()
private fun addAllRelatedNodes(to: MutableSet<TypeConstructorMarker>, node: TypeConstructorMarker, includePostponedEdges: Boolean) {
@@ -61,6 +61,12 @@ enum class ConstraintKind {
fun isLower(): Boolean = this == LOWER
fun isUpper(): Boolean = this == UPPER
fun isEqual(): Boolean = this == EQUALITY
fun opposite() = when (this) {
LOWER -> UPPER
UPPER -> LOWER
EQUALITY -> EQUALITY
}
}
class Constraint(
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer
import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector
import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter
import org.jetbrains.kotlin.resolve.calls.inference.components.PostponedArgumentInputTypesResolver
import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.OnlyInputTypesDiagnostic
@@ -31,6 +32,7 @@ class NewConstraintSystemImpl(
ConstraintInjector.Context,
ResultTypeResolver.Context,
KotlinConstraintSystemCompleter.Context,
PostponedArgumentInputTypesResolver.Context,
PostponedArgumentsAnalyzer.Context {
private val storage = MutableConstraintStorage()
private var state = State.BUILDING
@@ -87,6 +89,8 @@ class NewConstraintSystemImpl(
override fun asPostponedArgumentsAnalyzerContext() = apply { checkState(State.BUILDING) }
override fun asPostponedArgumentInputTypesResolverContext() = apply { checkState(State.BUILDING) }
// ConstraintSystemOperation
override fun registerVariable(variable: TypeVariableMarker) {
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.resolve.calls.model.CallableReferenceKotlinCallArgument
import org.jetbrains.kotlin.resolve.calls.model.LambdaKotlinCallArgument
import org.jetbrains.kotlin.resolve.calls.model.PostponableKotlinCallArgument
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOnlyInputTypesAnnotation
import org.jetbrains.kotlin.types.KotlinType
@@ -79,7 +80,15 @@ class TypeVariableFromCallableDescriptor(
}
class TypeVariableForLambdaReturnType(
val lambdaArgument: LambdaKotlinCallArgument,
builtIns: KotlinBuiltIns,
name: String
) : NewTypeVariable(builtIns, name) {
override fun hasOnlyInputTypesAnnotation(): Boolean = false
}
class TypeVariableForLambdaParameterType(
val atom: PostponableKotlinCallArgument,
val index: Int,
builtIns: KotlinBuiltIns,
name: String
) : NewTypeVariable(builtIns, name) {
@@ -92,3 +101,10 @@ class TypeVariableForCallableReferenceReturnType(
) : NewTypeVariable(builtIns, name) {
override fun hasOnlyInputTypesAnnotation(): Boolean = false
}
class TypeVariableForCallableReferenceParameterType(
builtIns: KotlinBuiltIns,
name: String
) : NewTypeVariable(builtIns, name) {
override fun hasOnlyInputTypesAnnotation(): Boolean = false
}
@@ -13,10 +13,7 @@ import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMa
import org.jetbrains.kotlin.resolve.calls.components.extractInputOutputTypesFromCallableReferenceExpectedType
import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintError
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForCallableReferenceReturnType
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForLambdaReturnType
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant
import org.jetbrains.kotlin.types.KotlinType
@@ -98,6 +95,12 @@ interface PostponedResolvedAtomMarker {
val analyzed: Boolean
}
interface PostponedAtomWithRevisableExpectedType {
var revisedExpectedType: UnwrappedType?
val expectedType: UnwrappedType?
val atom: PostponableKotlinCallArgument
}
sealed class PostponedResolvedAtom : ResolvedAtom(), PostponedResolvedAtomMarker {
abstract override val inputTypes: Collection<UnwrappedType>
abstract override val outputType: UnwrappedType?
@@ -107,10 +110,13 @@ sealed class PostponedResolvedAtom : ResolvedAtom(), PostponedResolvedAtomMarker
class LambdaWithTypeVariableAsExpectedTypeAtom(
override val atom: LambdaKotlinCallArgument,
override val expectedType: UnwrappedType
) : PostponedResolvedAtom() {
) : PostponedResolvedAtom(), PostponedAtomWithRevisableExpectedType {
override val inputTypes: Collection<UnwrappedType> get() = listOf(expectedType)
override val outputType: UnwrappedType? get() = null
var isReturnArgumentOfAnotherLambda: Boolean = false
override var revisedExpectedType: UnwrappedType? = null
var parameterTypesFromDeclaration: List<UnwrappedType?>? = null
fun setAnalyzed(resolvedLambdaAtom: ResolvedLambdaAtom) {
setAnalyzedResults(listOf(resolvedLambdaAtom))
@@ -180,15 +186,16 @@ sealed class AbstractPostponedCallableReferenceAtom(
get() = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType)?.outputType
}
class CallableReferenceWithTypeVariableAsExpectedTypeAtom(
class CallableReferenceWithRevisedExpectedTypeAtom(
atom: CallableReferenceKotlinCallArgument,
expectedType: UnwrappedType?,
val typeVariableForReturnType: TypeVariableForCallableReferenceReturnType?
) : AbstractPostponedCallableReferenceAtom(atom, expectedType)
class PostponedCallableReferenceAtom(
eagerCallableReferenceAtom: EagerCallableReferenceAtom
) : AbstractPostponedCallableReferenceAtom(eagerCallableReferenceAtom.atom, eagerCallableReferenceAtom.expectedType)
) : AbstractPostponedCallableReferenceAtom(eagerCallableReferenceAtom.atom, eagerCallableReferenceAtom.expectedType), PostponedAtomWithRevisableExpectedType {
override var revisedExpectedType: UnwrappedType? = null
}
class ResolvedCollectionLiteralAtom(
override val atom: CollectionLiteralKotlinCallArgument,