From 634d9834de31f7445db2ebec3de95716e0f142cd Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Thu, 2 Mar 2017 15:55:38 +0300 Subject: [PATCH] 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. --- .../generators/ArgumentsGenerationUtils.kt | 22 +++---- .../psi2ir/generators/AssignmentGenerator.kt | 7 +-- .../kotlin/psi2ir/generators/CallGenerator.kt | 63 ++++++++++++++++--- .../psi2ir/generators/StatementGenerator.kt | 47 +++----------- .../intermediate/TransientReceiverValue.kt | 4 +- .../ir/irText/expressions/objectAsCallable.kt | 9 +++ .../irText/expressions/objectAsCallable.txt | 45 +++++++++++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 6 ++ 8 files changed, 137 insertions(+), 66 deletions(-) create mode 100644 compiler/testData/ir/irText/expressions/objectAsCallable.kt create mode 100644 compiler/testData/ir/irText/expressions/objectAsCallable.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt index 9467e8abdcb..aa04315a3ce 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt @@ -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( diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt index 81b68fdcd39..b1e73ccc326 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt @@ -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") diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt index 2157e330296..c32f9605fa3 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt @@ -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?, - 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?, + origin: IrStatementOrigin? = null + ) = @Suppress("DEPRECATION") if (descriptor is LocalVariableDescriptor && descriptor.isDelegated) IrCallImpl(startOffset, endOffset, descriptor.type, descriptor.getter!!, typeArguments, origin ?: IrStatementOrigin.GET_LOCAL_PROPERTY) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt index b4c57116f6b..969134ab28b 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt @@ -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) ?: 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 c76824ed3fe..4a71c397cff 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 @@ -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") } } diff --git a/compiler/testData/ir/irText/expressions/objectAsCallable.kt b/compiler/testData/ir/irText/expressions/objectAsCallable.kt new file mode 100644 index 00000000000..59b940d54a0 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/objectAsCallable.kt @@ -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) \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/objectAsCallable.txt b/compiler/testData/ir/irText/expressions/objectAsCallable.txt new file mode 100644 index 00000000000..148dba68b1f --- /dev/null +++ b/compiler/testData/ir/irText/expressions/objectAsCallable.txt @@ -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 + 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 (): kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): 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 (): kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): Int' + GET_FIELD 'test2: Int' type=kotlin.Int origin=null diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index ae54cf1211b..b2a10f2a504 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -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");