Wrong receiver is generated for variable-as-function call on object.

Move 'generateExpressionForReferencedDescriptor' to CallGenerator,
use it in 'generateCall',
add PSI-free versions of some utility methods so that call elements can
be generated when we're already deep in ResolvedCall generation
and have forgotten about PSI.
This commit is contained in:
Dmitry Petrov
2017-03-02 15:55:38 +03:00
parent d573962259
commit 634d9834de
8 changed files with 137 additions and 66 deletions
@@ -41,9 +41,12 @@ import java.lang.AssertionError
fun StatementGenerator.generateReceiverOrNull(ktDefaultElement: KtElement, receiver: ReceiverValue?): IntermediateValue? =
receiver?.let { generateReceiver(ktDefaultElement, receiver) }
fun StatementGenerator.generateReceiver(ktDefaultElement: KtElement, receiver: ReceiverValue): IntermediateValue {
fun StatementGenerator.generateReceiver(ktDefaultElement: KtElement, receiver: ReceiverValue): IntermediateValue =
generateReceiver(ktDefaultElement.startOffset, ktDefaultElement.endOffset, receiver)
fun StatementGenerator.generateReceiver(startOffset: Int, endOffset: Int, receiver: ReceiverValue): IntermediateValue {
if (receiver is TransientReceiver) {
return TransientReceiverValue(ktDefaultElement.text, receiver.type)
return TransientReceiverValue(receiver.type)
}
val receiverExpression = when (receiver) {
@@ -51,11 +54,10 @@ fun StatementGenerator.generateReceiver(ktDefaultElement: KtElement, receiver: R
if (receiver.classDescriptor.kind.isSingleton &&
this.scopeOwner != receiver.classDescriptor && //For anonymous initializers
this.scopeOwner.containingDeclaration != receiver.classDescriptor) {
IrGetObjectValueImpl(ktDefaultElement.startOffset, ktDefaultElement.endOffset, receiver.type,
receiver.classDescriptor)
IrGetObjectValueImpl(startOffset, endOffset, receiver.type, receiver.classDescriptor)
}
else {
IrGetValueImpl(ktDefaultElement.startOffset, ktDefaultElement.endOffset, receiver.classDescriptor.thisAsReceiverParameter)
IrGetValueImpl(startOffset, endOffset, receiver.classDescriptor.thisAsReceiverParameter)
}
}
is ThisClassReceiver ->
@@ -68,8 +70,7 @@ fun StatementGenerator.generateReceiver(ktDefaultElement: KtElement, receiver: R
IrGetObjectValueImpl(receiver.expression.startOffset, receiver.expression.endOffset, receiver.type,
receiver.classQualifier.descriptor as ClassDescriptor)
is ExtensionReceiver ->
IrGetValueImpl(ktDefaultElement.startOffset, ktDefaultElement.startOffset,
receiver.declarationDescriptor.extensionReceiverParameter!!)
IrGetValueImpl(startOffset, startOffset, receiver.declarationDescriptor.extensionReceiverParameter!!)
else ->
TODO("Receiver: ${receiver.javaClass.simpleName}")
}
@@ -88,14 +89,13 @@ private fun generateThisOrSuperReceiver(receiver: ReceiverValue, classDescriptor
}
fun StatementGenerator.generateBackingFieldReceiver(
ktDefaultElement: KtElement,
startOffset: Int,
endOffset: Int,
resolvedCall: ResolvedCall<*>?,
fieldDescriptor: SyntheticFieldDescriptor
): IntermediateValue? {
val receiver = resolvedCall?.dispatchReceiver ?: fieldDescriptor.getDispatchReceiverForBackend() ?: return null
return this.generateReceiver(ktDefaultElement, receiver)
return this.generateReceiver(startOffset, endOffset, receiver)
}
fun StatementGenerator.generateCallReceiver(
@@ -108,11 +108,8 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
return when (descriptor) {
is SyntheticFieldDescriptor -> {
val receiverValue = statementGenerator.generateBackingFieldReceiver(ktLeft, resolvedCall, descriptor)
BackingFieldLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor.propertyDescriptor,
receiverValue, origin)
val receiverValue = statementGenerator.generateBackingFieldReceiver(ktLeft.startOffset, ktLeft.endOffset, resolvedCall, descriptor)
BackingFieldLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor.propertyDescriptor, receiverValue, origin)
}
is LocalVariableDescriptor ->
@Suppress("DEPRECATION")
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
@@ -26,8 +27,12 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.intermediate.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
import org.jetbrains.kotlin.types.KotlinType
import java.util.*
@@ -38,21 +43,61 @@ class CallGenerator(statementGenerator: StatementGenerator): StatementGeneratorE
return when (descriptor) {
is PropertyDescriptor ->
generatePropertyGetterCall(descriptor, startOffset, endOffset, call)
is VariableDescriptor ->
call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
generateGetVariable(startOffset, endOffset, descriptor, getTypeArguments(call.original), origin)
}
is FunctionDescriptor ->
generateFunctionCall(descriptor, startOffset, endOffset, origin, call)
else ->
TODO("Unexpected callable descriptor: $descriptor ${descriptor.javaClass.simpleName}")
call.callReceiver.call { _, _ ->
generateValueReference(startOffset, endOffset, descriptor, call.original, origin)
}
}
}
fun generateGetVariable(startOffset: Int, endOffset: Int,
descriptor: VariableDescriptor,
typeArguments: Map<TypeParameterDescriptor, KotlinType>?,
origin: IrStatementOrigin? = null) =
fun generateValueReference(
startOffset: Int,
endOffset: Int,
descriptor: DeclarationDescriptor,
resolvedCall: ResolvedCall<*>?,
origin: IrStatementOrigin?
): IrExpression =
when (descriptor) {
is FakeCallableDescriptorForObject ->
generateValueReference(startOffset, endOffset, descriptor.getReferencedDescriptor(), resolvedCall, origin)
is TypeAliasDescriptor ->
generateValueReference(startOffset, endOffset, descriptor.classDescriptor!!, null, origin)
is ClassDescriptor -> {
val classValueType = descriptor.classValueType!!
when {
DescriptorUtils.isObject(descriptor) ->
IrGetObjectValueImpl(startOffset, endOffset, classValueType, descriptor)
DescriptorUtils.isEnumEntry(descriptor) ->
IrGetEnumValueImpl(startOffset, endOffset, classValueType, descriptor)
else -> {
val companionObjectDescriptor = descriptor.companionObjectDescriptor
?: throw java.lang.AssertionError("Class value without companion object: $descriptor")
IrGetObjectValueImpl(startOffset, endOffset, classValueType, companionObjectDescriptor)
}
}
}
is PropertyDescriptor -> {
generateCall(startOffset, endOffset, statementGenerator.pregenerateCall(resolvedCall!!))
}
is SyntheticFieldDescriptor -> {
val receiver = statementGenerator.generateBackingFieldReceiver(startOffset, endOffset, resolvedCall, descriptor)
IrGetFieldImpl(startOffset, endOffset, descriptor.propertyDescriptor, receiver?.load())
}
is VariableDescriptor ->
generateGetVariable(startOffset, endOffset, descriptor, getTypeArguments(resolvedCall), origin)
else ->
TODO("Unexpected callable descriptor: $descriptor ${descriptor.javaClass.simpleName}")
}
private fun generateGetVariable(
startOffset: Int,
endOffset: Int,
descriptor: VariableDescriptor,
typeArguments: Map<TypeParameterDescriptor, KotlinType>?,
origin: IrStatementOrigin? = null
) =
@Suppress("DEPRECATION")
if (descriptor is LocalVariableDescriptor && descriptor.isDelegated)
IrCallImpl(startOffset, endOffset, descriptor.type, descriptor.getter!!, typeArguments, origin ?: IrStatementOrigin.GET_LOCAL_PROPERTY)
@@ -16,8 +16,10 @@
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.assertCast
import org.jetbrains.kotlin.ir.builders.Scope
@@ -35,13 +37,10 @@ import org.jetbrains.kotlin.psi2ir.intermediate.createTemporaryVariableInBlock
import org.jetbrains.kotlin.psi2ir.intermediate.setExplicitReceiverValue
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import java.lang.AssertionError
@@ -262,40 +261,10 @@ class StatementGenerator(
expression: KtExpression,
resolvedCall: ResolvedCall<*>?
): IrExpression =
when (descriptor) {
is FakeCallableDescriptorForObject ->
generateExpressionForReferencedDescriptor(descriptor.getReferencedDescriptor(), expression, resolvedCall)
is TypeAliasDescriptor ->
generateExpressionForReferencedDescriptor(descriptor.classDescriptor!!, expression, null)
is ClassDescriptor -> {
val classValueType = descriptor.classValueType!!
when {
DescriptorUtils.isObject(descriptor) ->
IrGetObjectValueImpl(expression.startOffset, expression.endOffset, classValueType, descriptor)
DescriptorUtils.isEnumEntry(descriptor) ->
IrGetEnumValueImpl(expression.startOffset, expression.endOffset, classValueType, descriptor)
else -> {
IrGetObjectValueImpl(expression.startOffset, expression.endOffset, classValueType,
descriptor.companionObjectDescriptor ?: throw AssertionError("Class value without companion object: $descriptor"))
}
}
}
is PropertyDescriptor -> {
CallGenerator(this).generateCall(expression.startOffset, expression.endOffset, pregenerateCall(resolvedCall!!))
}
is SyntheticFieldDescriptor -> {
val receiver = generateBackingFieldReceiver(expression, resolvedCall, descriptor)
IrGetFieldImpl(expression.startOffset, expression.endOffset, descriptor.propertyDescriptor, receiver?.load())
}
is VariableDescriptor ->
CallGenerator(this).generateGetVariable(expression.startOffset, expression.endOffset, descriptor,
getTypeArguments(resolvedCall))
else ->
IrErrorExpressionImpl(
expression.startOffset, expression.endOffset, getInferredTypeWithImplicitCastsOrFail(expression),
expression.text + ": ${descriptor.name} ${descriptor.javaClass.simpleName}"
)
}
CallGenerator(this).generateValueReference(
expression.startOffset, expression.endOffset,
descriptor, resolvedCall, null
)
override fun visitCallExpression(expression: KtCallExpression, data: Nothing?): IrStatement {
val resolvedCall = getResolvedCall(expression) ?:
@@ -19,8 +19,8 @@ package org.jetbrains.kotlin.psi2ir.intermediate
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.types.KotlinType
class TransientReceiverValue(val description: String, override val type: KotlinType): IntermediateValue {
class TransientReceiverValue(override val type: KotlinType): IntermediateValue {
override fun load(): IrExpression {
throw AssertionError("Transient receiver should not be instantiated: $description")
throw AssertionError("Transient receiver should not be instantiated")
}
}
@@ -0,0 +1,9 @@
object A
enum class En { X }
operator fun A.invoke(i: Int) = i
operator fun En.invoke(i: Int) = i
val test1 = A(42)
val test2 = En.X(42)
@@ -0,0 +1,45 @@
FILE /objectAsCallable.kt
CLASS OBJECT A
CONSTRUCTOR private constructor A()
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'constructor Any()'
INSTANCE_INITIALIZER_CALL classDescriptor='A'
CLASS ENUM_CLASS En
CONSTRUCTOR private constructor En()
BLOCK_BODY
ENUM_CONSTRUCTOR_CALL 'constructor Enum(String, Int)'
INSTANCE_INITIALIZER_CALL classDescriptor='En'
ENUM_ENTRY enum entry X
init: ENUM_CONSTRUCTOR_CALL 'constructor En()'
FUN ENUM_CLASS_SPECIAL_MEMBER public final fun values(): kotlin.Array<En>
SYNTHETIC_BODY kind=ENUM_VALUES
FUN ENUM_CLASS_SPECIAL_MEMBER public final fun valueOf(value: kotlin.String): En
SYNTHETIC_BODY kind=ENUM_VALUEOF
FUN public operator fun A.invoke(i: kotlin.Int): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing from='invoke(Int) on A: Int'
GET_VAR 'value-parameter i: Int' type=kotlin.Int origin=null
FUN public operator fun En.invoke(i: kotlin.Int): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing from='invoke(Int) on En: Int'
GET_VAR 'value-parameter i: Int' type=kotlin.Int origin=null
PROPERTY public val test1: kotlin.Int
FIELD PROPERTY_BACKING_FIELD public val test1: kotlin.Int
EXPRESSION_BODY
CALL 'invoke(Int) on A: Int' type=kotlin.Int origin=INVOKE
$receiver: GET_OBJECT 'A' type=A
i: CONST Int type=kotlin.Int value='42'
FUN DEFAULT_PROPERTY_ACCESSOR public fun <get-test1>(): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-test1>(): Int'
GET_FIELD 'test1: Int' type=kotlin.Int origin=null
PROPERTY public val test2: kotlin.Int
FIELD PROPERTY_BACKING_FIELD public val test2: kotlin.Int
EXPRESSION_BODY
CALL 'invoke(Int) on En: Int' type=kotlin.Int origin=INVOKE
$receiver: GET_ENUM 'X' type=En
i: CONST Int type=kotlin.Int value='42'
FUN DEFAULT_PROPERTY_ACCESSOR public fun <get-test2>(): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-test2>(): Int'
GET_FIELD 'test2: Int' type=kotlin.Int origin=null
@@ -590,6 +590,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName);
}
@TestMetadata("objectAsCallable.kt")
public void testObjectAsCallable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/objectAsCallable.kt");
doTest(fileName);
}
@TestMetadata("objectClassReference.kt")
public void testObjectClassReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/objectClassReference.kt");