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 bf7f96a7d11..448b0112ea9 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 @@ -134,14 +134,10 @@ private fun StatementGenerator.shouldGenerateReceiverAsSingletonReference(receiv } private fun StatementGenerator.generateThisOrSuperReceiver(receiver: ReceiverValue, classDescriptor: ClassDescriptor): IrExpression { - val expressionReceiver = - receiver as? ExpressionReceiver ?: throw AssertionError("'this' or 'super' receiver should be an expression receiver") + val expressionReceiver = receiver as? ExpressionReceiver + ?: throw AssertionError("'this' or 'super' receiver should be an expression receiver") val ktReceiver = expressionReceiver.expression - return IrGetValueImpl( - ktReceiver.startOffsetSkippingComments, ktReceiver.endOffset, - expressionReceiver.type.toIrType(), - context.symbolTable.referenceValueParameter(classDescriptor.thisAsReceiverParameter) - ) + return generateThisReceiver(ktReceiver.startOffsetSkippingComments, ktReceiver.endOffset, expressionReceiver.type, classDescriptor) } fun StatementGenerator.generateBackingFieldReceiver( 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 5e3e14e830f..7b2aa1ad6bc 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 @@ -38,6 +38,7 @@ 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.constants.CompileTimeConstant @@ -364,28 +365,52 @@ class StatementGenerator( override fun visitSafeQualifiedExpression(expression: KtSafeQualifiedExpression, data: Nothing?): IrStatement = expression.selectorExpression!!.accept(this, data) + private fun isInsideClass(classDescriptor: ClassDescriptor): Boolean { + var scopeDescriptor: DeclarationDescriptor? = scopeOwner + while (scopeDescriptor != null) { + if (scopeDescriptor == classDescriptor) return true + scopeDescriptor = scopeDescriptor.containingDeclaration + } + return false + } + + fun generateThisReceiver(startOffset: Int, endOffset: Int, kotlinType: KotlinType, classDescriptor: ClassDescriptor): IrExpression { + val thisAsReceiverParameter = classDescriptor.thisAsReceiverParameter + val thisType = kotlinType.toIrType() + + return if (DescriptorUtils.isObject(classDescriptor) && !isInsideClass(classDescriptor)) { + IrGetObjectValueImpl( + startOffset, endOffset, + thisType, + context.symbolTable.referenceClass(classDescriptor) + ) + } else { + IrGetValueImpl( + startOffset, endOffset, + thisType, + context.symbolTable.referenceValueParameter(thisAsReceiverParameter) + ) + } + } + override fun visitThisExpression(expression: KtThisExpression, data: Nothing?): IrExpression { val referenceTarget = getOrFail(BindingContext.REFERENCE_TARGET, expression.instanceReference) { "No reference target for this" } + val startOffset = expression.startOffsetSkippingComments + val endOffset = expression.endOffset return when (referenceTarget) { - is ClassDescriptor -> { - val thisAsReceiverParameter = referenceTarget.thisAsReceiverParameter - val thisType = thisAsReceiverParameter.type.toIrType() - IrGetValueImpl( - expression.startOffsetSkippingComments, expression.endOffset, - thisType, - context.symbolTable.referenceValueParameter(thisAsReceiverParameter) - ) - } + is ClassDescriptor -> + generateThisReceiver(startOffset, endOffset, referenceTarget.thisAsReceiverParameter.type, referenceTarget) is CallableDescriptor -> { val extensionReceiver = referenceTarget.extensionReceiverParameter ?: TODO("No extension receiver: $referenceTarget") val extensionReceiverType = extensionReceiver.type.toIrType() IrGetValueImpl( - expression.startOffsetSkippingComments, expression.endOffset, + startOffset, endOffset, extensionReceiverType, context.symbolTable.referenceValueParameter(extensionReceiver) ) } + else -> error("Expected this or receiver: $referenceTarget") } diff --git a/compiler/testData/codegen/box/localClasses/anonymousObjectInExtension.kt b/compiler/testData/codegen/box/localClasses/anonymousObjectInExtension.kt index 0eb1d3217a4..74124f89a58 100644 --- a/compiler/testData/codegen/box/localClasses/anonymousObjectInExtension.kt +++ b/compiler/testData/codegen/box/localClasses/anonymousObjectInExtension.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR, JS_IR - fun WithCompanion.test(): String { object : WithCompanion(this) {} return "OK" diff --git a/compiler/testData/ir/irText/expressions/thisReferenceBeforeClassDeclared.kt b/compiler/testData/ir/irText/expressions/thisReferenceBeforeClassDeclared.kt new file mode 100644 index 00000000000..9ecd279c30f --- /dev/null +++ b/compiler/testData/ir/irText/expressions/thisReferenceBeforeClassDeclared.kt @@ -0,0 +1,10 @@ +fun WithCompanion.test() { + val test1 = object : WithCompanion(this) {} + val test2 = object : WithCompanion(this.foo()) {} +} + +open class WithCompanion(a: WithCompanion.Companion) { + companion object { + fun foo(): WithCompanion.Companion = this + } +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/thisReferenceBeforeClassDeclared.txt b/compiler/testData/ir/irText/expressions/thisReferenceBeforeClassDeclared.txt new file mode 100644 index 00000000000..d10e7efb373 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/thisReferenceBeforeClassDeclared.txt @@ -0,0 +1,95 @@ +FILE fqName: fileName:/thisReferenceBeforeClassDeclared.kt + FUN name:test visibility:public modality:FINAL <> ($receiver:WithCompanion) returnType:kotlin.Unit flags: + $receiver: VALUE_PARAMETER name: type:WithCompanion flags: + BLOCK_BODY + VAR name:test1 type:test. flags:val + BLOCK type=test. origin=OBJECT_LITERAL + CLASS CLASS name: modality:FINAL visibility:local flags: superTypes:[WithCompanion] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:test. flags: + CONSTRUCTOR visibility:public <> () returnType:test. flags:primary + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor WithCompanion(WithCompanion.Companion)' + a: GET_OBJECT 'companion object of WithCompanion' type=WithCompanion.Companion + INSTANCE_INITIALIZER_CALL classDescriptor='' + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags: + overridden: + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags: + overridden: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags: + overridden: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + CALL 'constructor ()' type=test. origin=OBJECT_LITERAL + VAR name:test2 type:test. flags:val + BLOCK type=test. origin=OBJECT_LITERAL + CLASS CLASS name: modality:FINAL visibility:local flags: superTypes:[WithCompanion] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:test. flags: + CONSTRUCTOR visibility:public <> () returnType:test. flags:primary + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor WithCompanion(WithCompanion.Companion)' + a: CALL 'foo(): WithCompanion.Companion' type=WithCompanion.Companion origin=null + $this: GET_OBJECT 'companion object of WithCompanion' type=WithCompanion.Companion + INSTANCE_INITIALIZER_CALL classDescriptor='' + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags: + overridden: + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags: + overridden: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags: + overridden: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + CALL 'constructor ()' type=test. origin=OBJECT_LITERAL + CLASS CLASS name:WithCompanion modality:OPEN visibility:public flags: superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:WithCompanion flags: + CONSTRUCTOR visibility:public <> (a:WithCompanion.Companion) returnType:WithCompanion flags:primary + VALUE_PARAMETER name:a index:0 type:WithCompanion.Companion flags: + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='WithCompanion' + CLASS OBJECT name:Companion modality:FINAL visibility:public flags:companion superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:WithCompanion.Companion flags: + CONSTRUCTOR visibility:private <> () returnType:WithCompanion.Companion flags:primary + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='companion object of WithCompanion' + FUN name:foo visibility:public modality:FINAL <> ($this:WithCompanion.Companion) returnType:WithCompanion.Companion flags: + $this: VALUE_PARAMETER name: type:WithCompanion.Companion flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='foo(): WithCompanion.Companion' + GET_VAR 'this@Companion: Companion' type=WithCompanion.Companion origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index eeda514383d..bbe0d0cb24c 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -1132,6 +1132,11 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { runTest("compiler/testData/ir/irText/expressions/thisOfGenericOuterClass.kt"); } + @TestMetadata("thisReferenceBeforeClassDeclared.kt") + public void testThisReferenceBeforeClassDeclared() throws Exception { + runTest("compiler/testData/ir/irText/expressions/thisReferenceBeforeClassDeclared.kt"); + } + @TestMetadata("throw.kt") public void testThrow() throws Exception { runTest("compiler/testData/ir/irText/expressions/throw.kt");