From f4f5359725c9c3d0d92b8ce09f23c582a50aa4ed Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 22 Nov 2017 17:40:06 +0100 Subject: [PATCH] Fix exception on inlining callable reference with implicit this in LHS Use ResolvedCall to determine the receiver type in the JVM codegen, instead of manually inspecting the PSI #KT-20821 Fixed --- .../kotlin/codegen/ExpressionCodegen.java | 10 ++----- .../kotlin/codegen/JvmCodegenUtil.java | 17 +++++++++++ .../kotlin/codegen/inline/InlineCodegen.kt | 29 +++++++------------ .../codegen/inline/SourceCompilerForInline.kt | 21 ++++---------- .../bound/emptyLhsFunction.kt | 15 ++++++++++ .../bound/emptyLhsProperty.kt | 14 +++++++++ .../IrBlackBoxInlineCodegenTestGenerated.java | 12 ++++++++ ...otlinAgainstInlineKotlinTestGenerated.java | 12 ++++++++ .../BlackBoxInlineCodegenTestGenerated.java | 12 ++++++++ ...otlinAgainstInlineKotlinTestGenerated.java | 12 ++++++++ ...CallableReferenceInlineTestsGenerated.java | 12 ++++++++ 11 files changed, 124 insertions(+), 42 deletions(-) create mode 100644 compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsFunction.kt create mode 100644 compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsProperty.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index ba621a69cb7..691828d8607 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2839,14 +2839,8 @@ public class ExpressionCodegen extends KtVisitor impleme @Nullable public StackValue generateCallableReferenceReceiver(@NotNull ResolvedCall resolvedCall) { - CallableDescriptor descriptor = resolvedCall.getResultingDescriptor(); - if (descriptor.getExtensionReceiverParameter() == null && descriptor.getDispatchReceiverParameter() == null) return null; - - ReceiverValue dispatchReceiver = resolvedCall.getDispatchReceiver(); - ReceiverValue extensionReceiver = resolvedCall.getExtensionReceiver(); - assert dispatchReceiver == null || extensionReceiver == null : "Cannot generate reference with both receivers: " + descriptor; - ReceiverValue receiver = dispatchReceiver != null ? dispatchReceiver : extensionReceiver; - if (receiver == null || receiver instanceof TransientReceiver) return null; + ReceiverValue receiver = getBoundCallableReferenceReceiver(resolvedCall); + if (receiver == null) return null; return StackValue.coercion(generateReceiverValue(receiver, false), asmType(receiver.getType())); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java index 5c3a7e2cc50..7dc69551c8f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java @@ -41,8 +41,10 @@ import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils; +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; +import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor; import org.jetbrains.kotlin.types.KotlinType; @@ -313,4 +315,19 @@ public class JvmCodegenUtil { public static boolean isDelegatedLocalVariable(@NotNull DeclarationDescriptor descriptor) { return descriptor instanceof LocalVariableDescriptor && ((LocalVariableDescriptor) descriptor).isDelegated(); } + + @Nullable + public static ReceiverValue getBoundCallableReferenceReceiver(@NotNull ResolvedCall resolvedCall) { + CallableDescriptor descriptor = resolvedCall.getResultingDescriptor(); + if (descriptor.getExtensionReceiverParameter() == null && descriptor.getDispatchReceiverParameter() == null) return null; + + ReceiverValue dispatchReceiver = resolvedCall.getDispatchReceiver(); + ReceiverValue extensionReceiver = resolvedCall.getExtensionReceiver(); + assert dispatchReceiver == null || extensionReceiver == null : "Cannot generate reference with both receivers: " + descriptor; + ReceiverValue receiver = dispatchReceiver != null ? dispatchReceiver : extensionReceiver; + + if (receiver instanceof TransientReceiver) return null; + + return receiver; + } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index 5ae4c6d8d8d..ce5164e97f0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -36,10 +36,10 @@ import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtPsiUtil import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.inline.InlineUtil.isInlinableParameterExpression @@ -48,8 +48,8 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor -import org.jetbrains.kotlin.types.expressions.DoubleColonLHS import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral import org.jetbrains.kotlin.types.expressions.LabelResolver import org.jetbrains.org.objectweb.asm.Opcodes @@ -704,19 +704,10 @@ class PsiInlineCodegen( activeLambda = null } - - private fun getBoundCallableReferenceReceiver( - argumentExpression: KtExpression - ): KtExpression? { - val deparenthesized = KtPsiUtil.deparenthesize(argumentExpression) - if (deparenthesized is KtCallableReferenceExpression) { - val receiverExpression = deparenthesized.receiverExpression - if (receiverExpression != null) { - val lhs = state.bindingContext.get(BindingContext.DOUBLE_COLON_LHS, receiverExpression) - if (lhs is DoubleColonLHS.Expression) return receiverExpression - } - } - return null + private fun getBoundCallableReferenceReceiver(argumentExpression: KtExpression): ReceiverValue? { + val deparenthesized = KtPsiUtil.deparenthesize(argumentExpression) as? KtCallableReferenceExpression ?: return null + val resolvedCall = deparenthesized.callableReference.getResolvedCallWithAssert(state.bindingContext) + return JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall) } /*lambda or callable reference*/ @@ -736,10 +727,10 @@ class PsiInlineCodegen( if (isInliningParameter(argumentExpression, valueParameterDescriptor)) { val lambdaInfo = rememberClosure(argumentExpression, parameterType, valueParameterDescriptor) - val receiver = getBoundCallableReferenceReceiver(argumentExpression) - if (receiver != null) { - val receiverValue = codegen.gen(receiver) - putClosureParametersOnStack(lambdaInfo, StackValue.coercion(receiverValue, receiverValue.type.boxReceiverForBoundReference())) + val receiverValue = getBoundCallableReferenceReceiver(argumentExpression) + if (receiverValue != null) { + val receiver = codegen.generateReceiverValue(receiverValue, false) + putClosureParametersOnStack(lambdaInfo, StackValue.coercion(receiver, receiver.type.boxReceiverForBoundReference())) } } else { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt index a9d716858de..60fb51f1868 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt @@ -38,7 +38,7 @@ import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode import org.jetbrains.org.objectweb.asm.tree.LabelNode import org.jetbrains.org.objectweb.asm.tree.MethodNode -import java.util.HashMap +import java.util.* import kotlin.properties.Delegates interface SourceCompilerForInline { @@ -174,28 +174,19 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid val strategy = when (expression) { is KtCallableReferenceExpression -> { - val receiverExpression = expression.receiverExpression - val receiverType = if (receiverExpression != null && state.bindingContext.getType(receiverExpression) != null) - state.typeMapper.mapType(state.bindingContext.getType(receiverExpression)!!) - else - null + val resolvedCall = expression.callableReference.getResolvedCallWithAssert(state.bindingContext) + val receiverType = JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)?.type?.let(state.typeMapper::mapType) if (isLambda && lambdaInfo!!.isPropertyReference) { val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor) val info = lambdaInfo.propertyReferenceInfo PropertyReferenceCodegen.PropertyReferenceGenerationStrategy( true, info!!.getFunction, info.target, asmType, receiverType, - lambdaInfo.functionWithBodyOrCallableReference, state, true) + lambdaInfo.functionWithBodyOrCallableReference, state, true + ) } else { - FunctionReferenceGenerationStrategy( - state, - descriptor, - expression.callableReference - .getResolvedCallWithAssert(state.bindingContext), - receiverType, null, - true - ) + FunctionReferenceGenerationStrategy(state, descriptor, resolvedCall, receiverType, null, true) } } is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody) diff --git a/compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsFunction.kt b/compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsFunction.kt new file mode 100644 index 00000000000..906fa54a9c3 --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsFunction.kt @@ -0,0 +1,15 @@ +class X { + fun x(): String { + return foo("O", "K", ::y) + } + + fun y(a: String, b: String): String = a + b +} + +inline fun foo(a: String, b: String, f: (String, String) -> String): String { + return f(a, b) +} + +fun box(): String { + return X().x() +} diff --git a/compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsProperty.kt b/compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsProperty.kt new file mode 100644 index 00000000000..f745c2d4e81 --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsProperty.kt @@ -0,0 +1,14 @@ +class X { + val result: String + inline get() = "OK" + + fun x(): String { + return go(::result) + } +} + +inline fun go(f: () -> String): String = f() + +fun box(): String { + return X().x() +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java index 05440b8c0ab..f535c27a3c8 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java @@ -745,6 +745,18 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli doTest(fileName); } + @TestMetadata("emptyLhsFunction.kt") + public void testEmptyLhsFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsFunction.kt"); + doTest(fileName); + } + + @TestMetadata("emptyLhsProperty.kt") + public void testEmptyLhsProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsProperty.kt"); + doTest(fileName); + } + @TestMetadata("expression.kt") public void testExpression() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt"); diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java index c33d3bd47d4..530e1014b22 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java @@ -745,6 +745,18 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC doTest(fileName); } + @TestMetadata("emptyLhsFunction.kt") + public void testEmptyLhsFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsFunction.kt"); + doTest(fileName); + } + + @TestMetadata("emptyLhsProperty.kt") + public void testEmptyLhsProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsProperty.kt"); + doTest(fileName); + } + @TestMetadata("expression.kt") public void testExpression() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java index 3fae7c2fe85..ec36d52fa04 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java @@ -745,6 +745,18 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo doTest(fileName); } + @TestMetadata("emptyLhsFunction.kt") + public void testEmptyLhsFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsFunction.kt"); + doTest(fileName); + } + + @TestMetadata("emptyLhsProperty.kt") + public void testEmptyLhsProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsProperty.kt"); + doTest(fileName); + } + @TestMetadata("expression.kt") public void testExpression() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java index fa480e47206..5acb12b2dae 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -745,6 +745,18 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi doTest(fileName); } + @TestMetadata("emptyLhsFunction.kt") + public void testEmptyLhsFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsFunction.kt"); + doTest(fileName); + } + + @TestMetadata("emptyLhsProperty.kt") + public void testEmptyLhsProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsProperty.kt"); + doTest(fileName); + } + @TestMetadata("expression.kt") public void testExpression() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CallableReferenceInlineTestsGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CallableReferenceInlineTestsGenerated.java index 520b787e575..78a89950e5b 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CallableReferenceInlineTestsGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CallableReferenceInlineTestsGenerated.java @@ -116,6 +116,18 @@ public class CallableReferenceInlineTestsGenerated extends AbstractCallableRefer doTest(fileName); } + @TestMetadata("emptyLhsFunction.kt") + public void testEmptyLhsFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsFunction.kt"); + doTest(fileName); + } + + @TestMetadata("emptyLhsProperty.kt") + public void testEmptyLhsProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/emptyLhsProperty.kt"); + doTest(fileName); + } + @TestMetadata("expression.kt") public void testExpression() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");