diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 43306926e1e..47490fb6436 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1723,12 +1723,22 @@ public class ExpressionCodegen extends KtVisitor impleme } private boolean shouldGenerateSingletonAsThisOrOuterFromContext(ClassDescriptor classDescriptor) { - return isPossiblyUninitializedSingleton(classDescriptor) && - isInsideSingleton(classDescriptor) && - isThisInitialized(classDescriptor); - } + if (!isPossiblyUninitializedSingleton(classDescriptor)) return false; + if (!isInsideSingleton(classDescriptor)) return false; + + // We are inside a singleton class 'S' with possibly uninitialized static instance + // (enum entry, interface companion object). + // Such singleton can be referenced by name, or as an explicit or implicit 'this'. + // For a given singleton class 'S' we either use 'this@S' from context (local or captured), + // or 'S' as a static instance. + // + // Local or captured 'this@S' should be used if: + // - we are in the constructor for 'S', + // and corresponding instance is initialized by super or delegating constructor call; + // - we are in any other member of 'S' or any of its inner classes. + // + // Otherwise, a static instance should be used. - private boolean isThisInitialized(ClassDescriptor classDescriptor) { CodegenContext context = this.context; while (context != null) { if (context instanceof ConstructorContext) { @@ -1738,9 +1748,24 @@ public class ExpressionCodegen extends KtVisitor impleme return constructorContext.isThisInitialized(); } } + else if (context instanceof ClassContext) { + ClassDescriptor contextClass = ((ClassContext) context).getContextDescriptor(); + if (isInInnerClassesChainFor(contextClass, classDescriptor)) { + return true; + } + } context = context.getParentContext(); } - return true; + return false; + } + + private static boolean isInInnerClassesChainFor(ClassDescriptor innerClass, ClassDescriptor outerClass) { + if (innerClass == outerClass) return true; + if (!innerClass.isInner()) return false; + + DeclarationDescriptor containingDeclaration = innerClass.getContainingDeclaration(); + if (!(containingDeclaration instanceof ClassDescriptor)) return false; + return isInInnerClassesChainFor((ClassDescriptor) containingDeclaration, outerClass); } @Nullable diff --git a/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt new file mode 100644 index 00000000000..135eeffae89 --- /dev/null +++ b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt @@ -0,0 +1,24 @@ +interface IFoo { + fun foo(): String +} + +interface IBar { + fun bar(): String +} + +abstract class Base(val x: IFoo) + +enum class Test : IFoo, IBar { + FOO { + // FOO referenced from inner class constructor with uninitialized 'this' + inner class Inner : Base(FOO) + + val z = Inner() + + override fun foo() = "OK" + + override fun bar() = z.x.foo() + } +} + +fun box() = Test.FOO.bar() \ No newline at end of file diff --git a/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt new file mode 100644 index 00000000000..7745c582462 --- /dev/null +++ b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt @@ -0,0 +1,24 @@ +interface IFoo { + fun foo(): String +} + +interface IBar { + fun bar(): String +} + +enum class Test : IFoo, IBar { + FOO { + // FOO referenced from inner class constructor with initialized 'this' + inner class Inner { + val fooFoo = FOO.foo() + } + + val z = Inner() + + override fun foo() = "OK" + + override fun bar() = z.fooFoo + } +} + +fun box() = Test.FOO.bar() \ No newline at end of file diff --git a/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt new file mode 100644 index 00000000000..635103b8b02 --- /dev/null +++ b/compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt @@ -0,0 +1,23 @@ +interface IFoo { + fun foo(): String +} + +interface IBar { + fun bar(): String +} + +enum class Test : IFoo, IBar { + FOO { + // FOO referenced from inner class constructor with initialized 'this', + // in delegate initializer + inner class Inner : IFoo by FOO + + val z = Inner() + + override fun foo() = "OK" + + override fun bar() = z.foo() + } +} + +fun box() = Test.FOO.bar() \ No newline at end of file diff --git a/compiler/testData/codegen/box/enum/kt20651a.kt b/compiler/testData/codegen/box/enum/kt20651a.kt new file mode 100644 index 00000000000..4bdecd58654 --- /dev/null +++ b/compiler/testData/codegen/box/enum/kt20651a.kt @@ -0,0 +1,8 @@ +enum class Foo( + val x: String, + val callback: () -> String +) { + FOO("OK", { FOO.x }) +} + +fun box() = Foo.FOO.callback() \ No newline at end of file diff --git a/compiler/testData/codegen/box/enum/kt20651b.kt b/compiler/testData/codegen/box/enum/kt20651b.kt new file mode 100644 index 00000000000..477e3036569 --- /dev/null +++ b/compiler/testData/codegen/box/enum/kt20651b.kt @@ -0,0 +1,17 @@ +interface Callback { + fun invoke(): String +} + +enum class Foo( + val x: String, + val callback: Callback +) { + FOO( + "OK", + object : Callback { + override fun invoke() = FOO.x + } + ) +} + +fun box() = Foo.FOO.callback.invoke() \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index faddcb1571a..00e9c4942d9 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -7934,6 +7934,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("enumEntryReferenceFromInnerClassConstructor1.kt") + public void testEnumEntryReferenceFromInnerClassConstructor1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntryReferenceFromInnerClassConstructor2.kt") + public void testEnumEntryReferenceFromInnerClassConstructor2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntryReferenceFromInnerClassConstructor3.kt") + public void testEnumEntryReferenceFromInnerClassConstructor3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt"); + doTest(fileName); + } + @TestMetadata("enumInheritedFromTrait.kt") public void testEnumInheritedFromTrait() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumInheritedFromTrait.kt"); @@ -8012,6 +8030,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("kt20651a.kt") + public void testKt20651a() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt20651a.kt"); + doTest(fileName); + } + + @TestMetadata("kt20651b.kt") + public void testKt20651b() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt20651b.kt"); + doTest(fileName); + } + @TestMetadata("kt2350.kt") public void testKt2350() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt2350.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 6b2b5c174b2..b6aca259fc9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -7934,6 +7934,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("enumEntryReferenceFromInnerClassConstructor1.kt") + public void testEnumEntryReferenceFromInnerClassConstructor1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntryReferenceFromInnerClassConstructor2.kt") + public void testEnumEntryReferenceFromInnerClassConstructor2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntryReferenceFromInnerClassConstructor3.kt") + public void testEnumEntryReferenceFromInnerClassConstructor3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt"); + doTest(fileName); + } + @TestMetadata("enumInheritedFromTrait.kt") public void testEnumInheritedFromTrait() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumInheritedFromTrait.kt"); @@ -8012,6 +8030,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("kt20651a.kt") + public void testKt20651a() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt20651a.kt"); + doTest(fileName); + } + + @TestMetadata("kt20651b.kt") + public void testKt20651b() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt20651b.kt"); + doTest(fileName); + } + @TestMetadata("kt2350.kt") public void testKt2350() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt2350.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index cdcd294165d..6aa9a4fe431 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -7934,6 +7934,24 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("enumEntryReferenceFromInnerClassConstructor1.kt") + public void testEnumEntryReferenceFromInnerClassConstructor1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntryReferenceFromInnerClassConstructor2.kt") + public void testEnumEntryReferenceFromInnerClassConstructor2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntryReferenceFromInnerClassConstructor3.kt") + public void testEnumEntryReferenceFromInnerClassConstructor3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt"); + doTest(fileName); + } + @TestMetadata("enumInheritedFromTrait.kt") public void testEnumInheritedFromTrait() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumInheritedFromTrait.kt"); @@ -8012,6 +8030,18 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("kt20651a.kt") + public void testKt20651a() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt20651a.kt"); + doTest(fileName); + } + + @TestMetadata("kt20651b.kt") + public void testKt20651b() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt20651b.kt"); + doTest(fileName); + } + @TestMetadata("kt2350.kt") public void testKt2350() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt2350.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 ab6030bb277..89b41755d5d 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 @@ -8600,6 +8600,24 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("enumEntryReferenceFromInnerClassConstructor1.kt") + public void testEnumEntryReferenceFromInnerClassConstructor1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor1.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntryReferenceFromInnerClassConstructor2.kt") + public void testEnumEntryReferenceFromInnerClassConstructor2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor2.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntryReferenceFromInnerClassConstructor3.kt") + public void testEnumEntryReferenceFromInnerClassConstructor3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumEntryReferenceFromInnerClassConstructor3.kt"); + doTest(fileName); + } + @TestMetadata("enumInheritedFromTrait.kt") public void testEnumInheritedFromTrait() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumInheritedFromTrait.kt"); @@ -8678,6 +8696,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("kt20651a.kt") + public void testKt20651a() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt20651a.kt"); + doTest(fileName); + } + + @TestMetadata("kt20651b.kt") + public void testKt20651b() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt20651b.kt"); + doTest(fileName); + } + @TestMetadata("kt2350.kt") public void testKt2350() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt2350.kt");