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.
This commit is contained in:
pyos
2020-01-02 13:01:02 +01:00
committed by Alexander Udalov
parent 98f5c5aa95
commit 6b5d92a693
4 changed files with 51 additions and 13 deletions
@@ -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 {
@@ -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()
@@ -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");
@@ -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");