Support default arguments in expect declarations
This commit is contained in:
committed by
SvyatoslavScherbina
parent
e0d5a5d167
commit
cb4e5399d4
+110
-2
@@ -3,7 +3,23 @@ package org.jetbrains.kotlin.backend.konan.lower
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isExpectMember
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.MemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver
|
||||
|
||||
/**
|
||||
* This pass removes all declarations with `isExpect == true`.
|
||||
@@ -12,6 +28,98 @@ internal class ExpectDeclarationsRemoving(val context: Context) : FileLoweringPa
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
// All declarations with `isExpect == true` are nested into a top-level declaration with `isExpect == true`.
|
||||
irFile.declarations.removeAll { it.descriptor.isExpectMember }
|
||||
irFile.declarations.removeAll {
|
||||
if (it.descriptor.isExpectMember) {
|
||||
copyDefaultArgumentsFromExpectToActual(it)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyDefaultArgumentsFromExpectToActual(declaration: IrDeclaration) {
|
||||
declaration.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitValueParameter(declaration: IrValueParameter) {
|
||||
super.visitValueParameter(declaration)
|
||||
|
||||
val defaultValue = declaration.defaultValue ?: return
|
||||
val function = declaration.parent as IrFunction
|
||||
|
||||
val index = declaration.index
|
||||
assert(function.valueParameters[index] == declaration)
|
||||
|
||||
function.findActualForExpected().valueParameters[index].defaultValue = defaultValue.also {
|
||||
it.expression = it.expression.remapExpectValueSymbols()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun IrFunction.findActualForExpected(): IrFunction =
|
||||
context.ir.symbols.symbolTable.referenceFunction(descriptor.findActualForExpect()).owner as IrFunction
|
||||
|
||||
private fun IrClass.findActualForExpected(): IrClass =
|
||||
context.ir.symbols.symbolTable.referenceClass(descriptor.findActualForExpect()).owner
|
||||
|
||||
private inline fun <reified T : MemberDescriptor> T.findActualForExpect() = with(ExpectedActualResolver) {
|
||||
val descriptor = this@findActualForExpect
|
||||
|
||||
if (!descriptor.isExpect) error(this)
|
||||
|
||||
findCompatibleActualForExpected(descriptor.module).singleOrNull() ?: error(descriptor)
|
||||
} as T
|
||||
|
||||
private fun IrExpression.remapExpectValueSymbols(): IrExpression {
|
||||
return this.transform(object : IrElementTransformerVoid() {
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
val newSymbol = remapExpectValueSymbol(expression.symbol)
|
||||
?: return expression
|
||||
|
||||
return IrGetValueImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
newSymbol,
|
||||
expression.origin
|
||||
)
|
||||
}
|
||||
}, data = null)
|
||||
}
|
||||
|
||||
private fun remapExpectValueSymbol(symbol: IrValueSymbol): IrValueParameterSymbol? {
|
||||
if (symbol !is IrValueParameterSymbol) {
|
||||
return null
|
||||
}
|
||||
|
||||
val parameter = symbol.owner
|
||||
val parent = parameter.parent
|
||||
|
||||
return when (parent) {
|
||||
is IrClass -> {
|
||||
assert(parameter == parent.thisReceiver)
|
||||
parent.findActualForExpected().thisReceiver!!
|
||||
}
|
||||
|
||||
is IrFunction -> when {
|
||||
parameter == parent.dispatchReceiverParameter ->
|
||||
parent.findActualForExpected().dispatchReceiverParameter!!
|
||||
|
||||
parameter == parent.extensionReceiverParameter ->
|
||||
parent.findActualForExpected().extensionReceiverParameter!!
|
||||
|
||||
else -> {
|
||||
assert(parent.valueParameters[parameter.index] == parameter)
|
||||
parent.findActualForExpected().valueParameters[parameter.index]
|
||||
}
|
||||
}
|
||||
|
||||
else -> error(parent)
|
||||
}.symbol
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2061,6 +2061,11 @@ task mpp2(type: LinkKonanTest) {
|
||||
flags = ['-Xmulti-platform']
|
||||
}
|
||||
|
||||
task mpp_default_args(type: RunStandaloneKonanTest) {
|
||||
source = "codegen/mpp/mpp_default_args.kt"
|
||||
flags = ['-tr', '-Xmulti-platform']
|
||||
}
|
||||
|
||||
task unit1(type: RunKonanTest) {
|
||||
goldValue = "First\nkotlin.Unit\n"
|
||||
source = "codegen/basics/unit1.kt"
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package codegen.mpp.mpp_default_args
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
@Test fun runTest() {
|
||||
box()
|
||||
}
|
||||
|
||||
fun box() {
|
||||
assertEquals(test1(), 42)
|
||||
assertEquals(test2(17), 34)
|
||||
assertEquals(test3(), -1)
|
||||
|
||||
Test4().test()
|
||||
Test5().Inner().test()
|
||||
|
||||
42.test6()
|
||||
}
|
||||
|
||||
expect fun test1(x: Int = 42): Int
|
||||
actual fun test1(x: Int) = x
|
||||
|
||||
expect fun test2(x: Int, y: Int = x): Int
|
||||
actual fun test2(x: Int, y: Int) = x + y
|
||||
|
||||
expect fun test3(x: Int = 42, y: Int = x + 1): Int
|
||||
actual fun test3(x: Int, y: Int) = x - y
|
||||
|
||||
expect class Test4 {
|
||||
fun test(arg: Any = this)
|
||||
}
|
||||
|
||||
actual class Test4 {
|
||||
actual fun test(arg: Any) {
|
||||
assertEquals(arg, this)
|
||||
}
|
||||
}
|
||||
|
||||
expect class Test5 {
|
||||
inner class Inner {
|
||||
constructor(arg: Any = this@Test5)
|
||||
|
||||
fun test(arg1: Any = this@Test5, arg2: Any = this@Inner)
|
||||
}
|
||||
}
|
||||
|
||||
actual class Test5 {
|
||||
actual inner class Inner {
|
||||
actual constructor(arg: Any) {
|
||||
assertEquals(arg, this@Test5)
|
||||
}
|
||||
|
||||
actual fun test(arg1: Any, arg2: Any) {
|
||||
assertEquals(arg1, this@Test5)
|
||||
assertEquals(arg2, this@Inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect fun Int.test6(arg: Int = this)
|
||||
actual fun Int.test6(arg: Int) {
|
||||
assertEquals(arg, this)
|
||||
}
|
||||
Reference in New Issue
Block a user