Support default arguments in expect declarations

This commit is contained in:
Svyatoslav Scherbina
2018-02-12 13:27:07 +03:00
committed by SvyatoslavScherbina
parent e0d5a5d167
commit cb4e5399d4
3 changed files with 178 additions and 2 deletions
@@ -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
}
}
+5
View File
@@ -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)
}