JVM_IR: fix for SingletonReferencesLowering
When replacing an enum entry reference with `this`, you need to take `this` from the function's `dispatchReceiverParameter`, not the class's `thisReceiver`. Otherwise the code generator fails to find the reference among accessible variables.
This commit is contained in:
+19
-23
@@ -5,19 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.*
|
||||
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.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
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.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
internal val singletonReferencesPhase = makeIrFilePhase(
|
||||
@@ -26,13 +21,11 @@ internal val singletonReferencesPhase = makeIrFilePhase(
|
||||
description = "Handle singleton references"
|
||||
)
|
||||
|
||||
private class SingletonReferencesLowering(val context: JvmBackendContext) : ClassLoweringPass, IrElementTransformerVoid() {
|
||||
private lateinit var containingClass: IrClass
|
||||
private class SingletonReferencesLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
|
||||
private val constructingEnums = arrayListOf<IrDeclarationParent>()
|
||||
|
||||
override fun lower(irClass: IrClass) {
|
||||
containingClass = irClass
|
||||
irClass.transformChildrenVoid(this)
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
|
||||
@@ -44,14 +37,14 @@ private class SingletonReferencesLowering(val context: JvmBackendContext) : Clas
|
||||
|
||||
override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression {
|
||||
val candidate = expression.symbol.owner.correspondingClass
|
||||
|
||||
return if (candidate != null && isInScope(candidate) && !isVisitingSuperConstructor(candidate)) {
|
||||
val appropriateThis = thisOfClass(candidate)
|
||||
return if (candidate != null && appropriateThis != null && !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)
|
||||
IrGetValueImpl(expression.startOffset, expression.endOffset, expression.type, appropriateThis.symbol)
|
||||
} else {
|
||||
val entrySymbol = context.declarationFactory.getFieldForEnumEntry(expression.symbol.owner, expression.type)
|
||||
IrGetFieldImpl(expression.startOffset, expression.endOffset, entrySymbol.symbol, expression.type)
|
||||
@@ -64,13 +57,16 @@ private class SingletonReferencesLowering(val context: JvmBackendContext) : Clas
|
||||
}
|
||||
|
||||
// `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
|
||||
private fun thisOfClass(declaration: IrClass?): IrValueParameter? {
|
||||
if (declaration == null) return null
|
||||
for (scope in allScopes.reversed()) {
|
||||
when (val element = scope.irElement) {
|
||||
is IrFunction ->
|
||||
element.dispatchReceiverParameter?.let { if (it.type.classOrNull == declaration.symbol) return it }
|
||||
is IrClass -> if (element == declaration) return element.thisReceiver
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// `this` isn't usable before `super.<init>`. Consider the example,
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
enum class Enum {
|
||||
ENUM_VALUE {
|
||||
override fun test() = ENUM_VALUE
|
||||
};
|
||||
|
||||
abstract fun test(): Enum
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (Enum.ENUM_VALUE.test() != Enum.ENUM_VALUE) return "fail"
|
||||
return "OK"
|
||||
}
|
||||
+5
@@ -10552,6 +10552,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/enum/ordinal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("refToThis.kt")
|
||||
public void testRefToThis() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/refToThis.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/simple.kt");
|
||||
|
||||
+5
@@ -10552,6 +10552,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/enum/ordinal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("refToThis.kt")
|
||||
public void testRefToThis() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/refToThis.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/simple.kt");
|
||||
|
||||
+5
@@ -9437,6 +9437,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/enum/ordinal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("refToThis.kt")
|
||||
public void testRefToThis() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/refToThis.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/simple.kt");
|
||||
|
||||
Generated
+5
@@ -8177,6 +8177,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/enum/ordinal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("refToThis.kt")
|
||||
public void testRefToThis() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/refToThis.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/simple.kt");
|
||||
|
||||
+5
@@ -9262,6 +9262,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/enum/ordinal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("refToThis.kt")
|
||||
public void testRefToThis() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/refToThis.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/simple.kt");
|
||||
|
||||
Reference in New Issue
Block a user