From 1420926a9b4788b3c0aae6953830c018adb80b02 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Thu, 25 May 2017 15:53:26 +0300 Subject: [PATCH] Generate receivers for callable references in the same way as for calls Properly handle callable members imported from objects and other related corner cases. #KT-18084 Fixed --- .../ReflectionReferencesGenerator.kt | 43 +++++++----- .../intermediate/TransientReceiverValue.kt | 2 + .../kotlin/psi2ir/intermediate/Values.kt | 1 + .../callableReferenceToImportedFromObject.kt | 14 ++++ .../callableReferenceToImportedFromObject.txt | 65 +++++++++++++++++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 6 ++ 6 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 compiler/testData/ir/irText/expressions/callableReferenceToImportedFromObject.kt create mode 100644 compiler/testData/ir/irText/expressions/callableReferenceToImportedFromObject.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt index 11a45bcd186..7e579c34d19 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt @@ -25,8 +25,9 @@ import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtClassLiteralExpression import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.psi2ir.intermediate.TransientReceiverValue import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver +import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.expressions.DoubleColonLHS @@ -51,24 +52,30 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St fun generateCallableReference(ktCallableReference: KtCallableReferenceExpression): IrExpression { val resolvedCall = getResolvedCall(ktCallableReference.callableReference)!! - val irCallableRef = generateCallableReference( - ktCallableReference.startOffset, ktCallableReference.endOffset, - getInferredTypeWithImplicitCastsOrFail(ktCallableReference), - resolvedCall.resultingDescriptor, - typeArguments = null - ) - resolvedCall.dispatchReceiver?.let { dispatchReceiver -> - if (dispatchReceiver !is TransientReceiver) { - irCallableRef.dispatchReceiver = statementGenerator.generateReceiver(ktCallableReference, dispatchReceiver).load() - } - } - resolvedCall.extensionReceiver?.let { extensionReceiver -> - if (extensionReceiver !is TransientReceiver) { - irCallableRef.extensionReceiver = statementGenerator.generateReceiver(ktCallableReference, extensionReceiver).load() - } - } - return irCallableRef + val resultingDescriptor = resolvedCall.resultingDescriptor + val descriptorImportedFromObject = resultingDescriptor as? ImportedFromObjectCallableDescriptor<*> + val referencedDescriptor = descriptorImportedFromObject?.callableFromObject ?: resultingDescriptor + + val startOffset = ktCallableReference.startOffset + val endOffset = ktCallableReference.endOffset + + return statementGenerator.generateCallReceiver( + ktCallableReference, + resultingDescriptor, + resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver, + isSafe = false + ).call { dispatchReceiverValue, extensionReceiverValue -> + generateCallableReference( + startOffset, endOffset, + getInferredTypeWithImplicitCastsOrFail(ktCallableReference), + referencedDescriptor, + typeArguments = null + ).also { irCallableReference -> + irCallableReference.dispatchReceiver = dispatchReceiverValue?.loadIfExists() + irCallableReference.extensionReceiver = extensionReceiverValue?.loadIfExists() + } + } } fun generateCallableReference( diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/TransientReceiverValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/TransientReceiverValue.kt index 4a71c397cff..0570b7ad449 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/TransientReceiverValue.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/TransientReceiverValue.kt @@ -23,4 +23,6 @@ class TransientReceiverValue(override val type: KotlinType): IntermediateValue { override fun load(): IrExpression { throw AssertionError("Transient receiver should not be instantiated") } + + override fun loadIfExists(): IrExpression? = null } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/Values.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/Values.kt index 640138b4b41..454e3210281 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/Values.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/Values.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.types.KotlinType interface IntermediateValue { fun load(): IrExpression + fun loadIfExists(): IrExpression? = load() val type: KotlinType } diff --git a/compiler/testData/ir/irText/expressions/callableReferenceToImportedFromObject.kt b/compiler/testData/ir/irText/expressions/callableReferenceToImportedFromObject.kt new file mode 100644 index 00000000000..996561aaa3e --- /dev/null +++ b/compiler/testData/ir/irText/expressions/callableReferenceToImportedFromObject.kt @@ -0,0 +1,14 @@ +package test + +import test.Foo.a +import test.Foo.foo + +object Foo { + val a: String = "" + fun foo(): String = "" +} + +val test1 = ::a +val test1a = Foo::a +val test2 = ::foo +val test2a = Foo::foo \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/callableReferenceToImportedFromObject.txt b/compiler/testData/ir/irText/expressions/callableReferenceToImportedFromObject.txt new file mode 100644 index 00000000000..c182cd879b1 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/callableReferenceToImportedFromObject.txt @@ -0,0 +1,65 @@ +FILE /callableReferenceToImportedFromObject.kt + CLASS OBJECT Foo + $this: VALUE_PARAMETER this@Foo: Foo + CONSTRUCTOR private constructor Foo() + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='Foo' + PROPERTY public final val a: kotlin.String = "" + FIELD PROPERTY_BACKING_FIELD public final val a: kotlin.String = "" + EXPRESSION_BODY + CONST String type=kotlin.String value='' + FUN DEFAULT_PROPERTY_ACCESSOR public final fun (): kotlin.String + $this: VALUE_PARAMETER this@Foo: Foo + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): String' + GET_FIELD 'a: String' type=kotlin.String origin=null + receiver: GET_VAR 'this@Foo: Foo' type=test.Foo origin=null + FUN public final fun foo(): kotlin.String + $this: VALUE_PARAMETER this@Foo: Foo + BLOCK_BODY + RETURN type=kotlin.Nothing from='foo(): String' + CONST String type=kotlin.String value='' + FUN FAKE_OVERRIDE public open override fun equals(other: kotlin.Any?): kotlin.Boolean + $this: VALUE_PARAMETER this@Any: Any + VALUE_PARAMETER value-parameter other: kotlin.Any? + FUN FAKE_OVERRIDE public open override fun hashCode(): kotlin.Int + $this: VALUE_PARAMETER this@Any: Any + FUN FAKE_OVERRIDE public open override fun toString(): kotlin.String + $this: VALUE_PARAMETER this@Any: Any + PROPERTY public val test1: kotlin.reflect.KProperty0 + FIELD PROPERTY_BACKING_FIELD public val test1: kotlin.reflect.KProperty0 + EXPRESSION_BODY + PROPERTY_REFERENCE 'a: String' field=null getter='(): String' setter=null type=kotlin.reflect.KProperty0 origin=null + $this: GET_OBJECT 'Foo' type=test.Foo + FUN DEFAULT_PROPERTY_ACCESSOR public fun (): kotlin.reflect.KProperty0 + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): KProperty0' + GET_FIELD 'test1: KProperty0' type=kotlin.reflect.KProperty0 origin=null + PROPERTY public val test1a: kotlin.reflect.KProperty0 + FIELD PROPERTY_BACKING_FIELD public val test1a: kotlin.reflect.KProperty0 + EXPRESSION_BODY + PROPERTY_REFERENCE 'a: String' field=null getter='(): String' setter=null type=kotlin.reflect.KProperty0 origin=null + $this: GET_OBJECT 'Foo' type=test.Foo + FUN DEFAULT_PROPERTY_ACCESSOR public fun (): kotlin.reflect.KProperty0 + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): KProperty0' + GET_FIELD 'test1a: KProperty0' type=kotlin.reflect.KProperty0 origin=null + PROPERTY public val test2: kotlin.reflect.KFunction0 + FIELD PROPERTY_BACKING_FIELD public val test2: kotlin.reflect.KFunction0 + EXPRESSION_BODY + FUNCTION_REFERENCE 'foo(): String' type=kotlin.reflect.KFunction0 origin=null + $this: GET_OBJECT 'Foo' type=test.Foo + FUN DEFAULT_PROPERTY_ACCESSOR public fun (): kotlin.reflect.KFunction0 + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): KFunction0' + GET_FIELD 'test2: KFunction0' type=kotlin.reflect.KFunction0 origin=null + PROPERTY public val test2a: kotlin.reflect.KFunction0 + FIELD PROPERTY_BACKING_FIELD public val test2a: kotlin.reflect.KFunction0 + EXPRESSION_BODY + FUNCTION_REFERENCE 'foo(): String' type=kotlin.reflect.KFunction0 origin=null + $this: GET_OBJECT 'Foo' type=test.Foo + FUN DEFAULT_PROPERTY_ACCESSOR public fun (): kotlin.reflect.KFunction0 + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): KFunction0' + GET_FIELD 'test2a: KFunction0' type=kotlin.reflect.KFunction0 origin=null diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 1c1b0b40f89..d774f21a5a3 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -563,6 +563,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("callableReferenceToImportedFromObject.kt") + public void testCallableReferenceToImportedFromObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/callableReferenceToImportedFromObject.kt"); + doTest(fileName); + } + @TestMetadata("calls.kt") public void testCalls() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/calls.kt");