diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 962edb25bf2..e2bbda7713e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -81,6 +81,7 @@ val jvmPhases = namedIrFilePhase( makePatchParentsPhase(1) then + singletonReferencesPhase then jvmLocalDeclarationsPhase then singleAbstractMethodPhase then callableReferencePhase then @@ -95,7 +96,6 @@ val jvmPhases = namedIrFilePhase( enumClassPhase then objectClassPhase then makeInitializersPhase(JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, true) then - singletonReferencesPhase then syntheticAccessorPhase then bridgePhase then jvmOverloadsAnnotationPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SingletonReferencesLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SingletonReferencesLowering.kt index 17b6bc3d462..114ec6bc3d1 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SingletonReferencesLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SingletonReferencesLowering.kt @@ -5,14 +5,17 @@ package org.jetbrains.kotlin.backend.jvm.lower -import org.jetbrains.kotlin.backend.common.BodyLoweringPass +import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase +import org.jetbrains.kotlin.backend.common.pop +import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.ir.expressions.IrBody -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue -import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -22,18 +25,77 @@ internal val singletonReferencesPhase = makeIrFilePhase( description = "Handle singleton references" ) -private class SingletonReferencesLowering(val context: JvmBackendContext) : BodyLoweringPass, IrElementTransformerVoid() { - override fun lower(irBody: IrBody) { - irBody.transformChildrenVoid(this) +private class SingletonReferencesLowering(val context: JvmBackendContext) : ClassLoweringPass, IrElementTransformerVoid() { + private lateinit var containingClass: IrClass + private val constructingEnums = arrayListOf() + + override fun lower(irClass: IrClass) { + containingClass = irClass + irClass.transformChildrenVoid(this) + } + + override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression { + constructingEnums.push(expression.symbol.owner.parent) + val call = super.visitEnumConstructorCall(expression) + constructingEnums.pop() + return call } override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression { - val entrySymbol = context.declarationFactory.getFieldForEnumEntry(expression.symbol.owner, expression.type) - return IrGetFieldImpl(expression.startOffset, expression.endOffset, entrySymbol.symbol, expression.type) + val candidate = expression.symbol.owner.correspondingClass + + return if (candidate != null && isInScope(candidate) && !isVisitingSuperConstructor(candidate)) { + // Replace `SomeEnumClass.SomeEnumEntry` with `this`, if possible. + // + // SomeEnumEntry is a singleton, which is assigned (SETFIELD) to SomeEnumClass after the construction of the singleton is done. + // Therefore, during the construction of SomeEnumEntry, SomeEnumClass.SomeEnumEntry isn't available yet. All references to it + // must be replaced with `SomeEnumEntry.this`. + IrGetValueImpl(expression.startOffset, expression.endOffset, expression.type, candidate.thisReceiver!!.symbol) + } else { + val entrySymbol = context.declarationFactory.getFieldForEnumEntry(expression.symbol.owner, expression.type) + IrGetFieldImpl(expression.startOffset, expression.endOffset, entrySymbol.symbol, expression.type) + } } override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { val instanceField = context.declarationFactory.getFieldForObjectInstance(expression.symbol.owner) return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type) } + + // `this` is generally available while the reference is within the lexical scope of the containing enum entry. + private fun isInScope(symbol: IrSymbolOwner?): Boolean { + var candidate: IrDeclaration? = containingClass + + while (candidate != null && symbol != candidate) + candidate = candidate.parent as? IrDeclaration + + return candidate != null + } + + // `this` isn't usable before `super.`. Consider the example, + // + // 1: enum class Test(val x: String, val closure1: () -> String) { + // 2: FOO("O", { FOO.x }) { + // 3: val y: String = run { FOO.x } + // 4: }; + // 5: } + // + // The constructing sequence would look like the following, if the reference was lowered to `this`: + // + // FOO.(this) { + // val lambda1 = new lambda_in_line_1_type + // lambda_in_line_1_type.(lambda1, this) + // Test.(this, "O", lambda1) + // ... + // val lambda3 = new lambda_in_line_3_type + // lambda_in_line_3_type.(lambda3, this) + // } + // + // Before and after `Test.`, the type of `this` is `uninitializedThis` and `Test`, respectively. Therefore, passing `this` to + // `lambda_containing_foo_x_type.` results in a type mismatch. Passing `this` to `lambda_containing_foo_y_type.` is fine. + // + // Assumptions: + // 1. An enum entry's declaration parent is always the enum class. + // 2. Enums are constructed in , so there's no interleaving constructor calls from unrelated enums. + private fun isVisitingSuperConstructor(irClass: IrClass) = irClass.parent == constructingEnums.lastOrNull() } \ No newline at end of file diff --git a/compiler/testData/codegen/box/enum/deepInnerClassInEnumEntryClass.kt b/compiler/testData/codegen/box/enum/deepInnerClassInEnumEntryClass.kt index eec200eac1d..4c12585119f 100644 --- a/compiler/testData/codegen/box/enum/deepInnerClassInEnumEntryClass.kt +++ b/compiler/testData/codegen/box/enum/deepInnerClassInEnumEntryClass.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR - enum class A { X { val x = "OK" diff --git a/compiler/testData/codegen/box/enum/deepInnerClassInEnumEntryClass2.kt b/compiler/testData/codegen/box/enum/deepInnerClassInEnumEntryClass2.kt index d9b2b8cb42e..958b0be830f 100644 --- a/compiler/testData/codegen/box/enum/deepInnerClassInEnumEntryClass2.kt +++ b/compiler/testData/codegen/box/enum/deepInnerClassInEnumEntryClass2.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR - enum class A { X { val k = "K" diff --git a/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt index 7fce90cfd7f..135eeffae89 100644 --- a/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt +++ b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface IFoo { fun foo(): String } diff --git a/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt index 4a06775ea0d..7745c582462 100644 --- a/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt +++ b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface IFoo { fun foo(): String } diff --git a/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt index b43334323c0..635103b8b02 100644 --- a/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt +++ b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface IFoo { fun foo(): String } diff --git a/compiler/testData/codegen/box/enum/innerClassInEnumEntryClass.kt b/compiler/testData/codegen/box/enum/innerClassInEnumEntryClass.kt index 181ef811eaa..154e0a4539f 100644 --- a/compiler/testData/codegen/box/enum/innerClassInEnumEntryClass.kt +++ b/compiler/testData/codegen/box/enum/innerClassInEnumEntryClass.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR - enum class A { X { val x = "OK" diff --git a/compiler/testData/codegen/box/enum/innerClassMethodInEnumEntryClass.kt b/compiler/testData/codegen/box/enum/innerClassMethodInEnumEntryClass.kt index fcce8673a2e..8e561ccd6d1 100644 --- a/compiler/testData/codegen/box/enum/innerClassMethodInEnumEntryClass.kt +++ b/compiler/testData/codegen/box/enum/innerClassMethodInEnumEntryClass.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR - enum class A { X { val x = "OK" diff --git a/compiler/testData/codegen/box/enum/kt20651.kt b/compiler/testData/codegen/box/enum/kt20651.kt index 026902b2e5d..8845e4c6d48 100644 --- a/compiler/testData/codegen/box/enum/kt20651.kt +++ b/compiler/testData/codegen/box/enum/kt20651.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR enum class Test(val x: String, val closure1: () -> String) { FOO("O", { FOO.x }) { override val y: String = "K" diff --git a/compiler/testData/codegen/box/enum/kt20651_inlineLambda.kt b/compiler/testData/codegen/box/enum/kt20651_inlineLambda.kt index 41697c5dced..d1f12734575 100644 --- a/compiler/testData/codegen/box/enum/kt20651_inlineLambda.kt +++ b/compiler/testData/codegen/box/enum/kt20651_inlineLambda.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR enum class Test(val x: String, val closure1: () -> String) { FOO("O", run { { FOO.x } }) { override val y: String = "K" diff --git a/compiler/testData/codegen/box/enum/kt7257.kt b/compiler/testData/codegen/box/enum/kt7257.kt index e2fe684b5e1..78dca7e9610 100644 --- a/compiler/testData/codegen/box/enum/kt7257.kt +++ b/compiler/testData/codegen/box/enum/kt7257.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR enum class X { B { val value2 = "K" diff --git a/compiler/testData/codegen/box/enum/kt7257_anonObjectInit.kt b/compiler/testData/codegen/box/enum/kt7257_anonObjectInit.kt index 08002c4e69d..2cc9963d5c6 100644 --- a/compiler/testData/codegen/box/enum/kt7257_anonObjectInit.kt +++ b/compiler/testData/codegen/box/enum/kt7257_anonObjectInit.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR enum class X { B { val value2 = "K" diff --git a/compiler/testData/codegen/box/enum/kt7257_anonObjectMethod.kt b/compiler/testData/codegen/box/enum/kt7257_anonObjectMethod.kt index ed97f2ddcf8..4f5e5ce5bda 100644 --- a/compiler/testData/codegen/box/enum/kt7257_anonObjectMethod.kt +++ b/compiler/testData/codegen/box/enum/kt7257_anonObjectMethod.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR enum class X { B { val value2 = "K" diff --git a/compiler/testData/codegen/box/enum/kt7257_boundReference1.kt b/compiler/testData/codegen/box/enum/kt7257_boundReference1.kt index a7008b03c36..02297ec3f33 100644 --- a/compiler/testData/codegen/box/enum/kt7257_boundReference1.kt +++ b/compiler/testData/codegen/box/enum/kt7257_boundReference1.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR - enum class X { B { val k = "K" diff --git a/compiler/testData/codegen/box/enum/kt7257_boundReference2.kt b/compiler/testData/codegen/box/enum/kt7257_boundReference2.kt index b8613250066..e34bc6403ba 100644 --- a/compiler/testData/codegen/box/enum/kt7257_boundReference2.kt +++ b/compiler/testData/codegen/box/enum/kt7257_boundReference2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR enum class X { B { diff --git a/compiler/testData/codegen/box/enum/kt7257_explicitReceiver.kt b/compiler/testData/codegen/box/enum/kt7257_explicitReceiver.kt index 66adf154b60..44c3183a50a 100644 --- a/compiler/testData/codegen/box/enum/kt7257_explicitReceiver.kt +++ b/compiler/testData/codegen/box/enum/kt7257_explicitReceiver.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR enum class X { B { override val value2 = "K" diff --git a/compiler/testData/codegen/box/enum/kt7257_fullyQualifiedReceiver.kt b/compiler/testData/codegen/box/enum/kt7257_fullyQualifiedReceiver.kt index bba458aeb3c..384e2daa331 100644 --- a/compiler/testData/codegen/box/enum/kt7257_fullyQualifiedReceiver.kt +++ b/compiler/testData/codegen/box/enum/kt7257_fullyQualifiedReceiver.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR enum class X { B { override val value2 = "K" diff --git a/compiler/testData/codegen/box/enum/kt7257_namedLocalFun.kt b/compiler/testData/codegen/box/enum/kt7257_namedLocalFun.kt index 713fe984c25..5438d725fba 100644 --- a/compiler/testData/codegen/box/enum/kt7257_namedLocalFun.kt +++ b/compiler/testData/codegen/box/enum/kt7257_namedLocalFun.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR enum class X { B { val value2 = "K" diff --git a/compiler/testData/codegen/box/enum/kt7257_notInline.kt b/compiler/testData/codegen/box/enum/kt7257_notInline.kt index 69e1882ef59..3851efb5669 100644 --- a/compiler/testData/codegen/box/enum/kt7257_notInline.kt +++ b/compiler/testData/codegen/box/enum/kt7257_notInline.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun T.letNoInline(fn: (T) -> R) = fn(this)