diff --git a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersBlocksAndObject.kt b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersBlocksAndObject.kt index ce7576c5e4455..51c27e351b7d9 100644 --- a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersBlocksAndObject.kt +++ b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersBlocksAndObject.kt @@ -1,5 +1,5 @@ // LANGUAGE: +CompanionBlocksAndExtensions -// IGNORE_BACKEND: JVM, JVM_IR, NATIVE +// IGNORE_BACKEND: JVM, JVM_IR var initOrder = "" diff --git a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersEnumImplementingInterfaceWithDefaultMember.kt b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersEnumImplementingInterfaceWithDefaultMember.kt index 61f24ca971f00..15fbbc4e6645a 100644 --- a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersEnumImplementingInterfaceWithDefaultMember.kt +++ b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersEnumImplementingInterfaceWithDefaultMember.kt @@ -1,5 +1,5 @@ // LANGUAGE: +CompanionBlocksAndExtensions -// IGNORE_BACKEND: JVM_IR, NATIVE +// IGNORE_BACKEND: JVM_IR var initOrder = "" diff --git a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritance.kt b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritance.kt index 98f395948850d..d89ee9926c62e 100644 --- a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritance.kt +++ b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritance.kt @@ -1,5 +1,4 @@ // LANGUAGE: +CompanionBlocksAndExtensions -// IGNORE_BACKEND: NATIVE var initOrder = "" diff --git a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMixedWithObject.kt b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMixedWithObject.kt index 439b0fe0c01a7..c0cbb94a832c8 100644 --- a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMixedWithObject.kt +++ b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMixedWithObject.kt @@ -1,5 +1,5 @@ // LANGUAGE: +CompanionBlocksAndExtensions -// IGNORE_BACKEND: JVM, JVM_IR, NATIVE +// IGNORE_BACKEND: JVM, JVM_IR var initOrder = "" diff --git a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultiLevel.kt b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultiLevel.kt index 40c29785c9720..4eb1248d32040 100644 --- a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultiLevel.kt +++ b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultiLevel.kt @@ -1,5 +1,4 @@ // LANGUAGE: +CompanionBlocksAndExtensions -// IGNORE_BACKEND: NATIVE var initOrder = "" diff --git a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultipleSupertypes.kt b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultipleSupertypes.kt index 36890e9c580f9..96a5616797aa7 100644 --- a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultipleSupertypes.kt +++ b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultipleSupertypes.kt @@ -1,5 +1,5 @@ // LANGUAGE: +CompanionBlocksAndExtensions -// IGNORE_BACKEND: JVM_IR, NATIVE +// IGNORE_BACKEND: JVM_IR var initOrder = "" diff --git a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultipleSupertypesMixedOrder.kt b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultipleSupertypesMixedOrder.kt index fdc11234e30ce..0bb9cc0c21f06 100644 --- a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultipleSupertypesMixedOrder.kt +++ b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersInheritanceMultipleSupertypesMixedOrder.kt @@ -1,5 +1,5 @@ // LANGUAGE: +CompanionBlocksAndExtensions -// IGNORE_BACKEND: JVM_IR, NATIVE +// IGNORE_BACKEND: JVM_IR // This test is essentially the same one as initializersInheritanceMultipleSupertypes, // but with different super type kinds order (interface, class, interface) @@ -59,11 +59,11 @@ fun box(): String { if (Foo.fooProp2 != "foo2") return "FAIL: fooProp2=${Foo.fooProp2}" // Order: superclass, then superinterfaces (declaration order), then Foo itself. - if (initOrder != "IF2 BAR1 BAR2 IF1 FOO1 FOO2 ") return "FAIL: initOrder=$initOrder" + if (initOrder != "BAR1 BAR2 IF2 IF1 FOO1 FOO2 ") return "FAIL: initOrder=$initOrder" // Accessing companion members again must not re-run any initializer. if (Foo.fooProp2 != "foo2") return "FAIL: fooProp2 (second access)=${Foo.fooProp2}" - if (initOrder != "IF2 BAR1 BAR2 IF1 FOO1 FOO2 ") return "FAIL: initOrder after re-access=$initOrder" + if (initOrder != "BAR1 BAR2 IF2 IF1 FOO1 FOO2 ") return "FAIL: initOrder after re-access=$initOrder" return "OK" } diff --git a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersTriggeredByObject.kt b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersTriggeredByObject.kt index 0868b5afcd049..5b1172e508b3e 100644 --- a/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersTriggeredByObject.kt +++ b/compiler/testData/codegen/box/companionBlocksAndExtensions/initializersTriggeredByObject.kt @@ -1,5 +1,5 @@ // LANGUAGE: +CompanionBlocksAndExtensions -// IGNORE_BACKEND: JVM, JVM_IR, NATIVE +// IGNORE_BACKEND: JVM, JVM_IR var initOrder = "" diff --git a/compiler/testData/codegen/box/objects/companionObjectAccess/companionInitOrderWithSuperclassCrossModule.kt b/compiler/testData/codegen/box/objects/companionObjectAccess/companionInitOrderWithSuperclassCrossModule.kt index f67d476217981..33c62c890ea0f 100644 --- a/compiler/testData/codegen/box/objects/companionObjectAccess/companionInitOrderWithSuperclassCrossModule.kt +++ b/compiler/testData/codegen/box/objects/companionObjectAccess/companionInitOrderWithSuperclassCrossModule.kt @@ -1,5 +1,7 @@ // ISSUE: KT-86521 Native: init order of companion objects is different from JVM // IGNORE_BACKEND: NATIVE +// ^ On Native `CompanionBlocksAndExtensions` language feature enables the JVM-like initialization. +// See nativeCompanionInitOrderCrossModule* tests for Native-specific treatment. // ISSUE: KT-84267 K/Wasm: init order of companion objects is different from JVM // ISSUE: KT-86640 K/Wasm: cross-module signature mismatch for parent companion getInstance // IGNORE_KLIB_RUNTIME_ERRORS_WITH_CUSTOM_SECOND_STAGE: Wasm-js:2.3,2.4 diff --git a/compiler/testData/codegen/box/objects/companionObjectAccess/companionObjectInitOrderWithSuperclass.kt b/compiler/testData/codegen/box/objects/companionObjectAccess/companionObjectInitOrderWithSuperclass.kt index 3b5c45a07d510..dd69c24fea958 100644 --- a/compiler/testData/codegen/box/objects/companionObjectAccess/companionObjectInitOrderWithSuperclass.kt +++ b/compiler/testData/codegen/box/objects/companionObjectAccess/companionObjectInitOrderWithSuperclass.kt @@ -1,5 +1,7 @@ // ISSUE: KT-86521 Native: init order of companion objects is different from JVM // IGNORE_BACKEND: NATIVE +// ^ On Native `CompanionBlocksAndExtensions` language feature enables the JVM-like initialization. +// See nativeCompanionInitOrder* tests for Native-specific treatment. // ISSUE: KT-84267 K/Wasm: init order of companion objects is different from JVM // IGNORE_KLIB_RUNTIME_ERRORS_WITH_CUSTOM_SECOND_STAGE: Wasm-js:2.3,2.4 // ^^^KT-84267 is fixed in 2.4.20-beta1 @@ -189,6 +191,62 @@ class A16 : B16() { companion object { init { log("A16.Companion") } } } +// multiple interface inheritance +interface I17 { + fun i() {} + companion object { init { log("I17.Companion") } } +} +interface J17 { + fun j() {} + companion object { init { log("J17.Companion") } } +} +interface K17 : I17 { + fun k() {} + companion object { init { log("K17.Companion") } } +} +interface L17 : J17 { + fun l() {} + companion object { init { log("L17.Companion") } } +} +interface M17 { + companion object { init { log("M17.Companion") } } +} +open class B17 : J17, K17 { + companion object { init { log("B17.Companion") } } +} +class A17: B17(), L17, M17 { + companion object { init { log("A17.Companion") } } +} + +// multiple interface inheritance; with instance creation +interface I18 { + fun i() {} + companion object { init { log("I18.Companion") } } +} +interface J18 { + fun j() {} + companion object { init { log("J18.Companion") } } +} +interface K18 : I18 { + fun k() {} + companion object { init { log("K18.Companion") } } +} +interface L18 : J18 { + fun l() {} + companion object { init { log("L18.Companion") } } +} +interface M18 { + companion object { init { log("M18.Companion") } } +} +open class B18 : J18, K18 { + init { log("B18.init") } + companion object { init { log("B18.Companion") } } +} +class A18: B18(), L18, M18 { + init { log("A18.init") } + companion object { init { log("A18.Companion") } } +} + fun box(): String { l = "" A1 @@ -279,5 +337,15 @@ fun box(): String { val r16 = l if (r16 != "B16.Companion\nA16.Companion\n") return "fail test16: '$r16'" + l = "" + A17 + val r17 = l + if (r17 != "J17.Companion\nI17.Companion\nK17.Companion\nB17.Companion\nL17.Companion\nA17.Companion\n") return "fail test17: '$r17'" + + l = "" + A18() + val r18 = l + if (r18 != "J18.Companion\nI18.Companion\nK18.Companion\nB18.Companion\nL18.Companion\nA18.Companion\nB18.init\nA18.init\n") return "fail test18: '$r18'" + return "OK" } diff --git a/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrder.kt b/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrder.kt new file mode 100644 index 0000000000000..5506bb252dd28 --- /dev/null +++ b/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrder.kt @@ -0,0 +1,348 @@ +// See companionInitOrderWithSuperclass for the common treatment. +// TARGET_BACKEND: NATIVE +// LANGUAGE: +CompanionBlocksAndExtensions +// With this language feature, the class initialization behaviour should be exactly the same as on JVM. + +var l = "" +private fun log(t: String) { + l += t + "\n" +} + +private fun logValue(t: String): String { + log(t) + return t +} + +// Each test uses its own class hierarchy so companions are initialized fresh. + +// companion-only access (no instance created) +open class B1 { + init { log("B1.init#1") } + companion object { init { log("B1.Companion") } } + init { log("B1.init#2") } +} +class A1 : B1() { + init { log("A1.init#1") } + companion object { init { log("A1.Companion") } } + init { log("A1.init#2") } +} + +// instance creation (triggers both companions then instance inits) +open class B2 { + init { log("B2.init#1") } + companion object { init { log("B2.Companion") } } + init { log("B2.init#2") } +} +class A2 : B2() { + init { log("A2.init#1") } + companion object { init { log("A2.Companion") } } + init { log("A2.init#2") } +} + +// companion access then instance creation +open class B3 { + init { log("B3.init#1") } + companion object { init { log("B3.Companion") } } + init { log("B3.init#2") } +} +class A3 : B3() { + init { log("A3.init#1") } + companion object { init { log("A3.Companion") } } + init { log("A3.init#2") } +} + +// instance creation then companion access +open class B4 { + init { log("B4.init#1") } + companion object { init { log("B4.Companion") } } + init { log("B4.init#2") } +} +class A4 : B4() { + init { log("A4.init#1") } + companion object { init { log("A4.Companion") } } + init { log("A4.init#2") } +} + +// 3-level hierarchy with companion access only +open class C5 { + companion object { init { log("C5.Companion") } } +} +open class B5 : C5() { + companion object { init { log("B5.Companion") } } +} +class A5 : B5() { + companion object { init { log("A5.Companion") } } +} + +// 3-level hierarchy with instance creation +open class C6 { + init { log("C6.init") } + companion object { init { log("C6.Companion") } } +} +open class B6 : C6() { + init { log("B6.init") } + companion object { init { log("B6.Companion") } } +} +class A6 : B6() { + init { log("A6.init") } + companion object { init { log("A6.Companion") } } +} + +// intermediate class with no companion, companion access only otherwise. +open class C7 { + companion object { init { log("C7.Companion") } } +} +open class B7 : C7() // no companion +class A7 : B7() { + companion object { init { log("A7.Companion") } } +} + +// intermediate class with no companion; instance creation +open class C8 { + init { log("C8.init") } + companion object { init { log("C8.Companion") } } +} +open class B8 : C8() // no companion +class A8 : B8() { + init { log("A8.init") } + companion object { init { log("A8.Companion") } } +} + +// parent companion access and repeated child companion access +open class B9 { + companion object { init { log("B9.Companion") } } +} +class A9 : B9() { + companion object { init { log("A9.Companion") } } +} + +// named private parent companion +open class B10 { + private companion object Parent { init { log("B10.Parent") } } +} +class A10 : B10() { + companion object Child { init { log("A10.Child") } } +} + +// child companion initializer reads from parent companion +open class B11 { + companion object { + val value = logValue("B11.Companion") + } +} +class A11 : B11() { + companion object { + val value = B11.value + "/" + logValue("A11.Companion") + } +} + +// superinterface companions are not superclass companions +interface I12 { + companion object { init { log("I12.Companion") } } +} +class A12 : I12 { + companion object { init { log("A12.Companion") } } +} + +// child companion object has its own superclass +open class B13 { + companion object { init { log("B13.Companion") } } +} +open class CompanionBase13 { + init { log("CompanionBase13.init") } +} +class A13 : B13() { + companion object : CompanionBase13() { + init { log("A13.Companion") } + } +} + +// two intermediate classes with no companion +open class C14 { + companion object { init { log("C14.Companion") } } +} +open class D14 : C14() +open class B14 : D14() +class A14 : B14() { + companion object { init { log("A14.Companion") } } +} + +// superclass and superinterface both have companions +interface I15 { + companion object { + val marker = logValue("I15.Companion") + } +} +open class B15 { + companion object { init { log("B15.Companion") } } +} +class A15 : B15(), I15 { + companion object { init { log("A15.Companion") } } +} + +// generic superclass +open class B16 { + companion object { init { log("B16.Companion") } } +} +class A16 : B16() { + companion object { init { log("A16.Companion") } } +} + +// multiple interface inheritance +interface I17 { + fun i() {} + companion object { init { log("I17.Companion") } } +} +interface J17 { + fun j() {} + companion object { init { log("J17.Companion") } } +} +interface K17 : I17 { + fun k() {} + companion object { init { log("K17.Companion") } } +} +interface L17 : J17 { + fun l() {} + companion object { init { log("L17.Companion") } } +} +interface M17 { + companion object { init { log("M17.Companion") } } +} +open class B17 : J17, K17 { + companion object { init { log("B17.Companion") } } +} +class A17: B17(), L17, M17 { + companion object { init { log("A17.Companion") } } +} + +// multiple interface inheritance; with instance creation +interface I18 { + fun i() {} + companion object { init { log("I18.Companion") } } +} +interface J18 { + fun j() {} + companion object { init { log("J18.Companion") } } +} +interface K18 : I18 { + fun k() {} + companion object { init { log("K18.Companion") } } +} +interface L18 : J18 { + fun l() {} + companion object { init { log("L18.Companion") } } +} +interface M18 { + companion object { init { log("M18.Companion") } } +} +open class B18 : J18, K18 { + init { log("B18.init") } + companion object { init { log("B18.Companion") } } +} +class A18: B18(), L18, M18 { + init { log("A18.init") } + companion object { init { log("A18.Companion") } } +} + +fun box(): String { + l = "" + A1 + val r1 = l + if (r1 != "B1.Companion\nA1.Companion\n") return "fail test1: '$r1'" + + l = "" + A2() + val r2 = l + if (r2 != "B2.Companion\nA2.Companion\nB2.init#1\nB2.init#2\nA2.init#1\nA2.init#2\n") return "fail test2: '$r2'" + + l = "" + A3 + log("--") + A3() + val r3 = l + if (r3 != "B3.Companion\nA3.Companion\n--\nB3.init#1\nB3.init#2\nA3.init#1\nA3.init#2\n") return "fail test3: '$r3'" + + l = "" + A4() + log("--") + A4 + val r4 = l + if (r4 != "B4.Companion\nA4.Companion\nB4.init#1\nB4.init#2\nA4.init#1\nA4.init#2\n--\n") return "fail test4: '$r4'" + + l = "" + A5 + val r5 = l + if (r5 != "C5.Companion\nB5.Companion\nA5.Companion\n") return "fail test5: '$r5'" + + l = "" + A6() + val r6 = l + if (r6 != "C6.Companion\nB6.Companion\nA6.Companion\nC6.init\nB6.init\nA6.init\n") return "fail test6: '$r6'" + + l = "" + A7 + val r7 = l + if (r7 != "C7.Companion\nA7.Companion\n") return "fail test7: '$r7'" + + l = "" + A8() + val r8 = l + if (r8 != "C8.Companion\nA8.Companion\nC8.init\nA8.init\n") return "fail test8: '$r8'" + + l = "" + B9 + A9 + A9 + B9 + val r9 = l + if (r9 != "B9.Companion\nA9.Companion\n") return "fail test9: '$r9'" + + l = "" + A10 + val r10 = l + if (r10 != "B10.Parent\nA10.Child\n") return "fail test10: '$r10'" + + l = "" + if (A11.value != "B11.Companion/A11.Companion") return "fail test11 value: '${A11.value}'" + val r11 = l + if (r11 != "B11.Companion\nA11.Companion\n") return "fail test11: '$r11'" + + l = "" + A12 + val r12 = l + if (r12 != "A12.Companion\n") return "fail test12: '$r12'" + + l = "" + A13 + val r13 = l + if (r13 != "B13.Companion\nCompanionBase13.init\nA13.Companion\n") return "fail test13: '$r13'" + + l = "" + A14 + val r14 = l + if (r14 != "C14.Companion\nA14.Companion\n") return "fail test14: '$r14'" + + l = "" + A15 + log("--") + if (I15.marker != "I15.Companion") return "fail test15 marker" + val r15 = l + if (r15 != "B15.Companion\nA15.Companion\n--\nI15.Companion\n") return "fail test15: '$r15'" + + l = "" + A16 + val r16 = l + if (r16 != "B16.Companion\nA16.Companion\n") return "fail test16: '$r16'" + + l = "" + A17 + val r17 = l + if (r17 != "J17.Companion\nI17.Companion\nK17.Companion\nB17.Companion\nL17.Companion\nA17.Companion\n") return "fail test17: '$r17'" + + l = "" + A18() + val r18 = l + if (r18 != "J18.Companion\nI18.Companion\nK18.Companion\nB18.Companion\nL18.Companion\nA18.Companion\nB18.init\nA18.init\n") return "fail test18: '$r18'" + + return "OK" +} diff --git a/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrderCrossModule.kt b/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrderCrossModule.kt new file mode 100644 index 0000000000000..f6852ce12c695 --- /dev/null +++ b/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrderCrossModule.kt @@ -0,0 +1,38 @@ +// See companionInitOrderWithSuperclassCrossModule for the common treatment +// TARGET_BACKEND: NATIVE +// LANGUAGE: +CompanionBlocksAndExtensions +// With this language feature, the class initialization behaviour should be exactly the same as on JVM. + +// MODULE: lib +// FILE: lib.kt + +package lib + +var initLog = "" + +fun log(t: String) { + initLog += t + "\n" +} + +open class Root { + companion object { init { log("Root.Companion") } } +} + +open class Middle : Root() + +// MODULE: main(lib) +// FILE: main.kt + +import lib.Middle +import lib.initLog + +class Leaf : Middle() { + companion object { init { lib.log("Leaf.Companion") } } +} + +fun box(): String { + initLog = "" + Leaf + val result = initLog + return if (result == "Root.Companion\nLeaf.Companion\n") "OK" else "fail: '$result'" +} diff --git a/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrderCrossModuleLegacy.kt b/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrderCrossModuleLegacy.kt new file mode 100644 index 0000000000000..358b18578690c --- /dev/null +++ b/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrderCrossModuleLegacy.kt @@ -0,0 +1,38 @@ +// See companionInitOrderWithSuperclassCrossModule for the common treatment +// TARGET_BACKEND: NATIVE +// LANGUAGE: -CompanionBlocksAndExtensions +// Without this language feature, the initialization order on Native does not include recursive initialization of superclasses and superinterfaces. + +// MODULE: lib +// FILE: lib.kt + +package lib + +var initLog = "" + +fun log(t: String) { + initLog += t + "\n" +} + +open class Root { + companion object { init { log("Root.Companion") } } +} + +open class Middle : Root() + +// MODULE: main(lib) +// FILE: main.kt + +import lib.Middle +import lib.initLog + +class Leaf : Middle() { + companion object { init { lib.log("Leaf.Companion") } } +} + +fun box(): String { + initLog = "" + Leaf + val result = initLog + return if (result == "Root.Companion\nLeaf.Companion\n") "OK" else "fail: '$result'" +} diff --git a/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrderLegacy.kt b/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrderLegacy.kt new file mode 100644 index 0000000000000..14a3c4545bdd2 --- /dev/null +++ b/compiler/testData/codegen/box/objects/companionObjectAccess/nativeCompanionInitOrderLegacy.kt @@ -0,0 +1,348 @@ +// See companionInitOrderWithSuperclass for the common treatment. +// TARGET_BACKEND: NATIVE +// LANGUAGE: -CompanionBlocksAndExtensions +// Without this language feature, the initialization order on Native does not include recursive initialization of superclasses and superinterfaces. + +var l = "" +private fun log(t: String) { + l += t + "\n" +} + +private fun logValue(t: String): String { + log(t) + return t +} + +// Each test uses its own class hierarchy so companions are initialized fresh. + +// companion-only access (no instance created) +open class B1 { + init { log("B1.init#1") } + companion object { init { log("B1.Companion") } } + init { log("B1.init#2") } +} +class A1 : B1() { + init { log("A1.init#1") } + companion object { init { log("A1.Companion") } } + init { log("A1.init#2") } +} + +// instance creation (triggers both companions then instance inits) +open class B2 { + init { log("B2.init#1") } + companion object { init { log("B2.Companion") } } + init { log("B2.init#2") } +} +class A2 : B2() { + init { log("A2.init#1") } + companion object { init { log("A2.Companion") } } + init { log("A2.init#2") } +} + +// companion access then instance creation +open class B3 { + init { log("B3.init#1") } + companion object { init { log("B3.Companion") } } + init { log("B3.init#2") } +} +class A3 : B3() { + init { log("A3.init#1") } + companion object { init { log("A3.Companion") } } + init { log("A3.init#2") } +} + +// instance creation then companion access +open class B4 { + init { log("B4.init#1") } + companion object { init { log("B4.Companion") } } + init { log("B4.init#2") } +} +class A4 : B4() { + init { log("A4.init#1") } + companion object { init { log("A4.Companion") } } + init { log("A4.init#2") } +} + +// 3-level hierarchy with companion access only +open class C5 { + companion object { init { log("C5.Companion") } } +} +open class B5 : C5() { + companion object { init { log("B5.Companion") } } +} +class A5 : B5() { + companion object { init { log("A5.Companion") } } +} + +// 3-level hierarchy with instance creation +open class C6 { + init { log("C6.init") } + companion object { init { log("C6.Companion") } } +} +open class B6 : C6() { + init { log("B6.init") } + companion object { init { log("B6.Companion") } } +} +class A6 : B6() { + init { log("A6.init") } + companion object { init { log("A6.Companion") } } +} + +// intermediate class with no companion, companion access only otherwise. +open class C7 { + companion object { init { log("C7.Companion") } } +} +open class B7 : C7() // no companion +class A7 : B7() { + companion object { init { log("A7.Companion") } } +} + +// intermediate class with no companion; instance creation +open class C8 { + init { log("C8.init") } + companion object { init { log("C8.Companion") } } +} +open class B8 : C8() // no companion +class A8 : B8() { + init { log("A8.init") } + companion object { init { log("A8.Companion") } } +} + +// parent companion access and repeated child companion access +open class B9 { + companion object { init { log("B9.Companion") } } +} +class A9 : B9() { + companion object { init { log("A9.Companion") } } +} + +// named private parent companion +open class B10 { + private companion object Parent { init { log("B10.Parent") } } +} +class A10 : B10() { + companion object Child { init { log("A10.Child") } } +} + +// child companion initializer reads from parent companion +open class B11 { + companion object { + val value = logValue("B11.Companion") + } +} +class A11 : B11() { + companion object { + val value = B11.value + "/" + logValue("A11.Companion") + } +} + +// superinterface companions are not superclass companions +interface I12 { + companion object { init { log("I12.Companion") } } +} +class A12 : I12 { + companion object { init { log("A12.Companion") } } +} + +// child companion object has its own superclass +open class B13 { + companion object { init { log("B13.Companion") } } +} +open class CompanionBase13 { + init { log("CompanionBase13.init") } +} +class A13 : B13() { + companion object : CompanionBase13() { + init { log("A13.Companion") } + } +} + +// two intermediate classes with no companion +open class C14 { + companion object { init { log("C14.Companion") } } +} +open class D14 : C14() +open class B14 : D14() +class A14 : B14() { + companion object { init { log("A14.Companion") } } +} + +// superclass and superinterface both have companions +interface I15 { + companion object { + val marker = logValue("I15.Companion") + } +} +open class B15 { + companion object { init { log("B15.Companion") } } +} +class A15 : B15(), I15 { + companion object { init { log("A15.Companion") } } +} + +// generic superclass +open class B16 { + companion object { init { log("B16.Companion") } } +} +class A16 : B16() { + companion object { init { log("A16.Companion") } } +} + +// multiple interface inheritance +interface I17 { + fun i() {} + companion object { init { log("I17.Companion") } } +} +interface J17 { + fun j() {} + companion object { init { log("J17.Companion") } } +} +interface K17 : I17 { + fun k() {} + companion object { init { log("K17.Companion") } } +} +interface L17 : J17 { + fun l() {} + companion object { init { log("L17.Companion") } } +} +interface M17 { + companion object { init { log("M17.Companion") } } +} +open class B17 : J17, K17 { + companion object { init { log("B17.Companion") } } +} +class A17: B17(), L17, M17 { + companion object { init { log("A17.Companion") } } +} + +// multiple interface inheritance; with instance creation +interface I18 { + fun i() {} + companion object { init { log("I18.Companion") } } +} +interface J18 { + fun j() {} + companion object { init { log("J18.Companion") } } +} +interface K18 : I18 { + fun k() {} + companion object { init { log("K18.Companion") } } +} +interface L18 : J18 { + fun l() {} + companion object { init { log("L18.Companion") } } +} +interface M18 { + companion object { init { log("M18.Companion") } } +} +open class B18 : J18, K18 { + init { log("B18.init") } + companion object { init { log("B18.Companion") } } +} +class A18: B18(), L18, M18 { + init { log("A18.init") } + companion object { init { log("A18.Companion") } } +} + +fun box(): String { + l = "" + A1 + val r1 = l + if (r1 != "A1.Companion\n") return "fail test1: '$r1'" + + l = "" + A2() + val r2 = l + if (r2 != "A2.Companion\nB2.Companion\nB2.init#1\nB2.init#2\nA2.init#1\nA2.init#2\n") return "fail test2: '$r2'" + + l = "" + A3 + log("--") + A3() + val r3 = l + if (r3 != "A3.Companion\n--\nB3.Companion\nB3.init#1\nB3.init#2\nA3.init#1\nA3.init#2\n") return "fail test3: '$r3'" + + l = "" + A4() + log("--") + A4 + val r4 = l + if (r4 != "A4.Companion\nB4.Companion\nB4.init#1\nB4.init#2\nA4.init#1\nA4.init#2\n--\n") return "fail test4: '$r4'" + + l = "" + A5 + val r5 = l + if (r5 != "A5.Companion\n") return "fail test5: '$r5'" + + l = "" + A6() + val r6 = l + if (r6 != "A6.Companion\nB6.Companion\nC6.Companion\nC6.init\nB6.init\nA6.init\n") return "fail test6: '$r6'" + + l = "" + A7 + val r7 = l + if (r7 != "A7.Companion\n") return "fail test7: '$r7'" + + l = "" + A8() + val r8 = l + if (r8 != "A8.Companion\nC8.Companion\nC8.init\nA8.init\n") return "fail test8: '$r8'" + + l = "" + B9 + A9 + A9 + B9 + val r9 = l + if (r9 != "B9.Companion\nA9.Companion\n") return "fail test9: '$r9'" + + l = "" + A10 + val r10 = l + if (r10 != "A10.Child\n") return "fail test10: '$r10'" + + l = "" + if (A11.value != "B11.Companion/A11.Companion") return "fail test11 value: '${A11.value}'" + val r11 = l + if (r11 != "B11.Companion\nA11.Companion\n") return "fail test11: '$r11'" + + l = "" + A12 + val r12 = l + if (r12 != "A12.Companion\n") return "fail test12: '$r12'" + + l = "" + A13 + val r13 = l + if (r13 != "CompanionBase13.init\nA13.Companion\n") return "fail test13: '$r13'" + + l = "" + A14 + val r14 = l + if (r14 != "A14.Companion\n") return "fail test14: '$r14'" + + l = "" + A15 + log("--") + if (I15.marker != "I15.Companion") return "fail test15 marker" + val r15 = l + if (r15 != "A15.Companion\n--\nI15.Companion\n") return "fail test15: '$r15'" + + l = "" + A16 + val r16 = l + if (r16 != "A16.Companion\n") return "fail test16: '$r16'" + + l = "" + A17 + val r17 = l + if (r17 != "A17.Companion\n") return "fail test17: '$r17'" + + l = "" + A18() + val r18 = l + if (r18 != "A18.Companion\nB18.Companion\nB18.init\nA18.init\n") return "fail test18: '$r18'" + + return "OK" +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ObjectClassLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ObjectClassLowering.kt index 47ba366734dbd..c4a4eabbc91f6 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ObjectClassLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ObjectClassLowering.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName import org.jetbrains.kotlin.backend.konan.ir.buildSimpleAnnotation import org.jetbrains.kotlin.backend.konan.ir.isUnit +import org.jetbrains.kotlin.backend.konan.ir.konanLibrary import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* @@ -22,6 +23,7 @@ import org.jetbrains.kotlin.ir.irAttribute import org.jetbrains.kotlin.ir.objcinterop.* import org.jetbrains.kotlin.ir.types.isAny import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.library.newCompanionInitializationEnabled import org.jetbrains.kotlin.utils.addToStdlib.getOrSetIfNull private var IrClass.objectClassInstanceFunction: IrSimpleFunction? by irAttribute(copyByDefault = false) @@ -77,11 +79,18 @@ internal class ObjectClassLowering(val generationState: NativeGenerationState) : declaration ) } - declaration.declarations.singleOrNull { (it as? IrClass)?.isCompanion == true }?.let { - processObjectClass( - it as IrClass, - declaration - ) + val companionIndex = declaration.declarations.indexOfFirst { (it as? IrClass)?.isCompanion == true } + if (companionIndex != -1) { + val companionDeclaration = declaration.declarations[companionIndex] as IrClass + val addAtIndex = if (declaration.konanLibrary?.newCompanionInitializationEnabled == true) { + // With the initialization order, the `companion object` and `companion` blocks should be initialized + // following the program order. So, the field holding `companion object` instance should be placed + // next to the `companion object` to preserve the program order. + companionIndex + 1 + } else { + -1 + } + processObjectClass(companionDeclaration, declaration, addAtIndex) } return declaration } @@ -106,11 +115,16 @@ internal class ObjectClassLowering(val generationState: NativeGenerationState) : fun processObjectClass( declaration: IrClass, - classToAdd: IrClass + classToAdd: IrClass, + addAtIndex: Int = -1, ) { val function = context.getObjectClassInstanceFunction(declaration) val property = function.correspondingPropertySymbol!!.owner - classToAdd.declarations.add(property) + if (addAtIndex == -1) { + classToAdd.declarations.add(property) + } else { + classToAdd.declarations.add(addAtIndex, property) + } property.addBackingField { isFinal = true diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StaticInitializersLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StaticInitializersLowering.kt index 5d4830ce1b034..839f448301b9b 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StaticInitializersLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StaticInitializersLowering.kt @@ -9,9 +9,12 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.binaryTypeIsReference +import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny +import org.jetbrains.kotlin.backend.konan.ir.konanLibrary import org.jetbrains.kotlin.backend.konan.llvm.FieldStorageKind import org.jetbrains.kotlin.backend.konan.llvm.storageKind import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.declarations.buildFun @@ -20,11 +23,17 @@ import org.jetbrains.kotlin.ir.builders.irNull import org.jetbrains.kotlin.ir.builders.irSetField import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.irAttribute +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.types.classOrFail import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.library.newCompanionInitializationEnabled import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addToStdlib.getOrSetIfNull import org.jetbrains.kotlin.utils.addToStdlib.runIf internal object StaticInitializersOrigins { @@ -60,6 +69,8 @@ internal fun ConfigChecks.shouldBeInitializedEagerly(irField: IrField): Boolean return annotations.hasAnnotation(KonanFqNames.eagerInitialization) } +internal var IrClass.clinitTriggerFunction: IrSimpleFunctionSymbol? by irAttribute(copyByDefault = true) + internal class StaticInitializersLowering(val context: Context) : FileLoweringPass { override fun lower(irFile: IrFile) { irFile.acceptVoid(object : IrVisitorVoid() { @@ -71,12 +82,28 @@ internal class StaticInitializersLowering(val context: Context) : FileLoweringPa declaration.acceptChildrenVoid(this) } override fun visitClass(declaration: IrClass) { + declaration.addChild(declaration.getClinitTriggerFunction().owner.apply { + body = context.irFactory.createBlockBody(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET) + }) processDeclarationContainter(declaration) declaration.acceptChildrenVoid(this) } }) } + // An internal static method to trigger class initialization: + // * the regular rules of this lowering will insert calls to the actual initializers inside the body + // * initializers of other classes will emit calls to this trigger, when needed + private fun IrClass.getClinitTriggerFunction() = ::clinitTriggerFunction.getOrSetIfNull { + context.irFactory.buildFun { + name = Name.identifier($$"$clinit_trigger") + visibility = DescriptorVisibilities.PUBLIC + returnType = context.irBuiltIns.unitType + }.apply { + parent = this@getClinitTriggerFunction + }.symbol + } + private fun IrStatement.isConst(): Boolean = when (this) { is IrConst, is IrConstantValue -> true is IrBlock -> { @@ -99,6 +126,52 @@ internal class StaticInitializersLowering(val context: Context) : FileLoweringPa val builder = context.irBuiltIns.createIrBuilder((container as IrSymbolOwner).symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET) + if (container is IrClass && !container.isInterface && container.konanLibrary?.newCompanionInitializationEnabled == true) { + // Implemented as defined in + // https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-5.html#jvms-5.5 + // Next, if C is a class rather than an interface, then let SC be its superclass and let SI1, ..., SIn be all + // superinterfaces of C (whether direct or indirect) that declare at least one non-abstract, non-static method. + // The order of superinterfaces is given by a recursive enumeration over the superinterface hierarchy of each interface + // directly implemented by C. For each interface I directly implemented by C (in the order of the interfaces array of C), + // the enumeration recurs on I's superinterfaces (in the order of the interfaces array of I) before returning I. + // For each S in the list [ SC, SI1, ..., SIn ], if S has not yet been initialized, then recursively perform this entire + // procedure for S. If necessary, verify and prepare S first. + + val superClassesToInitialize = buildList { + container.getSuperClassNotAny()?.let { add(it) } + fun IrDeclaration.triggersInterfaceInitialization(): Boolean { + if (this !is IrOverridableDeclaration<*>) return false + if (isFakeOverride) return false + if (modality == Modality.ABSTRACT) return false + if (this is IrSimpleFunction && !isStatic) return true + if (this is IrProperty && (getter?.isStatic ?: backingField?.isStatic) != true) return true + return false + } + + fun IrClass.needsInitializationAsInterface() = isInterface && declarations.any { it.triggersInterfaceInitialization() } + val seen = mutableSetOf() + fun IrClass.collectSuperInterfacesToInitialize() { + if (!seen.add(symbol)) + return + for (superType in superTypes) { + val superClass = superType.classOrFail.owner + if (superClass.isInterface) { + superClass.collectSuperInterfacesToInitialize() + } + } + if (needsInitializationAsInterface()) { + add(this) + } + } + container.collectSuperInterfacesToInitialize() + } + for (superClass in superClassesToInitialize) { + val trigger = superClass.getClinitTriggerFunction() + globalInitializers.add(builder.irCall(trigger)) + threadLocalInitializers.add(builder.irCall(trigger)) + } + } + for (declaration in container.declarations) { val irField = (declaration as? IrField) ?: (declaration as? IrProperty)?.backingField if (irField == null || !irField.isStatic) continue