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:
@@ -2839,14 +2839,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public StackValue generateCallableReferenceReceiver(@NotNull ResolvedCall<?> resolvedCall) {
|
public StackValue generateCallableReferenceReceiver(@NotNull ResolvedCall<?> resolvedCall) {
|
||||||
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
|
ReceiverValue receiver = getBoundCallableReferenceReceiver(resolvedCall);
|
||||||
if (descriptor.getExtensionReceiverParameter() == null && descriptor.getDispatchReceiverParameter() == null) return null;
|
if (receiver == 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;
|
|
||||||
|
|
||||||
return StackValue.coercion(generateReceiverValue(receiver, false), asmType(receiver.getType()));
|
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.BindingContext;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
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.inline.InlineUtil;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
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.DeserializedCallableMemberDescriptor;
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor;
|
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor;
|
||||||
import org.jetbrains.kotlin.types.KotlinType;
|
import org.jetbrains.kotlin.types.KotlinType;
|
||||||
@@ -313,4 +315,19 @@ public class JvmCodegenUtil {
|
|||||||
public static boolean isDelegatedLocalVariable(@NotNull DeclarationDescriptor descriptor) {
|
public static boolean isDelegatedLocalVariable(@NotNull DeclarationDescriptor descriptor) {
|
||||||
return descriptor instanceof LocalVariableDescriptor && ((LocalVariableDescriptor) descriptor).isDelegated();
|
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.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
|
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.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil.isInlinableParameterExpression
|
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.JvmMethodParameterKind
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
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.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.ExpressionTypingUtils.isFunctionLiteral
|
||||||
import org.jetbrains.kotlin.types.expressions.LabelResolver
|
import org.jetbrains.kotlin.types.expressions.LabelResolver
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
@@ -704,19 +704,10 @@ class PsiInlineCodegen(
|
|||||||
activeLambda = null
|
activeLambda = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getBoundCallableReferenceReceiver(argumentExpression: KtExpression): ReceiverValue? {
|
||||||
private fun getBoundCallableReferenceReceiver(
|
val deparenthesized = KtPsiUtil.deparenthesize(argumentExpression) as? KtCallableReferenceExpression ?: return null
|
||||||
argumentExpression: KtExpression
|
val resolvedCall = deparenthesized.callableReference.getResolvedCallWithAssert(state.bindingContext)
|
||||||
): KtExpression? {
|
return JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*lambda or callable reference*/
|
/*lambda or callable reference*/
|
||||||
@@ -736,10 +727,10 @@ class PsiInlineCodegen(
|
|||||||
if (isInliningParameter(argumentExpression, valueParameterDescriptor)) {
|
if (isInliningParameter(argumentExpression, valueParameterDescriptor)) {
|
||||||
val lambdaInfo = rememberClosure(argumentExpression, parameterType, valueParameterDescriptor)
|
val lambdaInfo = rememberClosure(argumentExpression, parameterType, valueParameterDescriptor)
|
||||||
|
|
||||||
val receiver = getBoundCallableReferenceReceiver(argumentExpression)
|
val receiverValue = getBoundCallableReferenceReceiver(argumentExpression)
|
||||||
if (receiver != null) {
|
if (receiverValue != null) {
|
||||||
val receiverValue = codegen.gen(receiver)
|
val receiver = codegen.generateReceiverValue(receiverValue, false)
|
||||||
putClosureParametersOnStack(lambdaInfo, StackValue.coercion(receiverValue, receiverValue.type.boxReceiverForBoundReference()))
|
putClosureParametersOnStack(lambdaInfo, StackValue.coercion(receiver, receiver.type.boxReceiverForBoundReference()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+6
-15
@@ -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.AbstractInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.LabelNode
|
import org.jetbrains.org.objectweb.asm.tree.LabelNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
import java.util.HashMap
|
import java.util.*
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
interface SourceCompilerForInline {
|
interface SourceCompilerForInline {
|
||||||
@@ -174,28 +174,19 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
|
|||||||
|
|
||||||
val strategy = when (expression) {
|
val strategy = when (expression) {
|
||||||
is KtCallableReferenceExpression -> {
|
is KtCallableReferenceExpression -> {
|
||||||
val receiverExpression = expression.receiverExpression
|
val resolvedCall = expression.callableReference.getResolvedCallWithAssert(state.bindingContext)
|
||||||
val receiverType = if (receiverExpression != null && state.bindingContext.getType(receiverExpression) != null)
|
val receiverType = JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)?.type?.let(state.typeMapper::mapType)
|
||||||
state.typeMapper.mapType(state.bindingContext.getType(receiverExpression)!!)
|
|
||||||
else
|
|
||||||
null
|
|
||||||
|
|
||||||
if (isLambda && lambdaInfo!!.isPropertyReference) {
|
if (isLambda && lambdaInfo!!.isPropertyReference) {
|
||||||
val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor)
|
val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor)
|
||||||
val info = lambdaInfo.propertyReferenceInfo
|
val info = lambdaInfo.propertyReferenceInfo
|
||||||
PropertyReferenceCodegen.PropertyReferenceGenerationStrategy(
|
PropertyReferenceCodegen.PropertyReferenceGenerationStrategy(
|
||||||
true, info!!.getFunction, info.target, asmType, receiverType,
|
true, info!!.getFunction, info.target, asmType, receiverType,
|
||||||
lambdaInfo.functionWithBodyOrCallableReference, state, true)
|
lambdaInfo.functionWithBodyOrCallableReference, state, true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
FunctionReferenceGenerationStrategy(
|
FunctionReferenceGenerationStrategy(state, descriptor, resolvedCall, receiverType, null, true)
|
||||||
state,
|
|
||||||
descriptor,
|
|
||||||
expression.callableReference
|
|
||||||
.getResolvedCallWithAssert(state.bindingContext),
|
|
||||||
receiverType, null,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody)
|
is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody)
|
||||||
|
|||||||
+15
@@ -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()
|
||||||
|
}
|
||||||
+14
@@ -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()
|
||||||
|
}
|
||||||
+12
@@ -745,6 +745,18 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("expression.kt")
|
||||||
public void testExpression() throws Exception {
|
public void testExpression() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");
|
||||||
|
|||||||
+12
@@ -745,6 +745,18 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("expression.kt")
|
||||||
public void testExpression() throws Exception {
|
public void testExpression() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");
|
||||||
|
|||||||
@@ -745,6 +745,18 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("expression.kt")
|
||||||
public void testExpression() throws Exception {
|
public void testExpression() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");
|
||||||
|
|||||||
+12
@@ -745,6 +745,18 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("expression.kt")
|
||||||
public void testExpression() throws Exception {
|
public void testExpression() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");
|
||||||
|
|||||||
+12
@@ -116,6 +116,18 @@ public class CallableReferenceInlineTestsGenerated extends AbstractCallableRefer
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("expression.kt")
|
||||||
public void testExpression() throws Exception {
|
public void testExpression() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/expression.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user