[NI] Add feature for choosing candidate by lambda return type
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -59,6 +59,7 @@ data class ReturnArgumentsInfo(
|
||||
data class ReturnArgumentsAnalysisResult(
|
||||
val returnArgumentsInfo: ReturnArgumentsInfo,
|
||||
val inferenceSession: InferenceSession?,
|
||||
val lambdaReturnType: KotlinType? = null,
|
||||
val hasInapplicableCallForBuilderInference: Boolean = false
|
||||
)
|
||||
|
||||
|
||||
+84
@@ -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) &&
|
||||
|
||||
+18
@@ -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,
|
||||
|
||||
+18
-14
@@ -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? {
|
||||
|
||||
+36
-1
@@ -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 }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user