From 6b5d92a6935146391b887319ab752e91febbd4dc Mon Sep 17 00:00:00 2001 From: pyos Date: Thu, 2 Jan 2020 13:01:02 +0100 Subject: [PATCH] IR: work around a bug in interface delegation descriptors interface I { fun f(x: Int = 1) } class C(val y: I) : I by y { // implicit `override fun f(x: Int) = y.f(x)` has a default value for `x` } -- the only case where a function with overridden symbols has defaults. --- .../lower/DefaultArgumentStubGenerator.kt | 37 ++++++++++++------- .../delegatedDefault.kt | 17 +++++++++ ...mpileKotlinAgainstKotlinTestGenerated.java | 5 +++ ...mpileKotlinAgainstKotlinTestGenerated.java | 5 +++ 4 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/compileKotlinAgainstKotlin/delegatedDefault.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt index 6f34e50ab7a..ce0b84317ee 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt @@ -334,27 +334,38 @@ private fun IrFunction.generateDefaultsFunction( context: CommonBackendContext, skipInlineMethods: Boolean, skipExternalMethods: Boolean -): IrFunction? = context.ir.defaultParameterDeclarationsCache[this] ?: when { - skipInlineMethods && isInline -> null - skipExternalMethods && isExternalOrInheritedFromExternal() -> null - valueParameters.any { it.defaultValue != null } -> - generateDefaultsFunctionImpl(context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER).also { - context.ir.defaultParameterDeclarationsCache[this] = it - } - this is IrSimpleFunction -> { +): IrFunction? { + if (skipInlineMethods && isInline) return null + if (skipExternalMethods && isExternalOrInheritedFromExternal()) return null + context.ir.defaultParameterDeclarationsCache[this]?.let { return it } + if (this is IrSimpleFunction) { // If this is an override of a function with default arguments, produce a fake override of a default stub. val overriddenStubs = overriddenSymbols.mapNotNull { it.owner.generateDefaultsFunction(context, skipInlineMethods, skipExternalMethods)?.symbol as IrSimpleFunctionSymbol? } - if (overriddenStubs.isNotEmpty()) - generateDefaultsFunctionImpl(context, IrDeclarationOrigin.FAKE_OVERRIDE).also { + if (overriddenStubs.isNotEmpty()) { + return generateDefaultsFunctionImpl(context, IrDeclarationOrigin.FAKE_OVERRIDE).also { (it as IrSimpleFunction).overriddenSymbols.addAll(overriddenStubs) context.ir.defaultParameterDeclarationsCache[this] = it } - else - null + } } - else -> null + // Note: this is intentionally done *after* checking for overrides. While normally `override fun`s + // have no default parameters, there is an exception in case of interface delegation: + // interface I { + // fun f(x: Int = 1) + // } + // class C(val y: I) : I by y { + // // implicit `override fun f(x: Int) = y.f(x)` has a default value for `x` + // } + // Since this bug causes the metadata serializer to write the "has default value" flag into compiled + // binaries, it's way too late to fix it. Hence the workaround. + if (valueParameters.any { it.defaultValue != null }) { + return generateDefaultsFunctionImpl(context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER).also { + context.ir.defaultParameterDeclarationsCache[this] = it + } + } + return null } private fun IrFunction.generateDefaultsFunctionImpl(context: CommonBackendContext, newOrigin: IrDeclarationOrigin): IrFunction { diff --git a/compiler/testData/compileKotlinAgainstKotlin/delegatedDefault.kt b/compiler/testData/compileKotlinAgainstKotlin/delegatedDefault.kt new file mode 100644 index 00000000000..a3e995b04fa --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/delegatedDefault.kt @@ -0,0 +1,17 @@ +// FILE: A.kt +package lib + +interface A { + fun f(x: String = "OK"): String +} + +class B : A { + override fun f(x: String) = x +} + +class C(val x: A) : A by x + +// FILE: B.kt +import lib.* + +fun box() = C(B()).f() diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java index 0794d9f3eed..4651511184a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java @@ -118,6 +118,11 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl runTest("compiler/testData/compileKotlinAgainstKotlin/defaultLambdaRegeneration2.kt"); } + @TestMetadata("delegatedDefault.kt") + public void testDelegatedDefault() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/delegatedDefault.kt"); + } + @TestMetadata("doublyNestedClass.kt") public void testDoublyNestedClass() throws Exception { runTest("compiler/testData/compileKotlinAgainstKotlin/doublyNestedClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java index 02fb64e24f2..df6dde089f0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstKotlinTestGenerated.java @@ -113,6 +113,11 @@ public class IrCompileKotlinAgainstKotlinTestGenerated extends AbstractIrCompile runTest("compiler/testData/compileKotlinAgainstKotlin/defaultLambdaRegeneration2.kt"); } + @TestMetadata("delegatedDefault.kt") + public void testDelegatedDefault() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/delegatedDefault.kt"); + } + @TestMetadata("doublyNestedClass.kt") public void testDoublyNestedClass() throws Exception { runTest("compiler/testData/compileKotlinAgainstKotlin/doublyNestedClass.kt");