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.common.FileLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isExpectMember
|
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`.
|
* This pass removes all declarations with `isExpect == true`.
|
||||||
@@ -12,6 +28,98 @@ internal class ExpectDeclarationsRemoving(val context: Context) : FileLoweringPa
|
|||||||
|
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
// All declarations with `isExpect == true` are nested into a top-level declaration with `isExpect == true`.
|
// 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']
|
flags = ['-Xmulti-platform']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task mpp_default_args(type: RunStandaloneKonanTest) {
|
||||||
|
source = "codegen/mpp/mpp_default_args.kt"
|
||||||
|
flags = ['-tr', '-Xmulti-platform']
|
||||||
|
}
|
||||||
|
|
||||||
task unit1(type: RunKonanTest) {
|
task unit1(type: RunKonanTest) {
|
||||||
goldValue = "First\nkotlin.Unit\n"
|
goldValue = "First\nkotlin.Unit\n"
|
||||||
source = "codegen/basics/unit1.kt"
|
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