diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt index 8c9a7ec941b..c396388e0ee 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt @@ -150,39 +150,52 @@ class JvmCachedDeclarations( return defaultImplsMethods.getOrPut(interfaceFun) { val defaultImpls = getDefaultImplsClass(interfaceFun.parentAsClass) + // If `interfaceFun` is not a real implementation, then we're generating stubs in a descendant + // interface's DefaultImpls. For example, + // + // interface I1 { fun f() { ... } } + // interface I2 : I1 + // + // is supposed to allow using `I2.DefaultImpls.f` as if it was inherited from `I1.DefaultImpls`. + // The classes are not actually related and `I2.DefaultImpls.f` is not a fake override but a bridge. + val defaultImplsOrigin = when { + !forCompatibilityMode && !interfaceFun.isFakeOverride -> + when { + interfaceFun.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER -> + interfaceFun.origin + interfaceFun.origin.isSynthetic -> + JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_WITH_MOVED_RECEIVERS_SYNTHETIC + else -> + JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_WITH_MOVED_RECEIVERS + } + interfaceFun.resolveFakeOverride()!!.origin.isSynthetic -> + if (forCompatibilityMode) + JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY_SYNTHETIC + else + JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_TO_SYNTHETIC + else -> + if (forCompatibilityMode) + JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY + else + JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE + } + + // Interface functions are public or private, with one exception: clone in Cloneable, which is protected. + // However, Cloneable has no DefaultImpls, so this merely replicates the incorrect behavior of the old backend. + // We should rather not generate a bridge to clone when interface inherits from Cloneable at all. + val defaultImplsVisibility = + if (DescriptorVisibilities.isPrivate(interfaceFun.visibility)) + DescriptorVisibilities.PRIVATE + else + DescriptorVisibilities.PUBLIC + context.irFactory.createStaticFunctionWithReceivers( defaultImpls, interfaceFun.name, interfaceFun, dispatchReceiverType = parent.defaultType, - // If `interfaceFun` is not a real implementation, then we're generating stubs in a descendant - // interface's DefaultImpls. For example, - // - // interface I1 { fun f() { ... } } - // interface I2 : I1 - // - // is supposed to allow using `I2.DefaultImpls.f` as if it was inherited from `I1.DefaultImpls`. - // The classes are not actually related and `I2.DefaultImpls.f` is not a fake override but a bridge. - origin = when { - !forCompatibilityMode && !interfaceFun.isFakeOverride -> - when { - interfaceFun.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER -> interfaceFun.origin - interfaceFun.origin.isSynthetic -> JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_WITH_MOVED_RECEIVERS_SYNTHETIC - else -> JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_WITH_MOVED_RECEIVERS - } - interfaceFun.resolveFakeOverride()!!.origin.isSynthetic -> - if (forCompatibilityMode) JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY_SYNTHETIC - else JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_TO_SYNTHETIC - else -> - if (forCompatibilityMode) JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY - else JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE - }, + origin = defaultImplsOrigin, // Old backend doesn't generate ACC_FINAL on DefaultImpls methods. modality = Modality.OPEN, - - // Interface functions are public or private, with one exception: clone in Cloneable, which is protected. - // However, Cloneable has no DefaultImpls, so this merely replicates the incorrect behavior of the old backend. - // We should rather not generate a bridge to clone when interface inherits from Cloneable at all. - visibility = if (interfaceFun.visibility == DescriptorVisibilities.PRIVATE) DescriptorVisibilities.PRIVATE else DescriptorVisibilities.PUBLIC, - + visibility = defaultImplsVisibility, isFakeOverride = false, typeParametersFromContext = parent.typeParameters ).also { diff --git a/compiler/testData/codegen/bytecodeListing/kt43440.kt b/compiler/testData/codegen/bytecodeListing/kt43440.kt new file mode 100644 index 00000000000..0b2f1840c92 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/kt43440.kt @@ -0,0 +1,11 @@ +interface A { + private fun f(): T { + TODO() + } +} + +interface B { + private fun f(): T { + TODO() + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/kt43440.txt b/compiler/testData/codegen/bytecodeListing/kt43440.txt new file mode 100644 index 00000000000..6061e08282b --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/kt43440.txt @@ -0,0 +1,25 @@ +@kotlin.Metadata +public final class A$DefaultImpls { + // source: 'kt43440.kt' + private static method f(p0: A): java.lang.Object + public final inner class A$DefaultImpls +} + +@kotlin.Metadata +public interface A { + // source: 'kt43440.kt' + public final inner class A$DefaultImpls +} + +@kotlin.Metadata +public final class B$DefaultImpls { + // source: 'kt43440.kt' + private static method f(p0: B): java.lang.Object + public final inner class B$DefaultImpls +} + +@kotlin.Metadata +public interface B { + // source: 'kt43440.kt' + public final inner class B$DefaultImpls +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 9509566f674..9c43a8ab05b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -119,6 +119,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { runTest("compiler/testData/codegen/bytecodeListing/kt43217.kt"); } + @TestMetadata("kt43440.kt") + public void testKt43440() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/kt43440.kt"); + } + @TestMetadata("localFunctionInInitBlock.kt") public void testLocalFunctionInInitBlock() throws Exception { runTest("compiler/testData/codegen/bytecodeListing/localFunctionInInitBlock.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java index b7edbcea4a3..059f4ee3e8e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java @@ -119,6 +119,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes runTest("compiler/testData/codegen/bytecodeListing/kt43217.kt"); } + @TestMetadata("kt43440.kt") + public void testKt43440() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/kt43440.kt"); + } + @TestMetadata("localFunctionInInitBlock.kt") public void testLocalFunctionInInitBlock() throws Exception { runTest("compiler/testData/codegen/bytecodeListing/localFunctionInInitBlock.kt");