[NI] Add feature for choosing candidate by lambda return type

This commit is contained in:
Dmitriy Novozhilov
2020-04-22 13:15:49 +03:00
parent f243b8946f
commit 865ddac07a
19 changed files with 429 additions and 23 deletions
@@ -18,15 +18,14 @@ package org.jetbrains.kotlin.resolve.calls
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceResolver
import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
import org.jetbrains.kotlin.resolve.calls.components.NewOverloadingConflictResolver
import org.jetbrains.kotlin.resolve.calls.components.*
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tower.*
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.model.safeSubstitute
import org.jetbrains.kotlin.utils.addToStdlib.same
import java.lang.UnsupportedOperationException
@@ -142,13 +141,19 @@ class KotlinCallResolver(
}
}
val maximallySpecificCandidates = overloadingConflictResolver.chooseMaximallySpecificCandidates(
var maximallySpecificCandidates = overloadingConflictResolver.chooseMaximallySpecificCandidates(
refinedCandidates,
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
discriminateGenerics = true // todo
)
if (maximallySpecificCandidates.size > 1 && callComponents.languageVersionSettings.supportsFeature(LanguageFeature.FactoryPatternResolution)) {
maximallySpecificCandidates = kotlinCallCompleter.chooseCandidateRegardingFactoryPatternResolution(maximallySpecificCandidates, resolutionCallbacks)
}
return kotlinCallCompleter.runCompletion(candidateFactory, maximallySpecificCandidates, expectedType, resolutionCallbacks)
}
}
@@ -59,6 +59,7 @@ data class ReturnArgumentsInfo(
data class ReturnArgumentsAnalysisResult(
val returnArgumentsInfo: ReturnArgumentsInfo,
val inferenceSession: InferenceSession?,
val lambdaReturnType: KotlinType? = null,
val hasInapplicableCallForBuilderInference: Boolean = false
)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor
@@ -15,11 +16,15 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintS
import org.jetbrains.kotlin.resolve.calls.inference.components.TrivialConstraintTypeInferenceOracle
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.Empty.hasContradiction
import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaArgumentConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tower.forceResolution
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.model.safeSubstitute
import org.jetbrains.kotlin.utils.addToStdlib.same
class KotlinCallCompleter(
private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer,
@@ -66,10 +71,89 @@ class KotlinCallCompleter(
candidate.runCompletion(completionMode, diagnosticHolder, resolutionCallbacks)
candidate.asCallResolutionResult(completionMode, diagnosticHolder)
}
ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException("Should not be here")
}
}
fun chooseCandidateRegardingFactoryPatternResolution(
candidates: Set<KotlinResolutionCandidate>,
resolutionCallbacks: KotlinResolutionCallbacks
): Set<KotlinResolutionCandidate> {
val lambdas = candidates.flatMap { candidate ->
candidate.getSubResolvedAtoms()
.filter { it is ResolvedLambdaAtom && !it.analyzed }
.map { candidate to it as ResolvedLambdaAtom }
}.groupBy { (_, atom) -> atom.atom }
.values
.singleOrNull()
?.toMap() ?: return candidates
if (!lambdas.values.same { it.parameters.size }) return candidates
if (!lambdas.values.all { it.expectedType?.isFunctionTypeOrSubtype == true }) return candidates
for (candidate in lambdas.keys) {
candidate.runCompletion(
ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA,
candidate,
resolutionCallbacks
)
}
if (!lambdas.entries.same { (candidate, atom) -> candidate.getInputTypesOfLambdaAtom(atom) }) {
return candidates
}
val newAtoms = mutableMapOf<KotlinResolutionCandidate, ResolvedLambdaAtom>()
for ((candidate, atom) in lambdas.entries) {
newAtoms[candidate] = kotlinConstraintSystemCompleter.prepareLambdaAtomForFactoryPattern(atom, candidate, candidate)
}
val diagnosticHolderForLambda = KotlinDiagnosticsHolder.SimpleHolder()
val iterator = newAtoms.entries.iterator()
val (firstCandidate, firstAtom) = iterator.next()
val results = postponedArgumentsAnalyzer.analyzeLambda(
firstCandidate.getSystem().asPostponedArgumentsAnalyzerContext(),
resolutionCallbacks,
firstAtom,
diagnosticHolderForLambda
)
lambdas.getValue(firstCandidate).setAnalyzedResults(results.returnArgumentsInfo, listOf(firstAtom))
val lambdaReturnType = results.lambdaReturnType ?: return candidates
while (iterator.hasNext()) {
val (candidate, atom) = iterator.next()
atom.setAnalyzedResults(results.returnArgumentsInfo, firstAtom.subResolvedAtoms!!)
lambdas.getValue(candidate).setAnalyzedResults(results.returnArgumentsInfo, listOf(atom))
candidate.csBuilder.addSubtypeConstraint(lambdaReturnType, atom.returnType, LambdaArgumentConstraintPosition(atom))
}
val errorCandidates = mutableSetOf<KotlinResolutionCandidate>()
val successfulCandidates = mutableSetOf<KotlinResolutionCandidate>()
for (candidate in candidates) {
if (candidate.isSuccessful) {
successfulCandidates += candidate
} else {
errorCandidates += candidate
}
}
return when {
successfulCandidates.isNotEmpty() -> successfulCandidates
else -> errorCandidates
}
}
private fun KotlinResolutionCandidate.getInputTypesOfLambdaAtom(atom: ResolvedLambdaAtom): List<UnwrappedType> {
val result = mutableListOf<UnwrappedType>()
val substitutor = csBuilder.buildCurrentSubstitutor()
val ctx = getSystem().asConstraintSystemCompleterContext()
for (inputType in atom.inputTypes) {
result += substitutor.safeSubstitute(ctx, inputType) as UnwrappedType
}
return result
}
private fun KotlinResolutionCandidate.checkSamWithVararg(diagnosticHolder: KotlinDiagnosticsHolder.SimpleHolder) {
val samConversionPerArgumentWithWarningsForVarargAfterSam =
callComponents.languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument) &&
@@ -220,6 +220,24 @@ fun LambdaWithTypeVariableAsExpectedTypeAtom.transformToResolvedLambda(
return resolvedLambdaAtom
}
fun ResolvedLambdaAtom.transformToResolvedLambda(
csBuilder: ConstraintSystemBuilder,
diagnosticsHolder: KotlinDiagnosticsHolder,
expectedType: UnwrappedType,
returnTypeVariable: TypeVariableForLambdaReturnType? = null
): ResolvedLambdaAtom {
val resolvedLambdaAtom = preprocessLambdaArgument(
csBuilder,
atom,
expectedType,
diagnosticsHolder,
forceResolution = true,
returnTypeVariable = returnTypeVariable
) as ResolvedLambdaAtom
return resolvedLambdaAtom
}
private fun preprocessCallableReference(
csBuilder: ConstraintSystemBuilder,
argument: CallableReferenceKotlinCallArgument,
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.CoroutinePosition
import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaArgumentConstraintPosition
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.types.typeUtil.builtIns
@@ -65,12 +66,12 @@ class PostponedArgumentsAnalyzer(
}
}
private fun analyzeLambda(
fun analyzeLambda(
c: Context,
resolutionCallbacks: KotlinResolutionCallbacks,
lambda: ResolvedLambdaAtom,
diagnosticHolder: KotlinDiagnosticsHolder
) {
): ReturnArgumentsAnalysisResult {
val stubsForPostponedVariables = c.bindingStubsForPostponedVariables()
val currentSubstitutor = c.buildCurrentSubstitutor(stubsForPostponedVariables.mapKeys { it.key.freshTypeConstructor(c) })
@@ -120,20 +121,21 @@ class PostponedArgumentsAnalyzer(
else FilteredAnnotations(annotations, true) { it != KotlinBuiltIns.FQ_NAMES.extensionFunctionType }
}
val (returnArgumentsInfo, inferenceSession, hasInapplicableCallForBuilderInference) =
resolutionCallbacks.analyzeAndGetLambdaReturnArguments(
lambda.atom,
lambda.isSuspend,
receiver,
parameters,
expectedTypeForReturnArguments,
convertedAnnotations ?: Annotations.EMPTY,
stubsForPostponedVariables.cast()
)
val returnArgumentsAnalysisResult = resolutionCallbacks.analyzeAndGetLambdaReturnArguments(
lambda.atom,
lambda.isSuspend,
receiver,
parameters,
expectedTypeForReturnArguments,
convertedAnnotations ?: Annotations.EMPTY,
stubsForPostponedVariables.cast()
)
val (returnArgumentsInfo, inferenceSession, inferedReturnType, hasInapplicableCallForBuilderInference) =
returnArgumentsAnalysisResult
if (hasInapplicableCallForBuilderInference) {
c.getBuilder().removePostponedVariables()
return
return returnArgumentsAnalysisResult
}
val returnArguments = returnArgumentsInfo.nonErrorArguments
@@ -169,7 +171,7 @@ class PostponedArgumentsAnalyzer(
val postponedVariables = inferenceSession.inferPostponedVariables(lambda, storageSnapshot, diagnosticHolder)
if (postponedVariables == null) {
c.getBuilder().removePostponedVariables()
return
return returnArgumentsAnalysisResult
}
for ((constructor, resultType) in postponedVariables) {
@@ -180,6 +182,8 @@ class PostponedArgumentsAnalyzer(
c.getBuilder().addEqualityConstraint(variable.defaultType(c), resultType, CoroutinePosition())
}
}
return returnArgumentsAnalysisResult
}
private fun UnwrappedType?.receiver(): UnwrappedType? {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.*
@@ -12,6 +13,8 @@ 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.model.safeSubstitute
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import kotlin.collections.LinkedHashSet
@@ -24,7 +27,8 @@ class KotlinConstraintSystemCompleter(
enum class ConstraintSystemCompletionMode {
FULL,
PARTIAL
PARTIAL,
UNTIL_FIRST_LAMBDA
}
interface Context : VariableFixationFinder.Context, ResultTypeResolver.Context {
@@ -91,7 +95,9 @@ class KotlinConstraintSystemCompleter(
analyze: (PostponedResolvedAtom) -> Unit
) {
completion@ while (true) {
// TODO
val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)
if (completionMode == ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA && hasLambdaToAnalyze(postponedArguments)) return
// Stage 1: analyze postponed arguments with fixed parameter types
if (analyzeArgumentWithFixedParameterTypes(postponedArguments, analyze))
@@ -210,6 +216,35 @@ class KotlinConstraintSystemCompleter(
return false
}
fun prepareLambdaAtomForFactoryPattern(
atom: ResolvedLambdaAtom,
candidate: KotlinResolutionCandidate,
diagnosticsHolder: KotlinDiagnosticsHolder,
): ResolvedLambdaAtom {
val returnVariable = TypeVariableForLambdaReturnType(candidate.callComponents.builtIns, "_R")
val csBuilder = candidate.csBuilder
csBuilder.registerVariable(returnVariable)
val functionalType: KotlinType = csBuilder.buildCurrentSubstitutor().safeSubstitute(candidate.getSystem().asConstraintSystemCompleterContext(), atom.expectedType!!) as KotlinType
val expectedType = KotlinTypeFactory.simpleType(
functionalType.annotations,
functionalType.constructor,
functionalType.arguments.dropLast(1) + returnVariable.defaultType.asTypeProjection(),
functionalType.isMarkedNullable
)
csBuilder.addSubtypeConstraint(
expectedType,
functionalType,
ArgumentConstraintPosition(atom.atom)
)
return atom.transformToResolvedLambda(csBuilder, diagnosticsHolder, expectedType, returnVariable)
}
private fun Context.hasLambdaToAnalyze(
postponedArguments: List<PostponedResolvedAtom>
): Boolean {
return analyzeArgumentWithFixedParameterTypes(postponedArguments) {}
}
private fun findPostponedArgumentWithRevisableExpectedType(postponedArguments: List<PostponedResolvedAtom>) =
postponedArguments.firstOrNull { argument -> argument is PostponedAtomWithRevisableExpectedType }