From 6e94eddb71fc74c2361cd0a39a83541258a9165c Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Mon, 27 Jan 2020 11:29:26 +0100 Subject: [PATCH] JVM_IR. Fix bridge generation --- compiler/fir/java/build.gradle.kts | 2 - .../backend/jvm/lower/BridgeLowering.kt | 2 +- ...nheritedDefaultMethodsOnClassesLowering.kt | 57 ++++++++++--------- compiler/ir/ir.tree/build.gradle.kts | 4 +- ...interfaceDelegationAndBridgesProcessing.kt | 31 ++++++++++ ...mpileKotlinAgainstKotlinTestGenerated.java | 5 ++ ...mpileKotlinAgainstKotlinTestGenerated.java | 5 ++ .../jvm-debugger-sequence/build.gradle.kts | 3 - 8 files changed, 74 insertions(+), 35 deletions(-) create mode 100644 compiler/testData/compileKotlinAgainstKotlin/interfaceDelegationAndBridgesProcessing.kt diff --git a/compiler/fir/java/build.gradle.kts b/compiler/fir/java/build.gradle.kts index 87e66eb1c61..b7eaf928c9d 100644 --- a/compiler/fir/java/build.gradle.kts +++ b/compiler/fir/java/build.gradle.kts @@ -18,5 +18,3 @@ sourceSets { "main" { projectDefault() } "test" {} } - -val compileKotlin: org.jetbrains.kotlin.gradle.tasks.KotlinCompile by tasks diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index 74cb0ed02e8..ba9d6893fdc 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt @@ -454,7 +454,7 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass private inner class FunctionHandleForIrFunction(val irFunction: IrSimpleFunction) : FunctionHandle { - override val isDeclaration get() = irFunction.origin != IrDeclarationOrigin.FAKE_OVERRIDE + override val isDeclaration get() = irFunction.origin != IrDeclarationOrigin.FAKE_OVERRIDE || irFunction.findInterfaceImplementation() != null override val isAbstract get() = irFunction.modality == Modality.ABSTRACT override val mayBeUsedAsSuperImplementation get() = !irFunction.parentAsClass.isInterface || irFunction.hasJvmDefault() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt index 8d19d056508..2ce1fa5ee5f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt @@ -58,7 +58,7 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo private fun generateInterfaceMethods(irClass: IrClass) { irClass.declarations.transform { declaration -> - (declaration as? IrSimpleFunction)?.getTargetForRedirection()?.let { implementation -> + (declaration as? IrSimpleFunction)?.findInterfaceImplementation()?.let { implementation -> generateDelegationToDefaultImpl(implementation, declaration) } ?: declaration } @@ -69,37 +69,13 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo // if the overriden symbol has been, or will be, replaced and patch it accordingly. override fun visitSimpleFunction(declaration: IrSimpleFunction) { declaration.overriddenSymbols.replaceAll { symbol -> - if (symbol.owner.getTargetForRedirection() != null) + if (symbol.owner.findInterfaceImplementation() != null) context.declarationFactory.getDefaultImplsRedirection(symbol.owner).symbol else symbol } super.visitSimpleFunction(declaration) } - private fun IrSimpleFunction.getTargetForRedirection(): IrSimpleFunction? { - if (origin != IrDeclarationOrigin.FAKE_OVERRIDE) return null - parent.let { if (it is IrClass && it.isJvmInterface) return null } - - val implementation = resolveFakeOverride() ?: return null - - // Only generate interface delegation for functions immediately inherited from an interface. - // (Otherwise, delegation will be present in the parent class) - if (overriddenSymbols.any { !it.owner.parentAsClass.isInterface && it.owner.modality != Modality.ABSTRACT && it.owner.resolveFakeOverride() == implementation }) { - return null - } - - if (!implementation.hasInterfaceParent() - || Visibilities.isPrivate(implementation.visibility) - || implementation.isDefinitelyNotDefaultImplsMethod() - || implementation.isMethodOfAny() - || implementation.hasJvmDefault() - ) { - return null - } - - return implementation - } - private fun generateDelegationToDefaultImpl( interfaceImplementation: IrSimpleFunction, classOverride: IrSimpleFunction @@ -217,3 +193,32 @@ private class InterfaceObjectCallsLowering(val context: JvmBackendContext) : IrE return super.visitCall(irCall(expression, resolved, newSuperQualifierSymbol = newSuperQualifierSymbol)) } } + +/** + * Given a fake override in a class, returns an overridden declaration with implementation in interface, such that a method delegating to that + * interface implementation should be generated into the class containing the fake override; or null if the given function is not a fake + * override of any interface implementation or such method was already generated into the superclass or is a method from Any. + */ +internal fun IrSimpleFunction.findInterfaceImplementation(): IrSimpleFunction? { + if (!isFakeOverride) return null + parent.let { if (it is IrClass && it.isJvmInterface) return null } + + val implementation = resolveFakeOverride() ?: return null + + // Only generate interface delegation for functions immediately inherited from an interface. + // (Otherwise, delegation will be present in the parent class) + if (overriddenSymbols.any { !it.owner.parentAsClass.isInterface && it.owner.modality != Modality.ABSTRACT && it.owner.resolveFakeOverride() == implementation }) { + return null + } + + if (!implementation.hasInterfaceParent() + || Visibilities.isPrivate(implementation.visibility) + || implementation.isDefinitelyNotDefaultImplsMethod() + || implementation.isMethodOfAny() + || implementation.hasJvmDefault() + ) { + return null + } + + return implementation +} \ No newline at end of file diff --git a/compiler/ir/ir.tree/build.gradle.kts b/compiler/ir/ir.tree/build.gradle.kts index f55fe608802..85c902645a9 100644 --- a/compiler/ir/ir.tree/build.gradle.kts +++ b/compiler/ir/ir.tree/build.gradle.kts @@ -13,6 +13,4 @@ dependencies { sourceSets { "main" { projectDefault() } "test" {} -} - -val compileKotlin: org.jetbrains.kotlin.gradle.tasks.KotlinCompile by tasks \ No newline at end of file +} \ No newline at end of file diff --git a/compiler/testData/compileKotlinAgainstKotlin/interfaceDelegationAndBridgesProcessing.kt b/compiler/testData/compileKotlinAgainstKotlin/interfaceDelegationAndBridgesProcessing.kt new file mode 100644 index 00000000000..6a38fd21d1d --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/interfaceDelegationAndBridgesProcessing.kt @@ -0,0 +1,31 @@ +// FILE: A.kt + +package test + +interface CodeBlock { + fun foo(): String +} + +interface CompositeCodeBlock: CodeBlock { + override fun foo(): String { + return "OK" + } +} + +interface ForLoopBody : CodeBlock + +abstract class CodeBlockBase: CompositeCodeBlock + +abstract class LineSeparatedCodeBlock: CodeBlockBase() + +// FILE: B.kt + +import test.* + +open class KotlinCodeBlock: LineSeparatedCodeBlock() + +class KotlinForLoopBody : KotlinCodeBlock(), ForLoopBody + +fun box(): String { + return KotlinForLoopBody().foo() +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java index 85a09c3b579..00ff4d1db54 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java @@ -163,6 +163,11 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl runTest("compiler/testData/compileKotlinAgainstKotlin/innerClassConstructor.kt"); } + @TestMetadata("interfaceDelegationAndBridgesProcessing.kt") + public void testInterfaceDelegationAndBridgesProcessing() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/interfaceDelegationAndBridgesProcessing.kt"); + } + @TestMetadata("internalSetterOverridden.kt") public void testInternalSetterOverridden() throws Exception { runTest("compiler/testData/compileKotlinAgainstKotlin/internalSetterOverridden.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java index 49a605cd0ac..eb80478f202 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java @@ -158,6 +158,11 @@ public class IrCompileKotlinAgainstKotlinTestGenerated extends AbstractIrCompile runTest("compiler/testData/compileKotlinAgainstKotlin/innerClassConstructor.kt"); } + @TestMetadata("interfaceDelegationAndBridgesProcessing.kt") + public void testInterfaceDelegationAndBridgesProcessing() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/interfaceDelegationAndBridgesProcessing.kt"); + } + @TestMetadata("internalSetterOverridden.kt") public void testInternalSetterOverridden() throws Exception { runTest("compiler/testData/compileKotlinAgainstKotlin/internalSetterOverridden.kt"); diff --git a/idea/jvm-debugger/jvm-debugger-sequence/build.gradle.kts b/idea/jvm-debugger/jvm-debugger-sequence/build.gradle.kts index 5609c56f317..03ba33bb324 100644 --- a/idea/jvm-debugger/jvm-debugger-sequence/build.gradle.kts +++ b/idea/jvm-debugger/jvm-debugger-sequence/build.gradle.kts @@ -23,6 +23,3 @@ sourceSets { "main" { projectDefault() } "test" { none() } } - -val compileKotlin: org.jetbrains.kotlin.gradle.tasks.KotlinCompile by tasks -compileKotlin.kotlinOptions.freeCompilerArgs += "-Xno-use-ir"