diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt index 29cecf0a325..576c587717f 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt @@ -156,10 +156,9 @@ object CodegenUtil { } @JvmStatic - fun constructFakeFunctionCall(project: Project, referencedFunction: FunctionDescriptor): KtCallExpression { - val fakeFunctionCall = StringBuilder("callableReferenceFakeCall(") - fakeFunctionCall.append(referencedFunction.valueParameters.joinToString(", ") { "p${it.index}" }) - fakeFunctionCall.append(")") - return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall.toString()) as KtCallExpression + fun constructFakeFunctionCall(project: Project, arity: Int): KtCallExpression { + val fakeFunctionCall = + (1..arity).joinToString(prefix = "callableReferenceFakeCall(", separator = ", ", postfix = ")") { "p$it" } + return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall) as KtCallExpression } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java index 1d939c862ab..5d701011cd8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java @@ -24,10 +24,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; -import org.jetbrains.kotlin.resolve.calls.model.DelegatingResolvedCall; -import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument; -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; -import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument; +import org.jetbrains.kotlin.resolve.calls.model.*; import org.jetbrains.kotlin.resolve.calls.util.CallMaker; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; @@ -40,8 +37,24 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject; - +/* + * Notice the difference between two function descriptors in this class. + * - [referencedFunction] is the function declaration which is referenced by the "::" expression. This is a real function present in code. + * - [functionDescriptor] is a synthetically created function which has the same signature as the "invoke" of the generated callable + * reference subclass. Its parameters include dispatch/extension receiver parameters of the referenced function, and those value + * parameters of the referenced function which are required by the expected function type where the callable reference is passed to. + * In simple cases, these value parameters are all of the referenced function's value parameters. But in cases when the referenced + * function has parameters with default values, or a vararg parameter, functionDescriptor can take fewer parameters than + * referencedFunction if the expected function type takes fewer parameters as well. For example: + * + * fun foo(a: A, b: B = ..., c: C = ..., vararg d: D) {} + * + * fun bar(f: (A, B) -> Unit) {} + * + * // referencedFunction: foo(A, B, C, vararg D) + * // functionDescriptor: invoke(A, B) + * bar(::foo) + */ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased { private final ResolvedCall resolvedCall; private final FunctionDescriptor referencedFunction; @@ -80,20 +93,21 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat every argument boils down to calling LOAD with the corresponding index */ - KtCallExpression fakeExpression = CodegenUtil.constructFakeFunctionCall(state.getProject(), referencedFunction); + int receivers = CallableReferenceUtilKt.computeExpectedNumberOfReceivers(referencedFunction, receiverType != null); + KtCallExpression fakeExpression = + CodegenUtil.constructFakeFunctionCall(state.getProject(), functionDescriptor.getValueParameters().size() - receivers); List fakeArguments = fakeExpression.getValueArguments(); ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter()); ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter()); - computeAndSaveArguments(fakeArguments, codegen); + computeAndSaveArguments(fakeArguments, codegen, receivers); ResolvedCall fakeResolvedCall = new DelegatingResolvedCall(resolvedCall) { - private final Map argumentMap; + private final Map argumentMap = new LinkedHashMap<>(); { - argumentMap = new LinkedHashMap<>(fakeArguments.size()); int index = 0; - List parameters = functionDescriptor.getValueParameters(); + List parameters = referencedFunction.getValueParameters(); for (ValueArgument argument : fakeArguments) { argumentMap.put(parameters.get(index), new ExpressionValueArgument(argument)); index++; @@ -146,20 +160,14 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat v.areturn(returnType); } - private void computeAndSaveArguments(@NotNull List fakeArguments, @NotNull ExpressionCodegen codegen) { - int receivers = (referencedFunction.getDispatchReceiverParameter() != null ? 1 : 0) + - (referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0) - - (receiverType != null ? 1 : 0); - - if (receivers < 0 && referencedFunction instanceof ConstructorDescriptor && isObject(referencedFunction.getContainingDeclaration().getContainingDeclaration())) { - //reference to object nested class - //TODO: seems problem should be fixed on frontend side (note that object instance are captured by generated class) - receivers = 0; - } - - List parameters = CollectionsKt.drop(functionDescriptor.getValueParameters(), receivers); - for (int i = 0; i < parameters.size(); i++) { - ValueParameterDescriptor parameter = parameters.get(i); + private void computeAndSaveArguments( + @NotNull List fakeArguments, @NotNull ExpressionCodegen codegen, int receivers + ) { + List valueParameters = CollectionsKt.drop(functionDescriptor.getValueParameters(), receivers); + assert valueParameters.size() == fakeArguments.size() + : functionDescriptor + ": " + valueParameters.size() + " != " + fakeArguments.size(); + for (int i = 0; i < valueParameters.size(); i++) { + ValueParameterDescriptor parameter = valueParameters.get(i); ValueArgument fakeArgument = fakeArguments.get(i); Type type = state.getTypeMapper().mapType(parameter); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt index f6490482dc7..6aa3d2cfe66 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView import org.jetbrains.kotlin.coroutines.isSuspendLambda import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.descriptors.impl.MutableClassDescriptor import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor @@ -29,7 +30,6 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils class JvmRuntimeTypes(module: ModuleDescriptor) { private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal")) @@ -77,7 +77,7 @@ class JvmRuntimeTypes(module: ModuleDescriptor) { descriptor.builtIns, Annotations.EMPTY, actualFunctionDescriptor.extensionReceiverParameter?.type, - ExpressionTypingUtils.getValueParametersTypes(actualFunctionDescriptor.valueParameters), + actualFunctionDescriptor.valueParameters.map { it.type }, null, actualFunctionDescriptor.returnType!! ) @@ -94,14 +94,20 @@ class JvmRuntimeTypes(module: ModuleDescriptor) { return listOf(lambda.defaultType, functionType) } - fun getSupertypesForFunctionReference(descriptor: FunctionDescriptor, isBound: Boolean): Collection { + fun getSupertypesForFunctionReference( + referencedFunction: FunctionDescriptor, + anonymousFunctionDescriptor: AnonymousFunctionDescriptor, + isBound: Boolean + ): Collection { + val receivers = computeExpectedNumberOfReceivers(referencedFunction, isBound) + val functionType = createFunctionType( - descriptor.builtIns, + referencedFunction.builtIns, Annotations.EMPTY, - if (isBound) null else descriptor.extensionReceiverParameter?.type ?: descriptor.dispatchReceiverParameter?.type, - ExpressionTypingUtils.getValueParametersTypes(descriptor.valueParameters), + if (isBound) null else referencedFunction.extensionReceiverParameter?.type ?: referencedFunction.dispatchReceiverParameter?.type, + anonymousFunctionDescriptor.valueParameters.drop(receivers).map { it.type }, null, - descriptor.returnType!! + referencedFunction.returnType!! ) return listOf(functionReference.defaultType, functionType) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java index 9560d4884fc..d8c91a060b3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping; import org.jetbrains.kotlin.coroutines.CoroutineUtilKt; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.Annotations; +import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.fileClasses.FileClasses; import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider; @@ -341,7 +342,9 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { callableDescriptor = bindingContext.get(FUNCTION, expression); if (callableDescriptor == null) return; - supertypes = runtimeTypes.getSupertypesForFunctionReference((FunctionDescriptor) target, receiverType != null); + supertypes = runtimeTypes.getSupertypesForFunctionReference( + (FunctionDescriptor) target, (AnonymousFunctionDescriptor) callableDescriptor, receiverType != null + ); } else if (target instanceof PropertyDescriptor) { callableDescriptor = bindingContext.get(VARIABLE, expression); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt index c3d7c682464..6797297b257 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt @@ -18,7 +18,10 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.codegen.binding.CalculatedClosure import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.psi.KtCallableReferenceExpression +import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.org.objectweb.asm.Type @@ -75,4 +78,19 @@ fun InstructionAdapter.generateClosureFieldsInitializationFromParameters(closure else -> null } -} \ No newline at end of file +} + +fun computeExpectedNumberOfReceivers(referencedFunction: FunctionDescriptor, isBound: Boolean): Int { + val receivers = (if (referencedFunction.dispatchReceiverParameter != null) 1 else 0) + + (if (referencedFunction.extensionReceiverParameter != null) 1 else 0) - + (if (isBound) 1 else 0) + + if (receivers < 0 && referencedFunction is ConstructorDescriptor && + DescriptorUtils.isObject(referencedFunction.containingDeclaration.containingDeclaration)) { + //reference to object nested class + //TODO: seems problem should be fixed on frontend side (note that object instance are captured by generated class) + return 0 + } + + return receivers +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingUtils.java index df5824ff2c0..da82921787c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingUtils.java @@ -44,9 +44,6 @@ import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt; -import java.util.ArrayList; -import java.util.List; - import static org.jetbrains.kotlin.diagnostics.Errors.TYPE_INFERENCE_ERRORS; import static org.jetbrains.kotlin.resolve.BindingContext.PROCESSED; @@ -185,15 +182,6 @@ public class ExpressionTypingUtils { && ((KtUnaryExpression) expression).getOperationReference().getReferencedNameElementType() == KtTokens.EXCLEXCL; } - @NotNull - public static List getValueParametersTypes(@NotNull List valueParameters) { - List parameterTypes = new ArrayList<>(valueParameters.size()); - for (ValueParameterDescriptor parameter : valueParameters) { - parameterTypes.add(parameter.getType()); - } - return parameterTypes; - } - /** * The primary case for local extensions is the following: * diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt index 711733f41e4..258a6d0f371 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt @@ -82,7 +82,7 @@ object CallableReferenceTranslator { receiver: JsExpression? ): JsExpression { val realResolvedCall = expression.callableReference.getFunctionResolvedCallWithAssert(context.bindingContext()) - val fakeExpression = CodegenUtil.constructFakeFunctionCall(expression.project, descriptor) + val fakeExpression = CodegenUtil.constructFakeFunctionCall(expression.project, descriptor.valueParameters.size) val fakeCall = CallMaker.makeCall(fakeExpression, null, null, fakeExpression, fakeExpression.valueArguments) val fakeResolvedCall = object : DelegatingResolvedCall(realResolvedCall) {