[FIR & IR] Support of default values for arguments in expect functions

Add FirActualDeclarationChecker that checks expect/actual return types

^KT-56331 Fixed, ^KT-56334 Fixed
This commit is contained in:
Ivan Kochurkin
2023-02-16 21:22:03 +01:00
committed by Space Team
parent a54b56d74e
commit d87619e06e
64 changed files with 819 additions and 705 deletions
@@ -173,13 +173,13 @@ private class CallablesLinkCollector(
private fun addLink(declaration: IrDeclarationBase) {
if (!declaration.isExpect) return
val member = actualMembers[generateIrElementFullName(declaration, expectActualMap, typeAliasMap)]
if (member != null) {
expectActualMap[declaration.symbol] = member.symbol
val actualMember = actualMembers[generateIrElementFullName(declaration, expectActualMap, typeAliasMap)]
if (actualMember != null) {
expectActualMap[declaration.symbol] = actualMember.symbol
if (declaration is IrProperty) {
member as IrProperty
declaration.getter?.symbol?.let { expectActualMap[it] = member.getter!!.symbol }
declaration.setter?.symbol?.let { expectActualMap[it] = member.setter!!.symbol }
val actualProperty = actualMember as IrProperty
declaration.getter?.symbol?.let { expectActualMap[it] = actualProperty.getter!!.symbol }
declaration.setter?.symbol?.let { expectActualMap[it] = actualProperty.setter!!.symbol }
}
} else {
reportMissingActual(declaration)
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.actualizer
import org.jetbrains.kotlin.backend.common.lower.copyAndActualizeDefaultValue
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
class FunctionDefaultParametersActualizer(private val expectActualMap: Map<IrSymbol, IrSymbol>) {
fun actualize() {
for ((expect, actual) in expectActualMap) {
if (expect is IrFunctionSymbol) {
actualize(expect.owner, (actual as IrFunctionSymbol).owner)
}
}
}
private fun actualize(expectFunction: IrFunction, actualFunction: IrFunction) {
expectFunction.valueParameters.zip(actualFunction.valueParameters).forEach { (expectParameter, actualParameter) ->
val expectDefaultValue = expectParameter.defaultValue
if (actualParameter.defaultValue == null && expectDefaultValue != null) {
actualParameter.defaultValue = expectDefaultValue.copyAndActualizeDefaultValue(
actualFunction,
actualParameter,
mapOf(),
classActualizer = { (expectActualMap[it.symbol] as IrClassSymbol).owner },
functionActualizer = { (expectActualMap[it.symbol] as IrFunctionSymbol).owner }
)
}
}
}
}
@@ -6,20 +6,21 @@
package org.jetbrains.kotlin.backend.common.actualizer
import org.jetbrains.kotlin.backend.common.ir.isProperExpect
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.name.FqName
object IrActualizer {
fun actualize(mainFragment: IrModuleFragment, dependentFragments: List<IrModuleFragment>) {
val (expectActualMap, typeAliasMap) = ExpectActualCollector(mainFragment, dependentFragments).collect()
removeExpectDeclaration(dependentFragments) // TODO: consider removing this call. See ExpectDeclarationRemover.kt
FunctionDefaultParametersActualizer(expectActualMap).actualize()
removeExpectDeclarations(dependentFragments)
addMissingFakeOverrides(expectActualMap, dependentFragments, typeAliasMap)
linkExpectToActual(expectActualMap, dependentFragments)
mergeIrFragments(mainFragment, dependentFragments)
}
private fun removeExpectDeclaration(dependentFragments: List<IrModuleFragment>) {
private fun removeExpectDeclarations(dependentFragments: List<IrModuleFragment>) {
for (fragment in dependentFragments) {
for (file in fragment.files) {
file.declarations.removeAll { it.isProperExpect }
@@ -12,13 +12,9 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
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.*
import org.jetbrains.kotlin.ir.types.extractTypeParameters
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil
import org.jetbrains.kotlin.resolve.multiplatform.findCompatibleActualsForExpected
@@ -153,16 +149,13 @@ open class ExpectDeclarationRemover(val symbolTable: ReferenceSymbolTable, priva
}
defaultValue.let { originalDefault ->
declaration.defaultValue = declaration.factory.createExpressionBody(originalDefault.startOffset, originalDefault.endOffset) {
expression = originalDefault.expression
.deepCopyWithSymbols(function) { symbolRemapper, _ ->
DeepCopyIrTreeWithSymbols(
symbolRemapper,
IrTypeParameterRemapper(typeParameterSubstitutionMap.getValue(expectToActual))
)
}
.remapExpectValueSymbols()
}
declaration.defaultValue = originalDefault.copyAndActualizeDefaultValue(
function,
declaration,
typeParameterSubstitutionMap.getValue(expectToActual),
classActualizer = { symbolTable.referenceClass(it.descriptor.findActualForExpect() as ClassDescriptor).owner },
functionActualizer = { symbolTable.referenceFunction(it.descriptor.findActualForExpect() as FunctionDescriptor).owner }
)
}
}
@@ -170,55 +163,4 @@ open class ExpectDeclarationRemover(val symbolTable: ReferenceSymbolTable, priva
if (!isActual) error(this)
return findCompatibleExpectsForActual().singleOrNull()
}
private fun IrExpression.remapExpectValueSymbols(): IrExpression {
return this.transform(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid()
val newValue = remapExpectValue(expression.symbol)
?: return expression
return IrGetValueImpl(
expression.startOffset,
expression.endOffset,
newValue.type,
newValue.symbol,
expression.origin
)
}
}, data = null)
}
private fun remapExpectValue(symbol: IrValueSymbol): IrValueParameter? {
if (symbol !is IrValueParameterSymbol) {
return null
}
val parameter = symbol.owner
return when (val parent = parameter.parent) {
is IrClass -> {
assert(parameter == parent.thisReceiver)
symbolTable.referenceClass(parent.descriptor.findActualForExpect() as ClassDescriptor).owner.thisReceiver!!
}
is IrFunction -> {
val actualFunction =
symbolTable.referenceFunction(parent.descriptor.findActualForExpect() as FunctionDescriptor).owner
when (parameter) {
parent.dispatchReceiverParameter ->
actualFunction.dispatchReceiverParameter!!
parent.extensionReceiverParameter ->
actualFunction.extensionReceiverParameter!!
else -> {
assert(parent.valueParameters[parameter.index] == parameter)
actualFunction.valueParameters[parameter.index]
}
}
}
else -> error(parent)
}
}
}
@@ -17,9 +17,14 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols
import org.jetbrains.kotlin.ir.util.IrTypeParameterRemapper
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -215,3 +220,61 @@ fun ParameterDescriptor.copyAsValueParameter(newOwner: CallableDescriptor, index
)
else -> throw Error("Unexpected parameter descriptor: $this")
}
fun IrExpressionBody.copyAndActualizeDefaultValue(
actualFunction: IrFunction,
actualValueParameter: IrValueParameter,
expectActualTypeParametersMap: Map<IrTypeParameter, IrTypeParameter>,
classActualizer: (IrClass) -> IrClass,
functionActualizer: (IrFunction) -> IrFunction
) = actualValueParameter.factory.createExpressionBody(startOffset, endOffset) {
expression = this@copyAndActualizeDefaultValue.expression
.deepCopyWithSymbols(actualFunction) { symbolRemapper, _ ->
DeepCopyIrTreeWithSymbols(symbolRemapper, IrTypeParameterRemapper(expectActualTypeParametersMap))
}
.transform(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid()
val newValue = remapExpectValue(expression.symbol) ?: return expression
return IrGetValueImpl(
expression.startOffset,
expression.endOffset,
newValue.type,
newValue.symbol,
expression.origin
)
}
private fun remapExpectValue(symbol: IrValueSymbol): IrValueParameter? {
if (symbol !is IrValueParameterSymbol) {
return null
}
val parameter = symbol.owner
return when (val parent = parameter.parent) {
is IrClass -> {
assert(parameter == parent.thisReceiver)
classActualizer(parent).thisReceiver!!
}
is IrFunction -> {
val function = functionActualizer(parent)
when (parameter) {
parent.dispatchReceiverParameter -> function.dispatchReceiverParameter!!
parent.extensionReceiverParameter -> function.extensionReceiverParameter!!
else -> {
assert(parent.valueParameters[parameter.index] == parameter)
function.valueParameters[parameter.index]
}
}
}
else -> error(parent)
}
}
}, data = null)
}