diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt index eb41fb3ba44..2758f4c48ff 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt @@ -181,6 +181,18 @@ class FakeValueArgumentForLeftCallableReference(val ktExpression: KtCallableRefe override fun isExternal(): Boolean = false } +class FakePositionalValueArgumentForCallableReferenceImpl( + private val callElement: KtElement, + override val index: Int +) : FakePositionalValueArgumentForCallableReference { + override fun getArgumentExpression(): KtExpression? = null + override fun getArgumentName(): ValueArgumentName? = null + override fun isNamed(): Boolean = false + override fun asElement(): KtElement = callElement + override fun getSpreadElement(): LeafPsiElement? = null + override fun isExternal(): Boolean = false +} + class EmptyLabeledReturn( val returnExpression: KtReturnExpression, builtIns: KotlinBuiltIns diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt index 3793a1c40fc..106472e1bc4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.MissingSupertypesResolver @@ -20,6 +21,8 @@ import org.jetbrains.kotlin.resolve.TemporaryBindingTrace import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext +import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceAdaptation +import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.ContextDependency import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor @@ -216,7 +219,7 @@ class ResolvedAtomCompleter( } val functionDescriptor = trace.bindingContext.get(BindingContext.FUNCTION, ktFunction) as? FunctionDescriptorImpl - ?: throw AssertionError("No function descriptor for resolved lambda argument") + ?: throw AssertionError("No function descriptor for resolved lambda argument") functionDescriptor.setReturnType(returnType) val existingLambdaType = trace.getType(ktArgumentExpression) @@ -277,7 +280,10 @@ class ResolvedAtomCompleter( val resultTypeParameters = callableCandidate.freshSubstitutor!!.freshVariables.map { resultSubstitutor.safeSubstitute(it.defaultType) } - val typeParametersSubstitutor = NewTypeSubstitutorByConstructorMap((callableCandidate.candidate.typeParameters.map { it.typeConstructor } zip resultTypeParameters).toMap()) + val typeParametersSubstitutor = + NewTypeSubstitutorByConstructorMap( + (callableCandidate.candidate.typeParameters.map { it.typeConstructor } zip resultTypeParameters).toMap() + ) val firstSubstitution = typeParametersSubstitutor.toOldSubstitution() val secondSubstitution = resultSubstitutor.toOldSubstitution() @@ -317,6 +323,8 @@ class ResolvedAtomCompleter( ) resolvedCall.setResultingSubstitutor(resultSubstitutor) + recordArgumentAdaptationForCallableReference(resolvedCall, callableCandidate.callableReferenceAdaptation) + tracing.bindCall(topLevelTrace, psiCall) tracing.bindReference(topLevelTrace, resolvedCall) tracing.bindResolvedCall(topLevelTrace, resolvedCall) @@ -352,6 +360,47 @@ class ResolvedAtomCompleter( kotlinToResolvedCallTransformer.runCallCheckers(resolvedCall, topLevelCallCheckerContext) } + private fun recordArgumentAdaptationForCallableReference( + resolvedCall: ResolvedCallImpl, + callableReferenceAdaptation: CallableReferenceAdaptation? + ) { + if (callableReferenceAdaptation == null) return + + val callElement = resolvedCall.call.callElement + + for ((valueParameter, resolvedCallArgument) in callableReferenceAdaptation.mappedArguments) { + resolvedCall.recordValueArgument( + valueParameter, + when (resolvedCallArgument) { + ResolvedCallArgument.DefaultArgument -> + DefaultValueArgument.DEFAULT + is ResolvedCallArgument.SimpleArgument -> { + val valueArgument = makeFakeValueArgument(resolvedCallArgument.callArgument, callElement) + if (valueParameter.isVararg) + VarargValueArgument(listOf(valueArgument)) + else + ExpressionValueArgument(valueArgument) + } + is ResolvedCallArgument.VarargArgument -> + VarargValueArgument( + resolvedCallArgument.arguments.map { + makeFakeValueArgument(it, callElement) + } + ) + } + ) + } + } + + private fun makeFakeValueArgument( + callArgument: KotlinCallArgument, + callElement: KtElement + ): ValueArgument { + val fakeCallArgument = callArgument as? FakeKotlinCallArgumentForCallableReference + ?: throw AssertionError("FakeKotlinCallArgumentForCallableReference expected: $callArgument") + return FakePositionalValueArgumentForCallableReferenceImpl(callElement, fakeCallArgument.index) + } + private fun completeCollectionLiteralCalls(collectionLiteralArgument: ResolvedCollectionLiteralAtom) { val psiCallArgument = collectionLiteralArgument.atom.psiCallArgument as CollectionLiteralKotlinCallArgumentImpl val context = psiCallArgument.outerCallContext diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/ValueArgument.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/ValueArgument.kt index e55b8bc7dd7..555b092a6f9 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/ValueArgument.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/ValueArgument.kt @@ -36,6 +36,10 @@ interface ValueArgument { fun isExternal(): Boolean } +interface FakePositionalValueArgumentForCallableReference : ValueArgument { + val index: Int +} + interface LambdaArgument : ValueArgument { fun getLambdaExpression(): KtLambdaExpression? } 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 c05651791b6..3d6f1581e17 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 @@ -59,7 +59,7 @@ class CallableReferenceCandidate( val extensionReceiver: CallableReceiver?, val explicitReceiverKind: ExplicitReceiverKind, val reflectionCandidateType: UnwrappedType, - val numDefaults: Int, + val callableReferenceAdaptation: CallableReferenceAdaptation?, val diagnostics: List ) : Candidate { override val resultingApplicability = getResultApplicability(diagnostics) @@ -67,8 +67,17 @@ class CallableReferenceCandidate( var freshSubstitutor: FreshVariableNewTypeSubstitutor? = null internal set + + val numDefaults get() = callableReferenceAdaptation?.defaults ?: 0 } +class CallableReferenceAdaptation( + val argumentTypes: Array, + val coercionStrategy: CoercionStrategy, + val defaults: Int, + val mappedArguments: Map +) + /** * cases: class A {}, class B { companion object }, object C, enum class D { E } * A::foo <-> Type @@ -179,7 +188,7 @@ class CallableReferencesCandidateFactory( val candidateDescriptor = towerCandidate.descriptor val diagnostics = SmartList() - val (reflectionCandidateType, defaults) = buildReflectionType( + val (reflectionCandidateType, callableReferenceAdaptation) = buildReflectionType( candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver, @@ -187,16 +196,17 @@ class CallableReferencesCandidateFactory( callComponents.builtIns ) - if (defaults != 0 && + if (callableReferenceAdaptation != null && + callableReferenceAdaptation.defaults != 0 && !callComponents.languageVersionSettings.supportsFeature(LanguageFeature.FunctionReferenceWithDefaultValueAsOtherType) ) { - diagnostics.add(CallableReferencesDefaultArgumentUsed(argument, candidateDescriptor, defaults)) + diagnostics.add(CallableReferencesDefaultArgumentUsed(argument, candidateDescriptor, callableReferenceAdaptation.defaults)) } if (candidateDescriptor !is CallableMemberDescriptor) { return CallableReferenceCandidate( candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver, - explicitReceiverKind, reflectionCandidateType, defaults, + explicitReceiverKind, reflectionCandidateType, callableReferenceAdaptation, listOf(NotCallableMemberReference(argument, candidateDescriptor)) ) } @@ -226,7 +236,7 @@ class CallableReferencesCandidateFactory( return CallableReferenceCandidate( candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver, - explicitReceiverKind, reflectionCandidateType, defaults, diagnostics + explicitReceiverKind, reflectionCandidateType, callableReferenceAdaptation, diagnostics ) } @@ -234,18 +244,18 @@ class CallableReferencesCandidateFactory( UNMAPPED, MAPPED_WITH_PLAIN_ARGS, MAPPED_WITH_ARRAY } - private fun getArgumentAndReturnTypeUseMappingByExpectedType( + private fun getCallableReferenceAdaptation( descriptor: FunctionDescriptor, expectedType: UnwrappedType?, unboundReceiverCount: Int, builtins: KotlinBuiltIns - ): Triple, CoercionStrategy, Int>? { + ): CallableReferenceAdaptation? { val inputOutputTypes = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType) ?: return null val expectedArgumentCount = inputOutputTypes.inputTypes.size - unboundReceiverCount if (expectedArgumentCount < 0) return null - val fakeArguments = (0..(expectedArgumentCount - 1)).map { FakeKotlinCallArgumentForCallableReference(it) } + val fakeArguments = (0 until expectedArgumentCount).map { FakeKotlinCallArgumentForCallableReference(it) } val argumentMapping = callComponents.argumentsToParametersMapper.mapArguments(fakeArguments, externalArgument = null, descriptor = descriptor) if (argumentMapping.diagnostics.any { !it.candidateApplicability.isSuccess }) return null @@ -288,8 +298,11 @@ class CallableReferencesCandidateFactory( val coercion = if (returnExpectedType.isUnit()) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION - @Suppress("UNCHECKED_CAST") - return Triple(mappedArguments as Array, coercion, defaults) + + return CallableReferenceAdaptation( + @Suppress("UNCHECKED_CAST") (mappedArguments as Array), + coercion, defaults, argumentMapping.parameterToCallArgumentMap + ) } private fun varargParameterTypeByExpectedParameter( @@ -328,7 +341,7 @@ class CallableReferencesCandidateFactory( extensionReceiver: CallableReceiver?, expectedType: UnwrappedType?, builtins: KotlinBuiltIns - ): Pair { + ): Pair { val argumentsAndReceivers = ArrayList(descriptor.valueParameters.size + 2) if (dispatchReceiver is CallableReceiver.UnboundReference) { @@ -356,35 +369,35 @@ class CallableReferencesCandidateFactory( argumentsAndReceivers, descriptorReturnType, mutable - ) to 0 + ) to null } is FunctionDescriptor -> { - val returnType: KotlinType - val defaults: Int - val argumentsAndExpectedTypeCoercion = getArgumentAndReturnTypeUseMappingByExpectedType( + val callableReferenceAdaptation = getCallableReferenceAdaptation( descriptor, expectedType, unboundReceiverCount = argumentsAndReceivers.size, builtins = builtins ) - if (argumentsAndExpectedTypeCoercion == null) { + val returnType = if (callableReferenceAdaptation == null) { descriptor.valueParameters.mapTo(argumentsAndReceivers) { it.type } - returnType = descriptorReturnType - defaults = 0 + descriptorReturnType } else { - val (arguments, coercion) = argumentsAndExpectedTypeCoercion - defaults = argumentsAndExpectedTypeCoercion.third + val arguments = callableReferenceAdaptation.argumentTypes + val coercion = callableReferenceAdaptation.coercionStrategy argumentsAndReceivers.addAll(arguments) - returnType = if (coercion == CoercionStrategy.COERCION_TO_UNIT) descriptor.builtIns.unitType else descriptorReturnType + if (coercion == CoercionStrategy.COERCION_TO_UNIT) + descriptor.builtIns.unitType + else + descriptorReturnType } return callComponents.reflectionTypes.getKFunctionType( Annotations.EMPTY, null, argumentsAndReceivers, null, returnType, descriptor.builtIns, descriptor.isSuspend - ) to defaults + ) to callableReferenceAdaptation } - else -> return ErrorUtils.createErrorType("Unsupported descriptor type: $descriptor") to 0 + else -> return ErrorUtils.createErrorType("Unsupported descriptor type: $descriptor") to null } }