[NI] Some improvements in callable reference resolution
This commit is contained in:
committed by
Mikhail Zarechenskiy
parent
f1144c9f01
commit
455c43afa8
+49
-27
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
|
|||||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.components.CreateDescriptorWithFreshTypeVariables.createToFreshVariableSubstitutorAndAddInitialConstraints
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
|
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor
|
import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition
|
import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition
|
||||||
@@ -42,6 +43,7 @@ import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
|
|||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
import org.jetbrains.kotlin.types.upperIfFlexible
|
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||||
import org.jetbrains.kotlin.utils.SmartList
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
|
||||||
sealed class CallableReceiver(val receiver: ReceiverValueWithSmartCastInfo) {
|
sealed class CallableReceiver(val receiver: ReceiverValueWithSmartCastInfo) {
|
||||||
class UnboundReference(val qualifier: QualifierReceiver, receiver: ReceiverValueWithSmartCastInfo) : CallableReceiver(receiver)
|
class UnboundReference(val qualifier: QualifierReceiver, receiver: ReceiverValueWithSmartCastInfo) : CallableReceiver(receiver)
|
||||||
@@ -108,6 +110,45 @@ fun createCallableReferenceProcessor(factory: CallableReferencesCandidateFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ConstraintSystemOperation.checkCallableReference(
|
||||||
|
argument: CallableReferenceKotlinCallArgument,
|
||||||
|
dispatchReceiver: CallableReceiver?,
|
||||||
|
extensionReceiver: CallableReceiver?,
|
||||||
|
candidateDescriptor: CallableDescriptor,
|
||||||
|
reflectionCandidateType: UnwrappedType,
|
||||||
|
expectedType: UnwrappedType?,
|
||||||
|
ownerDescriptor: DeclarationDescriptor
|
||||||
|
): Pair<FreshVariableNewTypeSubstitutor, KotlinCallDiagnostic?> {
|
||||||
|
val invisibleMember = Visibilities.findInvisibleMember(dispatchReceiver?.asReceiverValueForVisibilityChecks,
|
||||||
|
candidateDescriptor, ownerDescriptor)
|
||||||
|
|
||||||
|
val position = ArgumentConstraintPosition(argument)
|
||||||
|
|
||||||
|
val toFreshSubstitutor = createToFreshVariableSubstitutorAndAddInitialConstraints(candidateDescriptor, this, kotlinCall = null)
|
||||||
|
val reflectionType = toFreshSubstitutor.safeSubstitute(reflectionCandidateType)
|
||||||
|
|
||||||
|
if (expectedType != null) {
|
||||||
|
addSubtypeConstraint(reflectionType, expectedType, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
addReceiverConstraint(toFreshSubstitutor, dispatchReceiver, candidateDescriptor.dispatchReceiverParameter, position)
|
||||||
|
addReceiverConstraint(toFreshSubstitutor, extensionReceiver, candidateDescriptor.extensionReceiverParameter, position)
|
||||||
|
|
||||||
|
return toFreshSubstitutor to invisibleMember?.let(::VisibilityError)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun ConstraintSystemOperation.addReceiverConstraint(
|
||||||
|
toFreshSubstitutor: FreshVariableNewTypeSubstitutor,
|
||||||
|
receiver: CallableReceiver?,
|
||||||
|
candidateReceiver: ReceiverParameterDescriptor?,
|
||||||
|
position: ArgumentConstraintPosition
|
||||||
|
) {
|
||||||
|
val expectedType = toFreshSubstitutor.safeSubstitute(candidateReceiver?.value?.type?.unwrap() ?: return)
|
||||||
|
val receiverType = receiver?.receiver?.stableType ?: return
|
||||||
|
addSubtypeConstraint(receiverType, expectedType, position)
|
||||||
|
}
|
||||||
|
|
||||||
class CallableReferencesCandidateFactory(
|
class CallableReferencesCandidateFactory(
|
||||||
val argument: CallableReferenceKotlinCallArgument,
|
val argument: CallableReferenceKotlinCallArgument,
|
||||||
val outerCallContext: KotlinCallContext,
|
val outerCallContext: KotlinCallContext,
|
||||||
@@ -142,40 +183,23 @@ class CallableReferencesCandidateFactory(
|
|||||||
|
|
||||||
val diagnostics = SmartList<KotlinCallDiagnostic>()
|
val diagnostics = SmartList<KotlinCallDiagnostic>()
|
||||||
diagnostics.addAll(towerCandidate.diagnostics)
|
diagnostics.addAll(towerCandidate.diagnostics)
|
||||||
val invisibleMember = Visibilities.findInvisibleMember(dispatchCallableReceiver?.asReceiverValueForVisibilityChecks,
|
|
||||||
candidateDescriptor, outerCallContext.scopeTower.lexicalScope.ownerDescriptor)
|
|
||||||
if (invisibleMember != null) {
|
|
||||||
diagnostics.add(VisibilityError(invisibleMember))
|
|
||||||
}
|
|
||||||
// todo smartcast on receiver diagnostic and CheckInstantiationOfAbstractClass
|
// todo smartcast on receiver diagnostic and CheckInstantiationOfAbstractClass
|
||||||
|
|
||||||
compatibilityChecker {
|
compatibilityChecker {
|
||||||
if (it.hasContradiction || expectedType == null) return@compatibilityChecker
|
if (it.hasContradiction) return@compatibilityChecker
|
||||||
|
|
||||||
val toFreshSubstitutor = CreateDescriptorWithFreshTypeVariables.createToFreshVariableSubstitutorAndAddInitialConstraints(candidateDescriptor, it, kotlinCall = null)
|
val (_, visibilityError) = it.checkCallableReference(argument, dispatchCallableReceiver, extensionCallableReceiver, candidateDescriptor,
|
||||||
val reflectionType = toFreshSubstitutor.safeSubstitute(reflectionCandidateType)
|
reflectionCandidateType, expectedType, outerCallContext.scopeTower.lexicalScope.ownerDescriptor)
|
||||||
it.addSubtypeConstraint(reflectionType, expectedType, position)
|
|
||||||
|
|
||||||
it.addUnboundConstraint(toFreshSubstitutor, dispatchCallableReceiver, candidateDescriptor.dispatchReceiverParameter)
|
diagnostics.addIfNotNull(visibilityError)
|
||||||
it.addUnboundConstraint(toFreshSubstitutor, extensionCallableReceiver, candidateDescriptor.extensionReceiverParameter)
|
|
||||||
|
|
||||||
if (it.hasContradiction) diagnostics.add(CallableReferenceNotCompatible(argument, candidateDescriptor, expectedType, reflectionType))
|
if (it.hasContradiction) diagnostics.add(CallableReferenceNotCompatible(argument, candidateDescriptor, expectedType, reflectionCandidateType))
|
||||||
}
|
}
|
||||||
|
|
||||||
return CallableReferenceCandidate(candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver,
|
return CallableReferenceCandidate(candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver,
|
||||||
explicitReceiverKind, reflectionCandidateType, defaults, ResolutionCandidateStatus(diagnostics))
|
explicitReceiverKind, reflectionCandidateType, defaults, ResolutionCandidateStatus(diagnostics))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ConstraintSystemOperation.addUnboundConstraint(
|
|
||||||
toFreshSubstitutor: FreshVariableNewTypeSubstitutor,
|
|
||||||
receiver: CallableReceiver?,
|
|
||||||
candidateReceiver: ReceiverParameterDescriptor?
|
|
||||||
) {
|
|
||||||
val expectedType = toFreshSubstitutor.safeSubstitute(candidateReceiver?.value?.type?.unwrap() ?: return)
|
|
||||||
val receiverType = receiver?.receiver?.stableType ?: return
|
|
||||||
addSubtypeConstraint(receiverType, expectedType, position)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getArgumentAndReturnTypeUseMappingByExpectedType(
|
private fun getArgumentAndReturnTypeUseMappingByExpectedType(
|
||||||
descriptor: FunctionDescriptor,
|
descriptor: FunctionDescriptor,
|
||||||
expectedType: UnwrappedType?,
|
expectedType: UnwrappedType?,
|
||||||
@@ -235,12 +259,10 @@ class CallableReferencesCandidateFactory(
|
|||||||
val argumentsAndReceivers = ArrayList<KotlinType>(descriptor.valueParameters.size + 2)
|
val argumentsAndReceivers = ArrayList<KotlinType>(descriptor.valueParameters.size + 2)
|
||||||
|
|
||||||
if (dispatchReceiver is CallableReceiver.UnboundReference) {
|
if (dispatchReceiver is CallableReceiver.UnboundReference) {
|
||||||
argumentsAndReceivers.add(descriptor.dispatchReceiverParameter?.type
|
argumentsAndReceivers.add(dispatchReceiver.receiver.stableType)
|
||||||
?: error("Dispatch receiver should be not null for descriptor: $descriptor"))
|
|
||||||
}
|
}
|
||||||
if (extensionReceiver is CallableReceiver.UnboundReference) {
|
if (extensionReceiver is CallableReceiver.UnboundReference) {
|
||||||
argumentsAndReceivers.add(descriptor.extensionReceiverParameter?.type
|
argumentsAndReceivers.add(extensionReceiver.receiver.stableType)
|
||||||
?: error("Extension receiver should be not null for descriptor: $descriptor"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val descriptorReturnType = descriptor.returnType
|
val descriptorReturnType = descriptor.returnType
|
||||||
@@ -305,7 +327,7 @@ class CallableReferencesCandidateFactory(
|
|||||||
class CallableReferenceNotCompatible(
|
class CallableReferenceNotCompatible(
|
||||||
val argument: CallableReferenceKotlinCallArgument,
|
val argument: CallableReferenceKotlinCallArgument,
|
||||||
val candidate: CallableMemberDescriptor,
|
val candidate: CallableMemberDescriptor,
|
||||||
val expectedType: UnwrappedType,
|
val expectedType: UnwrappedType?,
|
||||||
val callableReverenceType: UnwrappedType
|
val callableReverenceType: UnwrappedType
|
||||||
) : KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) {
|
) : KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) {
|
||||||
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
|
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
|
||||||
|
|||||||
+8
-7
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.builtins.*
|
|||||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.calls.components.CreateDescriptorWithFreshTypeVariables.createToFreshVariableSubstitutorAndAddInitialConstraints
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
|
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition
|
import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition
|
||||||
@@ -173,16 +172,18 @@ internal object CheckArguments : ResolutionPart {
|
|||||||
1 -> candidates.single()
|
1 -> candidates.single()
|
||||||
else -> return CallableReferenceCandidatesAmbiguity(argument, candidates)
|
else -> return CallableReferenceCandidatesAmbiguity(argument, candidates)
|
||||||
}
|
}
|
||||||
|
val (toFreshSubstitutor, diagnostic) = with(chosenCandidate) {
|
||||||
|
csBuilder.checkCallableReference(argument, dispatchReceiver, extensionReceiver, candidate,
|
||||||
|
reflectionCandidateType, expectedType, callContext.scopeTower.lexicalScope.ownerDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
val resolvedCallableReference = ResolvedCallableReferenceArgument(
|
||||||
|
kotlinCall, argument, toFreshSubstitutor.freshVariables,
|
||||||
|
chosenCandidate, toFreshSubstitutor.safeSubstitute(chosenCandidate.reflectionCandidateType))
|
||||||
|
|
||||||
val toFreshSubstitutor = createToFreshVariableSubstitutorAndAddInitialConstraints(chosenCandidate.candidate, csBuilder, kotlinCall = null)
|
|
||||||
val reflectionType = toFreshSubstitutor.safeSubstitute(chosenCandidate.reflectionCandidateType)
|
|
||||||
csBuilder.addSubtypeConstraint(reflectionType, expectedType, ArgumentConstraintPosition(argument))
|
|
||||||
|
|
||||||
val resolvedCallableReference = ResolvedCallableReferenceArgument(kotlinCall, argument, toFreshSubstitutor.freshVariables, chosenCandidate)
|
|
||||||
csBuilder.addCallableReferenceArgument(resolvedCallableReference)
|
csBuilder.addCallableReferenceArgument(resolvedCallableReference)
|
||||||
|
|
||||||
return null
|
return diagnostic
|
||||||
}
|
}
|
||||||
|
|
||||||
fun processCollectionLiteralArgument(
|
fun processCollectionLiteralArgument(
|
||||||
|
|||||||
+6
-5
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
|
|||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
|
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
|
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.ArgumentWithPostponeResolution
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker
|
||||||
@@ -42,7 +43,7 @@ class FixationOrderCalculator {
|
|||||||
|
|
||||||
interface Context {
|
interface Context {
|
||||||
val notFixedTypeVariables: Map<TypeConstructor, VariableWithConstraints>
|
val notFixedTypeVariables: Map<TypeConstructor, VariableWithConstraints>
|
||||||
val lambdaArguments: List<ResolvedLambdaArgument>
|
val lambdaAndCallableReferenceArguments: List<ArgumentWithPostponeResolution>
|
||||||
}
|
}
|
||||||
|
|
||||||
fun computeCompletionOrder(
|
fun computeCompletionOrder(
|
||||||
@@ -87,11 +88,11 @@ class FixationOrderCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun buildLambdaEdges() {
|
private fun buildLambdaEdges() {
|
||||||
for (lambdaArgument in c.lambdaArguments) {
|
for (lambdaArgument in c.lambdaAndCallableReferenceArguments) {
|
||||||
if (lambdaArgument.analyzed) continue
|
if (lambdaArgument is ResolvedLambdaArgument && lambdaArgument.analyzed) continue // optimization
|
||||||
|
|
||||||
val typeVariablesInReturnType = SmartList<Variable>()
|
val typeVariablesInReturnType = SmartList<Variable>()
|
||||||
lambdaArgument.returnType.findTypeVariables(typeVariablesInReturnType)
|
lambdaArgument.outputType?.findTypeVariables(typeVariablesInReturnType)
|
||||||
|
|
||||||
if (typeVariablesInReturnType.isEmpty()) continue // optimization
|
if (typeVariablesInReturnType.isEmpty()) continue // optimization
|
||||||
|
|
||||||
@@ -131,7 +132,7 @@ class FixationOrderCalculator {
|
|||||||
topReturnType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction ->
|
topReturnType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction ->
|
||||||
enterToNode(variableWithConstraints, direction)
|
enterToNode(variableWithConstraints, direction)
|
||||||
}
|
}
|
||||||
for (resolvedLambdaArgument in c.lambdaArguments) {
|
for (resolvedLambdaArgument in c.lambdaAndCallableReferenceArguments) {
|
||||||
inner@ for (inputType in resolvedLambdaArgument.inputTypes) {
|
inner@ for (inputType in resolvedLambdaArgument.inputTypes) {
|
||||||
inputType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction ->
|
inputType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction ->
|
||||||
enterToNode(variableWithConstraints, direction)
|
enterToNode(variableWithConstraints, direction)
|
||||||
|
|||||||
+7
-1
@@ -249,12 +249,18 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
|
|||||||
storage.errors.add(error)
|
storage.errors.add(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FixationOrderCalculator.Context, KotlinCallCompleter.Context
|
// KotlinCallCompleter.Context
|
||||||
override val lambdaArguments: List<ResolvedLambdaArgument> get() {
|
override val lambdaArguments: List<ResolvedLambdaArgument> get() {
|
||||||
checkState(State.COMPLETION)
|
checkState(State.COMPLETION)
|
||||||
return storage.lambdaArguments
|
return storage.lambdaArguments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FixationOrderCalculator.Context
|
||||||
|
override val lambdaAndCallableReferenceArguments: List<ArgumentWithPostponeResolution> get() {
|
||||||
|
checkState(State.COMPLETION)
|
||||||
|
return storage.lambdaArguments + storage.callableReferenceArguments
|
||||||
|
}
|
||||||
|
|
||||||
override val callableReferenceArguments: List<ResolvedCallableReferenceArgument>
|
override val callableReferenceArguments: List<ResolvedCallableReferenceArgument>
|
||||||
get() {
|
get() {
|
||||||
checkState(State.COMPLETION)
|
checkState(State.COMPLETION)
|
||||||
|
|||||||
+6
-5
@@ -30,8 +30,6 @@ sealed class ArgumentWithPostponeResolution {
|
|||||||
abstract val argument: KotlinCallArgument
|
abstract val argument: KotlinCallArgument
|
||||||
abstract val inputTypes: Collection<UnwrappedType> // parameters and implicit receiver
|
abstract val inputTypes: Collection<UnwrappedType> // parameters and implicit receiver
|
||||||
abstract val outputType: UnwrappedType?
|
abstract val outputType: UnwrappedType?
|
||||||
|
|
||||||
var analyzed: Boolean = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResolvedLambdaArgument(
|
class ResolvedLambdaArgument(
|
||||||
@@ -42,6 +40,8 @@ class ResolvedLambdaArgument(
|
|||||||
val parameters: List<UnwrappedType>,
|
val parameters: List<UnwrappedType>,
|
||||||
val returnType: UnwrappedType
|
val returnType: UnwrappedType
|
||||||
) : ArgumentWithPostponeResolution() {
|
) : ArgumentWithPostponeResolution() {
|
||||||
|
var analyzed: Boolean = false
|
||||||
|
|
||||||
val type: SimpleType = createFunctionType(returnType.builtIns, Annotations.EMPTY, receiver, parameters, null, returnType, isSuspend) // todo support annotations
|
val type: SimpleType = createFunctionType(returnType.builtIns, Annotations.EMPTY, receiver, parameters, null, returnType, isSuspend) // todo support annotations
|
||||||
|
|
||||||
override val inputTypes: Collection<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
|
override val inputTypes: Collection<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
|
||||||
@@ -55,10 +55,11 @@ class ResolvedCallableReferenceArgument(
|
|||||||
override val outerCall: KotlinCall,
|
override val outerCall: KotlinCall,
|
||||||
override val argument: CallableReferenceKotlinCallArgument,
|
override val argument: CallableReferenceKotlinCallArgument,
|
||||||
val myTypeVariables: List<NewTypeVariable>,
|
val myTypeVariables: List<NewTypeVariable>,
|
||||||
val callableResolutionCandidate: CallableReferenceCandidate
|
val callableResolutionCandidate: CallableReferenceCandidate,
|
||||||
|
val reflectionType: UnwrappedType // via myTypeVariables variables
|
||||||
) : ArgumentWithPostponeResolution() {
|
) : ArgumentWithPostponeResolution() {
|
||||||
override val inputTypes: Collection<UnwrappedType> get() = emptyList()
|
override val inputTypes: Collection<UnwrappedType> get() = reflectionType.arguments.dropLast(1).map { it.type.unwrap() }
|
||||||
override val outputType: UnwrappedType? = null
|
override val outputType: UnwrappedType? = reflectionType.arguments.last().type.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResolvedCollectionLiteralArgument(
|
class ResolvedCollectionLiteralArgument(
|
||||||
|
|||||||
Reference in New Issue
Block a user