From ed6ca7d67eeed79b494fe38985a66172b698c5b7 Mon Sep 17 00:00:00 2001 From: Georgy Bronnikov Date: Tue, 15 Oct 2019 12:20:02 +0300 Subject: [PATCH] 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. --- .../jvm/lower/SingletonReferencesLowering.kt | 42 +++++++++---------- .../testData/codegen/box/enum/refToThis.kt | 12 ++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 +++ .../LightAnalysisModeTestGenerated.java | 5 +++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 +++ .../IrJsCodegenBoxTestGenerated.java | 5 +++ .../semantics/JsCodegenBoxTestGenerated.java | 5 +++ 7 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 compiler/testData/codegen/box/enum/refToThis.kt 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 0924e663a50..9a6fcce79f7 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,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() - 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.`. Consider the example, diff --git a/compiler/testData/codegen/box/enum/refToThis.kt b/compiler/testData/codegen/box/enum/refToThis.kt new file mode 100644 index 00000000000..77da3bf2133 --- /dev/null +++ b/compiler/testData/codegen/box/enum/refToThis.kt @@ -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" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 7b9885c1ddb..2b715d3f5d8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index d61790f5036..7d16c654fdf 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 4d7006fce84..12d34b755aa 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 1c037bf1edd..aaa4b627def 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -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"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index e7297be060d..506f6ca0362 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -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");