From 455c43afa885c7726cd90bbb36486befa9af7ffe Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Fri, 16 Jun 2017 13:20:14 +0300 Subject: [PATCH] [NI] Some improvements in callable reference resolution --- .../components/CallableReferenceResolution.kt | 76 ++++++++++++------- .../CheckArgumentsResolutionPart.kt | 15 ++-- .../components/FixationOrderCalculator.kt | 11 +-- .../model/NewConstraintSystemImpl.kt | 8 +- .../calls/model/ResolvedCallElements.kt | 11 +-- 5 files changed, 76 insertions(+), 45 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt index 5eda45772a7..2ef9822aee2 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.descriptors.* 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.components.FreshVariableNewTypeSubstitutor 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.upperIfFlexible import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.addIfNotNull sealed class CallableReceiver(val receiver: ReceiverValueWithSmartCastInfo) { 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 { + 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( val argument: CallableReferenceKotlinCallArgument, val outerCallContext: KotlinCallContext, @@ -142,40 +183,23 @@ class CallableReferencesCandidateFactory( val diagnostics = SmartList() 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 compatibilityChecker { - if (it.hasContradiction || expectedType == null) return@compatibilityChecker + if (it.hasContradiction) return@compatibilityChecker - val toFreshSubstitutor = CreateDescriptorWithFreshTypeVariables.createToFreshVariableSubstitutorAndAddInitialConstraints(candidateDescriptor, it, kotlinCall = null) - val reflectionType = toFreshSubstitutor.safeSubstitute(reflectionCandidateType) - it.addSubtypeConstraint(reflectionType, expectedType, position) + val (_, visibilityError) = it.checkCallableReference(argument, dispatchCallableReceiver, extensionCallableReceiver, candidateDescriptor, + reflectionCandidateType, expectedType, outerCallContext.scopeTower.lexicalScope.ownerDescriptor) - it.addUnboundConstraint(toFreshSubstitutor, dispatchCallableReceiver, candidateDescriptor.dispatchReceiverParameter) - it.addUnboundConstraint(toFreshSubstitutor, extensionCallableReceiver, candidateDescriptor.extensionReceiverParameter) + diagnostics.addIfNotNull(visibilityError) - 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, 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( descriptor: FunctionDescriptor, expectedType: UnwrappedType?, @@ -235,12 +259,10 @@ class CallableReferencesCandidateFactory( val argumentsAndReceivers = ArrayList(descriptor.valueParameters.size + 2) if (dispatchReceiver is CallableReceiver.UnboundReference) { - argumentsAndReceivers.add(descriptor.dispatchReceiverParameter?.type - ?: error("Dispatch receiver should be not null for descriptor: $descriptor")) + argumentsAndReceivers.add(dispatchReceiver.receiver.stableType) } if (extensionReceiver is CallableReceiver.UnboundReference) { - argumentsAndReceivers.add(descriptor.extensionReceiverParameter?.type - ?: error("Extension receiver should be not null for descriptor: $descriptor")) + argumentsAndReceivers.add(extensionReceiver.receiver.stableType) } val descriptorReturnType = descriptor.returnType @@ -305,7 +327,7 @@ class CallableReferencesCandidateFactory( class CallableReferenceNotCompatible( val argument: CallableReferenceKotlinCallArgument, val candidate: CallableMemberDescriptor, - val expectedType: UnwrappedType, + val expectedType: UnwrappedType?, val callableReverenceType: UnwrappedType ) : KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) { override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt index 35a8777d82b..ca0d43e69a5 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt @@ -20,7 +20,6 @@ import org.jetbrains.kotlin.builtins.* import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor 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.addSubtypeConstraintIfCompatible import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition @@ -173,16 +172,18 @@ internal object CheckArguments : ResolutionPart { 1 -> candidates.single() 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) - return null + return diagnostic } fun processCollectionLiteralArgument( diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt index bfb75336232..ec35ddc0d7b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt @@ -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.ConstraintKind 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.types.* import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker @@ -42,7 +43,7 @@ class FixationOrderCalculator { interface Context { val notFixedTypeVariables: Map - val lambdaArguments: List + val lambdaAndCallableReferenceArguments: List } fun computeCompletionOrder( @@ -87,11 +88,11 @@ class FixationOrderCalculator { } private fun buildLambdaEdges() { - for (lambdaArgument in c.lambdaArguments) { - if (lambdaArgument.analyzed) continue + for (lambdaArgument in c.lambdaAndCallableReferenceArguments) { + if (lambdaArgument is ResolvedLambdaArgument && lambdaArgument.analyzed) continue // optimization val typeVariablesInReturnType = SmartList() - lambdaArgument.returnType.findTypeVariables(typeVariablesInReturnType) + lambdaArgument.outputType?.findTypeVariables(typeVariablesInReturnType) if (typeVariablesInReturnType.isEmpty()) continue // optimization @@ -131,7 +132,7 @@ class FixationOrderCalculator { topReturnType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction -> enterToNode(variableWithConstraints, direction) } - for (resolvedLambdaArgument in c.lambdaArguments) { + for (resolvedLambdaArgument in c.lambdaAndCallableReferenceArguments) { inner@ for (inputType in resolvedLambdaArgument.inputTypes) { inputType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction -> enterToNode(variableWithConstraints, direction) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index a8714464ad0..18a5d33d748 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -249,12 +249,18 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re storage.errors.add(error) } - // FixationOrderCalculator.Context, KotlinCallCompleter.Context + // KotlinCallCompleter.Context override val lambdaArguments: List get() { checkState(State.COMPLETION) return storage.lambdaArguments } + // FixationOrderCalculator.Context + override val lambdaAndCallableReferenceArguments: List get() { + checkState(State.COMPLETION) + return storage.lambdaArguments + storage.callableReferenceArguments + } + override val callableReferenceArguments: List get() { checkState(State.COMPLETION) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt index b012d0b07a3..def4858150e 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt @@ -30,8 +30,6 @@ sealed class ArgumentWithPostponeResolution { abstract val argument: KotlinCallArgument abstract val inputTypes: Collection // parameters and implicit receiver abstract val outputType: UnwrappedType? - - var analyzed: Boolean = false } class ResolvedLambdaArgument( @@ -42,6 +40,8 @@ class ResolvedLambdaArgument( val parameters: List, val returnType: UnwrappedType ) : ArgumentWithPostponeResolution() { + var analyzed: Boolean = false + val type: SimpleType = createFunctionType(returnType.builtIns, Annotations.EMPTY, receiver, parameters, null, returnType, isSuspend) // todo support annotations override val inputTypes: Collection get() = receiver?.let { parameters + it } ?: parameters @@ -55,10 +55,11 @@ class ResolvedCallableReferenceArgument( override val outerCall: KotlinCall, override val argument: CallableReferenceKotlinCallArgument, val myTypeVariables: List, - val callableResolutionCandidate: CallableReferenceCandidate + val callableResolutionCandidate: CallableReferenceCandidate, + val reflectionType: UnwrappedType // via myTypeVariables variables ) : ArgumentWithPostponeResolution() { - override val inputTypes: Collection get() = emptyList() - override val outputType: UnwrappedType? = null + override val inputTypes: Collection get() = reflectionType.arguments.dropLast(1).map { it.type.unwrap() } + override val outputType: UnwrappedType? = reflectionType.arguments.last().type.unwrap() } class ResolvedCollectionLiteralArgument(