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
This commit is contained in:
Alexander Udalov
2017-11-22 17:40:06 +01:00
parent 1ceb751061
commit f4f5359725
11 changed files with 124 additions and 42 deletions
@@ -2839,14 +2839,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> 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()));
}
@@ -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;
}
}
@@ -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 {
@@ -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)
@@ -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()
}
@@ -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()
}
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");