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:
+1
-1
@@ -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 =
|
||||
|
||||
+1
-4
@@ -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) {
|
||||
|
||||
+2
@@ -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
|
||||
}
|
||||
+263
-289
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+473
@@ -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"
|
||||
}
|
||||
}
|
||||
+40
-12
@@ -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) {
|
||||
|
||||
+6
@@ -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(
|
||||
|
||||
+4
@@ -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)
|
||||
|
||||
+17
-1
@@ -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
|
||||
}
|
||||
|
||||
+16
-9
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user