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
This commit is contained in:
Dmitry Petrov
2017-05-25 15:53:26 +03:00
parent 7220183912
commit 1420926a9b
6 changed files with 113 additions and 18 deletions
@@ -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(
@@ -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
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.types.KotlinType
interface IntermediateValue {
fun load(): IrExpression
fun loadIfExists(): IrExpression? = load()
val type: KotlinType
}
@@ -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
@@ -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 <get-a>(): kotlin.String
$this: VALUE_PARAMETER this@Foo: Foo
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-a>(): 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<kotlin.String>
FIELD PROPERTY_BACKING_FIELD public val test1: kotlin.reflect.KProperty0<kotlin.String>
EXPRESSION_BODY
PROPERTY_REFERENCE 'a: String' field=null getter='<get-a>(): String' setter=null type=kotlin.reflect.KProperty0<kotlin.String> origin=null
$this: GET_OBJECT 'Foo' type=test.Foo
FUN DEFAULT_PROPERTY_ACCESSOR public fun <get-test1>(): kotlin.reflect.KProperty0<kotlin.String>
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-test1>(): KProperty0<String>'
GET_FIELD 'test1: KProperty0<String>' type=kotlin.reflect.KProperty0<kotlin.String> origin=null
PROPERTY public val test1a: kotlin.reflect.KProperty0<kotlin.String>
FIELD PROPERTY_BACKING_FIELD public val test1a: kotlin.reflect.KProperty0<kotlin.String>
EXPRESSION_BODY
PROPERTY_REFERENCE 'a: String' field=null getter='<get-a>(): String' setter=null type=kotlin.reflect.KProperty0<kotlin.String> origin=null
$this: GET_OBJECT 'Foo' type=test.Foo
FUN DEFAULT_PROPERTY_ACCESSOR public fun <get-test1a>(): kotlin.reflect.KProperty0<kotlin.String>
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-test1a>(): KProperty0<String>'
GET_FIELD 'test1a: KProperty0<String>' type=kotlin.reflect.KProperty0<kotlin.String> origin=null
PROPERTY public val test2: kotlin.reflect.KFunction0<kotlin.String>
FIELD PROPERTY_BACKING_FIELD public val test2: kotlin.reflect.KFunction0<kotlin.String>
EXPRESSION_BODY
FUNCTION_REFERENCE 'foo(): String' type=kotlin.reflect.KFunction0<kotlin.String> origin=null
$this: GET_OBJECT 'Foo' type=test.Foo
FUN DEFAULT_PROPERTY_ACCESSOR public fun <get-test2>(): kotlin.reflect.KFunction0<kotlin.String>
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-test2>(): KFunction0<String>'
GET_FIELD 'test2: KFunction0<String>' type=kotlin.reflect.KFunction0<kotlin.String> origin=null
PROPERTY public val test2a: kotlin.reflect.KFunction0<kotlin.String>
FIELD PROPERTY_BACKING_FIELD public val test2a: kotlin.reflect.KFunction0<kotlin.String>
EXPRESSION_BODY
FUNCTION_REFERENCE 'foo(): String' type=kotlin.reflect.KFunction0<kotlin.String> origin=null
$this: GET_OBJECT 'Foo' type=test.Foo
FUN DEFAULT_PROPERTY_ACCESSOR public fun <get-test2a>(): kotlin.reflect.KFunction0<kotlin.String>
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-test2a>(): KFunction0<String>'
GET_FIELD 'test2a: KFunction0<String>' type=kotlin.reflect.KFunction0<kotlin.String> origin=null
@@ -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");