[IR] Fix MFVC with type arguments/parameters

#KT-1179
This commit is contained in:
Evgeniy.Zhelenskiy
2022-06-08 02:16:44 +03:00
committed by teamcity
parent cd432b1371
commit d0590e4e83
8 changed files with 438 additions and 311 deletions
@@ -718,6 +718,9 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
is MultiFieldValueClassMapping -> {
val count = bridgeRemappedParameter.declarations.fields.size
val boxCall = irCall(bridgeRemappedParameter.declarations.boxMethod).apply {
bridgeRemappedParameter.boxedType.arguments.forEachIndexed { index, argument ->
putTypeArgument(index, argument.typeOrNull)
}
for (i in 0 until count) {
putValueArgument(i, irGet(bridgeExplicitParameters[bridgeIndex++]))
}
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -135,6 +136,9 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo
when (val remappedParameter = structure[i]) {
is MultiFieldValueClassMapping ->
putValueArgument(i, irCall(remappedParameter.declarations.boxMethod).apply {
remappedParameter.boxedType.arguments.forEachIndexed { index, argument ->
putTypeArgument(index, argument.typeOrNull)
}
val boxArgumentsCount = remappedParameter.valueParameters.size
for (boxArgumentIndex in 0 until boxArgumentsCount) {
putValueArgument(boxArgumentIndex, irGet(sourceFullValueParameterList[flattenedIndex++]))
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.pop
@@ -21,6 +18,7 @@ import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements
import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassSpecificDeclarations.ImplementationAgnostic
import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassSpecificDeclarations.VirtualProperty
import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassTree.InternalNode
import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassTree.Leaf
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.ir.isMultiFieldValueClassType
import org.jetbrains.kotlin.ir.IrElement
@@ -37,9 +35,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
import org.jetbrains.kotlin.ir.transformStatement
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
@@ -58,39 +54,39 @@ val jvmMultiFieldValueClassPhase = makeIrFilePhase(
private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) {
private open inner class ValueDeclarationsRemapper {
private val symbol2getter = mutableMapOf<IrValueSymbol, ExpressionGenerator<Unit>>()
private val symbol2setters = mutableMapOf<IrValueSymbol, List<ExpressionSupplier<Unit>?>>()
private val knownExpressions = mutableMapOf<IrExpression, ImplementationAgnostic<Unit>>()
private val symbol2getter = mutableMapOf<IrValueSymbol, ExpressionGenerator>()
private val symbol2setters = mutableMapOf<IrValueSymbol, List<ExpressionSupplier?>>()
private val knownExpressions = mutableMapOf<IrExpression, ImplementationAgnostic>()
fun remapSymbol(original: IrValueSymbol, replacement: IrValueDeclaration) {
symbol2getter[original] = { irGet(replacement) }
symbol2setters[original] = if (replacement.isAssignable) listOf { _, value -> irSet(replacement, value) } else listOf(null)
symbol2setters[original] = if (replacement.isAssignable) listOf { value -> irSet(replacement, value) } else listOf(null)
}
fun remapSymbol(
original: IrValueSymbol,
unboxed: List<VirtualProperty<Unit>>,
unboxed: List<VirtualProperty>,
declarations: MultiFieldValueClassSpecificDeclarations
): Unit =
remapSymbol(original, declarations.ImplementationAgnostic(unboxed))
remapSymbol(original, declarations.ImplementationAgnostic(original.owner.type as IrSimpleType, unboxed))
fun remapSymbol(original: IrValueSymbol, unboxed: ImplementationAgnostic<Unit>) {
fun remapSymbol(original: IrValueSymbol, unboxed: ImplementationAgnostic) {
symbol2getter[original] = {
unboxed.boxedExpression(this, Unit).also { irExpression -> knownExpressions[irExpression] = unboxed }
unboxed.boxedExpression(this).also { irExpression -> knownExpressions[irExpression] = unboxed }
}
symbol2setters[original] = unboxed.virtualFields.map { it.assigner }
}
fun IrBuilderWithScope.getter(original: IrValueSymbol): IrExpression? =
symbol2getter[original]?.invoke(this, Unit)
symbol2getter[original]?.invoke(this)
fun setter(original: IrValueSymbol): List<ExpressionSupplier<Unit>?>? = symbol2setters[original]
fun setter(original: IrValueSymbol): List<ExpressionSupplier?>? = symbol2setters[original]
fun implementationAgnostic(expression: IrExpression): ImplementationAgnostic<Unit>? = knownExpressions[expression]
fun implementationAgnostic(expression: IrExpression): ImplementationAgnostic? = knownExpressions[expression]
fun IrBuilderWithScope.subfield(expression: IrExpression, name: Name): IrExpression? =
implementationAgnostic(expression)?.get(name)?.let { (expressionGenerator, representation) ->
val res = expressionGenerator(this, Unit)
val res = expressionGenerator(this)
representation?.let { knownExpressions[res] = it }
res
}
@@ -98,7 +94,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
/**
* Register value declaration instead of singular expressions when possible
*/
fun registerExpression(getter: IrExpression, representation: ImplementationAgnostic<Unit>) {
fun registerExpression(getter: IrExpression, representation: ImplementationAgnostic) {
knownExpressions[getter] = representation
}
}
@@ -108,7 +104,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
private val regularClassMFVCPropertyMainGetters: MutableMap<IrClass, MutableMap<Name, IrSimpleFunction>> = mutableMapOf()
private val regularClassMFVCPropertyNextGetters: MutableMap<IrSimpleFunction, Map<Name, IrSimpleFunction>> = mutableMapOf()
private val regularClassMFVCPropertyFieldsMapping: MutableMap<IrField, List<IrField>> = mutableMapOf()
private val regularClassMFVCPropertyNodes: MutableMap<IrSimpleFunction, MultiFieldValueClassTree<Unit, Unit>> = mutableMapOf()
private val regularClassMFVCPropertyNodes: MutableMap<IrSimpleFunction, MultiFieldValueClassTree> = mutableMapOf()
private val regularClassMFVCPropertyAllPrimitiveGetters: MutableSet<IrSimpleFunction> = mutableSetOf()
override val replacements
@@ -148,7 +144,6 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
val oldFieldToInitializers: MutableMap<IrField, IrAnonymousInitializer> = mutableMapOf()
// we need to preserve order of initializations
// todo test it
val newFields: List<List<IrField>> = fieldsToReplace.map { oldField ->
val declarations = replacements.getDeclarations(oldField.type.erasedUpperBound)!!
val newFields = declarations.fields.map { sourceField ->
@@ -163,15 +158,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
}
}
regularClassMFVCPropertyFieldsMapping[oldField] = newFields
val virtualFields: List<VirtualProperty<IrValueDeclaration>> = newFields.map { newField ->
VirtualProperty(
type = newField.type,
makeGetter = { receiver: IrValueDeclaration -> irGetField(irGet(receiver), newField) },
assigner = { receiver: IrValueDeclaration, value: IrExpression -> irSetField(irGet(receiver), newField, value) },
symbol = null,
)
}
val implementationAgnostic = declarations.ImplementationAgnostic(virtualFields)
val nodes2getters = declarations.makeNodes2FieldExpressions(newFields)
val property = oldField.correspondingPropertySymbol?.owner
val mainGetter = property?.getter
@@ -181,7 +168,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
if (!property.isDelegated && mainGetter.isDefaultGetter(oldField)) {
mainGetter.origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER
regularClassMFVCPropertyMainGetters.getOrPut(declaration) { mutableMapOf() }[oldField.name] = mainGetter
val nodesToGetters = implementationAgnostic.nodeToExpressionGetters.mapValues { (node, exprGen) ->
val nodesToGetters = nodes2getters.mapValues { (node, exprGen) ->
if (node == declarations.loweringRepresentation) mainGetter else {
context.irFactory.buildProperty {
val nameAsString = "${oldField.name.asString()}$${declarations.nodeFullNames[node]!!.asString()}"
@@ -195,8 +182,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER
}.apply {
createDispatchReceiverParameter()
val function = this
body = with(context.createIrBuilder(this.symbol)) {
irExprBody(exprGen(this, dispatchReceiverParameter!!))
irExprBody(exprGen(this, function))
}
}
}.getter!!.also {
@@ -206,7 +194,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
}
regularClassMFVCPropertyNodes.putAll(nodesToGetters.map { (node, getter) -> getter to node })
for ((node, getter) in nodesToGetters) {
if (node is InternalNode<Unit, Unit>) {
if (node is InternalNode) {
regularClassMFVCPropertyNextGetters[getter] = node.fields.associate { it.name to nodesToGetters[it.node]!! }
}
}
@@ -225,9 +213,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
val representation = newFields.zip(gettersAndSetters) { newField, (getter, setter) ->
VirtualProperty(newField.type, getter, setter, null)
}
val fieldRepresentation = declarations.ImplementationAgnostic(representation)
val fieldRepresentation = declarations.ImplementationAgnostic(oldField.type as IrSimpleType, representation)
val boxed = fieldRepresentation.boxedExpression(
context.createIrBuilder((currentScope!!.irElement as IrSymbolOwner).symbol), Unit
context.createIrBuilder((currentScope!!.irElement as IrSymbolOwner).symbol)
)
valueDeclarationsRemapper.registerExpression(boxed, fieldRepresentation)
return boxed
@@ -330,18 +318,35 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
name = mangledName
returnType = source.returnType
}.apply {
val overriddenReplaced = replacement.overriddenSymbols.firstOrNull {
replacements.bindingNewFunctionToParameterTemplateStructure[it.owner] != null
}?.owner
if (source.parentAsClass.isMultiFieldValueClass && overriddenReplaced != null) {
copyParameterDeclarationsFrom(overriddenReplaced)
dispatchReceiverParameter = source.dispatchReceiverParameter!!.copyTo(this)
val replacementStructure = replacement.overriddenSymbols.firstNotNullOf {
replacements.bindingNewFunctionToParameterTemplateStructure[it.owner]
}.toMutableList().apply {
set(0, RegularMapping(ValueParameterTemplate(dispatchReceiverParameter!!, dispatchReceiverParameter!!.origin)))
val anyOverriddenReplaced = replacement.overriddenSymbols.any {
it.owner in replacements.bindingNewFunctionToParameterTemplateStructure
}
if (source.parentAsClass.isMultiFieldValueClass && anyOverriddenReplaced) {
copyTypeParametersFrom(source) // without static type parameters
val substitutionMap = makeTypeParameterSubstitutionMap(source, this)
dispatchReceiverParameter = source.dispatchReceiverParameter!!.let { // source!!!
it.copyTo(this, type = it.type.substitute(substitutionMap))
}
replacements.bindingNewFunctionToParameterTemplateStructure[this] = replacementStructure
require(replacement.dispatchReceiverParameter == null) {
"""
Ambiguous receivers:
${source.dispatchReceiverParameter!!.render()}
${replacement.dispatchReceiverParameter!!.render()}
""".trimIndent()
}
require(replacement.extensionReceiverParameter == null) {
"Static replacement must have no extension receiver but ${replacement.extensionReceiverParameter!!.render()} found"
}
val replacementStructure = replacements.bindingNewFunctionToParameterTemplateStructure[replacement]!!
val offset = replacementStructure[0].valueParameters.size
valueParameters = replacement.valueParameters.drop(offset).map {
it.copyTo(this, type = it.type.substitute(substitutionMap), index = it.index - offset)
}
val bridgeStructure =
replacementStructure.toMutableList().apply {
set(0, RegularMapping(ValueParameterTemplate(dispatchReceiverParameter!!, dispatchReceiverParameter!!.origin)))
}
replacements.bindingNewFunctionToParameterTemplateStructure[this] = bridgeStructure
} else {
copyParameterDeclarationsFrom(source)
}
@@ -358,9 +363,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
allScopes.push(createScope(source))
source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run {
val sourceExplicitParameters = source.explicitParameters
val targetExplicitParameters = target.explicitParameters
irExprBody(irCall(target).apply {
passTypeArgumentsFrom(source)
val targetExplicitParameters = target.explicitParameters
passTypeArgumentsWithOffsets(target, source) { source.typeParameters[it].defaultType }
val sourceStructure: List<RemappedParameter>? = replacements.bindingNewFunctionToParameterTemplateStructure[source]
val targetStructure: List<RemappedParameter>? = replacements.bindingNewFunctionToParameterTemplateStructure[target]
val errorMessage = {
@@ -415,15 +420,17 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
)
}
}
is RegularMapping, null ->
putArgument(
targetExplicitParameters[flattenedTargetIndex++],
irCall(remappedSourceParameter.declarations.boxMethod).apply {
sourceExplicitParameters
.slice(flattenedSourceIndex until flattenedSourceIndex + remappedSourceParameter.valueParameters.size)
.forEachIndexed { index, boxParameter -> putValueArgument(index, irGet(boxParameter)) }
.also { flattenedSourceIndex += remappedSourceParameter.valueParameters.size }
})
is RegularMapping, null -> putArgument(
targetExplicitParameters[flattenedTargetIndex++],
irCall(remappedSourceParameter.declarations.boxMethod).apply {
remappedSourceParameter.boxedType.arguments.forEachIndexed { index, argument ->
putTypeArgument(index, argument.typeOrNull)
}
sourceExplicitParameters
.slice(flattenedSourceIndex until flattenedSourceIndex + remappedSourceParameter.valueParameters.size)
.forEachIndexed { index, boxParameter -> putValueArgument(index, irGet(boxParameter)) }
.also { flattenedSourceIndex += remappedSourceParameter.valueParameters.size }
})
}
}
is RegularMapping, null -> when (remappedTargetParameter) {
@@ -451,6 +458,34 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
allScopes.pop()
}
private fun IrFunctionAccessExpression.passTypeArgumentsWithOffsets(
target: IrFunction, source: IrFunction, forCommonTypeParameters: (targetIndex: Int) -> IrType
) {
val passedTypeParametersSize = minOf(target.typeParameters.size, source.typeParameters.size)
val targetOffset = target.typeParameters.size - passedTypeParametersSize
val sourceOffset = source.typeParameters.size - passedTypeParametersSize
if (sourceOffset > 0) {
// static fun calls method
val dispatchReceiverType = source.parentAsClass.defaultType
require(dispatchReceiverType.let {
it.isMultiFieldValueClassType() && !it.isNullable() && it.erasedUpperBound.typeParameters.size == sourceOffset
}) { "Unexpected dispatcher receiver type: ${dispatchReceiverType.render()}" }
}
if (targetOffset > 0) {
// method calls static fun
val dispatchReceiverType = source.parentAsClass.defaultType
require(dispatchReceiverType.let {
it.isMultiFieldValueClassType() && !it.isNullable() && it.erasedUpperBound.typeParameters.size == targetOffset
}) { "Unexpected dispatcher receiver type: ${dispatchReceiverType.render()}" }
dispatchReceiverType.erasedUpperBound.typeParameters.forEachIndexed { index, typeParameter ->
putTypeArgument(index, typeParameter.defaultType)
}
}
for (i in 0 until passedTypeParametersSize) {
putTypeArgument(i + targetOffset, forCommonTypeParameters(i + sourceOffset))
}
}
override fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
val parametersStructure = replacements.bindingOldFunctionToParameterTemplateStructure[original]!!
require(parametersStructure.size == original.explicitParameters.size) {
@@ -495,7 +530,6 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
fun MultiFieldValueClassSpecificDeclarations.buildBoxFunction() {
boxMethod.body = with(context.createIrBuilder(boxMethod.symbol)) {
irExprBody(irCall(primaryConstructor.symbol).apply {
passTypeArgumentsFrom(boxMethod)
for (i in leaves.indices) {
putValueArgument(i, irGet(boxMethod.valueParameters[i]))
}
@@ -568,6 +602,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
if (newReceiver is IrCall) {
val nextFunction = regularClassMFVCPropertyNextGetters[newReceiver.symbol.owner]?.get(name)
if (nextFunction != null) {
require(nextFunction.typeParameters.isEmpty()) { "Subgetter with type parameters: ${nextFunction.render()}" }
return irCall(nextFunction).apply {
this.dispatchReceiver = newReceiver.dispatchReceiver
}
@@ -596,8 +631,11 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
require(leftClass == rightClass) { "Equals for different classes: $leftClass and $rightClass called" }
return context.createIrBuilder(expression.symbol).run {
irCall(leftImplementation.regularDeclarations.specializedEqualsMethod).apply {
(leftImplementation.type.arguments + rightImplementation.type.arguments).forEachIndexed { index, argument ->
putTypeArgument(index, argument.typeOrNull)
}
val arguments =
(leftImplementation.virtualFields + rightImplementation.virtualFields).map { it.makeGetter(this@run, Unit) }
(leftImplementation.virtualFields + rightImplementation.virtualFields).map { it.makeGetter(this@run) }
arguments.forEachIndexed { index, argument -> putValueArgument(index, argument) }
}
}
@@ -625,6 +663,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
val nonNullLeftArgumentVariable =
irTemporary(irImplicitCast(irGet(leftValue), leftArgument.type.makeNotNull()))
+irCall(context.irBuiltIns.eqeqSymbol).apply {
copyTypeArgumentsFrom(expression)
putValueArgument(0, irGet(nonNullLeftArgumentVariable))
putValueArgument(1, rightArgument)
}
@@ -639,7 +678,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
private fun makeLeavesGetters(currentGetter: IrSimpleFunction): List<IrSimpleFunction>? =
when (val node = regularClassMFVCPropertyNodes[currentGetter]) {
null -> null
is MultiFieldValueClassTree.Leaf<Unit> -> listOf(currentGetter)
is Leaf -> listOf(currentGetter)
is InternalNode -> node.fields.flatMap { makeLeavesGetters(regularClassMFVCPropertyNextGetters[currentGetter]!![it.name]!!)!! }
}
@@ -655,7 +694,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
val newArguments: List<IrExpression?> =
makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure.map { it.valueParameters })
+irCall(replacement.symbol).apply {
copyTypeArgumentsFrom(original)
passTypeArgumentsWithOffsets(replacement, originalFunction) { original.getTypeArgument(it)!! }
for ((parameter, argument) in replacement.explicitParameters zip newArguments) {
if (argument == null) continue
putArgument(replacement, parameter, argument.transform(this@JvmMultiFieldValueClassLowering, null))
@@ -671,6 +710,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
val toString = argument.type.erasedUpperBound.functions.single {
it.name.asString() == "toString" && it.valueParameters.isEmpty()
}
require(toString.typeParameters.isEmpty()) { "Bad toString: ${toString.render()}" }
irCall(toString).apply {
dispatchReceiver = argument
}
@@ -760,6 +800,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
?: return super.visitGetField(expression)
with(context.createIrBuilder(expression.symbol)) {
irCall(getter).apply {
require(getter.typeParameters.isEmpty()) { "Getter with type parameters: ${getter.render()}" }
dispatchReceiver = expression.receiver!!.transform(this@JvmMultiFieldValueClassLowering, null)
}
}
@@ -807,7 +848,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
}
)
for ((setter, subValue) in setters zip subValues) {
setter?.invoke(this, Unit, subValue)?.let { +it }
setter?.invoke(this, subValue)?.let { +it }
}
}
}
@@ -835,14 +876,14 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
}
private fun List<IrVariable>.toGettersAndSetters() = map { variable ->
Pair<ExpressionGenerator<Unit>, ExpressionSupplier<Unit>>(
Pair<ExpressionGenerator, ExpressionSupplier>(
{ irGet(variable) },
{ _, value: IrExpression -> irSet(variable, value) }
{ value: IrExpression -> irSet(variable, value) }
)
}
private fun List<IrField>.toGettersAndSetters(receiver: IrValueParameter, transformReceiver: Boolean = false) = map { field ->
Pair<ExpressionGenerator<Unit>, ExpressionSupplier<Unit>>(
Pair<ExpressionGenerator, ExpressionSupplier>(
{
val initialGetReceiver = irGet(receiver)
val resultReceiver =
@@ -850,7 +891,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
else initialGetReceiver
irGetField(resultReceiver, field)
},
{ _, value: IrExpression ->
{ value: IrExpression ->
val initialGetReceiver = irGet(receiver)
val resultReceiver =
if (transformReceiver) initialGetReceiver.transform(this@JvmMultiFieldValueClassLowering, null)
@@ -882,7 +923,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
}
fun IrBlockBuilder.flattenExpressionTo(
expression: IrExpression, variables: List<Pair<ExpressionGenerator<Unit>, ExpressionSupplier<Unit>>>
expression: IrExpression, variables: List<Pair<ExpressionGenerator, ExpressionSupplier>>
) {
val declarations = replacements.getDeclarations(expression.type.erasedUpperBound) ?: run {
val constructor = (expression as? IrConstructorCall)?.symbol?.owner ?: return@run null
@@ -891,7 +932,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
if (expression.type.isNullable() || declarations == null) {
require(variables.size == 1)
+variables.single().second(this, Unit, expression.transform(this@JvmMultiFieldValueClassLowering, null))
+variables.single().second(this, expression.transform(this@JvmMultiFieldValueClassLowering, null))
return
}
require(variables.size == declarations.leaves.size)
@@ -907,22 +948,23 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
for ((treeField, argument) in root.fields zip oldArguments) {
val size = when (treeField.node) {
is InternalNode -> replacements.getDeclarations(treeField.node.irClass)!!.leaves.size
is MultiFieldValueClassTree.Leaf -> 1
is Leaf -> 1
}
val subVariables = variables.slice(curOffset until (curOffset + size)).also { curOffset += size }
argument?.let { flattenExpressionTo(it, subVariables) }
}
+irCall(declarations.primaryConstructorImpl).apply {
variables.forEachIndexed { index, variable -> putValueArgument(index, variable.first(this@flattenExpressionTo, Unit)) }
copyTypeArgumentsFrom(expression)
variables.forEachIndexed { index, variable -> putValueArgument(index, variable.first(this@flattenExpressionTo)) }
}
return
}
}
val transformedExpression = expression.transform(this@JvmMultiFieldValueClassLowering, null)
valueDeclarationsRemapper.implementationAgnostic(transformedExpression)?.virtualFields?.map { it.makeGetter(this, Unit) }?.let {
valueDeclarationsRemapper.implementationAgnostic(transformedExpression)?.virtualFields?.map { it.makeGetter(this) }?.let {
require(variables.size == it.size)
for ((variable, subExpression) in variables zip it) {
+variable.second(this, Unit, subExpression)
+variable.second(this, subExpression)
}
return
}
@@ -936,7 +978,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
transformedExpression.getValueArgument(it)
}) {
if (argument != null) {
+variable.second(this, Unit, argument)
+variable.second(this, argument)
}
}
return
@@ -946,7 +988,8 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
val receiver = irTemporary(transformedExpression.dispatchReceiver)
require(getters.size == variables.size) { "Number of getters must be equal to number of variables" }
for ((variable, getter) in variables zip getters) {
+variable.second(this, Unit, irCall(getter).apply { dispatchReceiver = irGet(receiver) })
require(getter.typeParameters.isEmpty()) { "Getter with type parameters: ${getter.render()}" }
+variable.second(this, irCall(getter).apply { dispatchReceiver = irGet(receiver) })
}
return
}
@@ -960,7 +1003,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
}
val boxed = irTemporary(transformedExpression)
for ((variable, unboxMethod) in variables zip declarations.unboxMethods) {
+variable.second(this, Unit, irCall(unboxMethod).apply { dispatchReceiver = irGet(boxed) })
+variable.second(this, irCall(unboxMethod).apply { dispatchReceiver = irGet(boxed) })
}
}
@@ -43,7 +43,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
}
val replacement = replacements.getReplacementFunction(function)
if (replacement == null) {
if (function is IrConstructor) {
val constructorReplacement = replacements.getReplacementRegularClassConstructor(function)
@@ -7,15 +7,15 @@ package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.MultiFieldValueClassMapping
import org.jetbrains.kotlin.backend.jvm.ir.*
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.LockBasedStorageManager
@@ -89,20 +89,23 @@ class MemoizedMultiFieldValueClassReplacements(
.flatMap { it.valueParameters }.mapIndexed { index: Int, template -> template.toParameter(this, index) }
private fun List<ValueParameterTemplate>.grouped(
substitutionMap: Map<IrTypeParameterSymbol, IrType>,
originWhenFlattenedAndNotSpecified: IrDeclarationOrigin? = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER
): List<RemappedParameter> = map { parameter ->
val declarations = parameter.type.takeIf { !it.isNullable() }?.erasedUpperBound?.let { getDeclarations(it) }
?: return@map RemappedParameter.RegularMapping(parameter)
require(!parameter.original.hasDefaultValue()) { "Default parameters values are not supported for multi-field value classes" }
RemappedParameter.MultiFieldValueClassMapping(declarations, declarations.leaves.map { leaf ->
ValueParameterTemplate(
name = "${parameter.name}$${declarations.nodeFullNames[leaf]!!}",
type = leaf.type,
origin = parameter.origin ?: originWhenFlattenedAndNotSpecified,
defaultValue = null,
original = parameter.original,
)
})
val localSubstitutionMap = declarations.valueClass.typeParameters.zip((parameter.type as IrSimpleType).arguments)
.mapNotNull { (parameter, argument) -> if (argument is IrSimpleType) parameter.symbol to argument.type else null}.toMap()
MultiFieldValueClassMapping(declarations, parameter.type.substitute(substitutionMap) as IrSimpleType, declarations.leaves.map { leaf ->
ValueParameterTemplate(
name = "${parameter.name}$${declarations.nodeFullNames[leaf]!!}",
type = leaf.type.substitute(localSubstitutionMap),
origin = parameter.origin ?: originWhenFlattenedAndNotSpecified,
defaultValue = null,
original = parameter.original,
)
})
}
private fun buildReplacement(
@@ -122,7 +125,7 @@ class MemoizedMultiFieldValueClassReplacements(
}
private fun makeGroupedValueParametersFrom(
function: IrFunction, includeDispatcherReceiver: Boolean
function: IrFunction, includeDispatcherReceiver: Boolean, substitutionMap: Map<IrTypeParameterSymbol, IrType>
): List<RemappedParameter> {
val newFlattenedParameters = mutableListOf<RemappedParameter>()
if (function.dispatchReceiverParameter != null && includeDispatcherReceiver) {
@@ -133,7 +136,7 @@ class MemoizedMultiFieldValueClassReplacements(
defaultValue = null,
original = function.parentAsClass.thisReceiver!!,
)
newFlattenedParameters.addAll(listOf(template).grouped())
newFlattenedParameters.addAll(listOf(template).grouped(substitutionMap))
}
val contextReceivers = function.valueParameters.take(function.contextReceiverParametersCount)
.mapIndexed { index: Int, valueParameter: IrValueParameter ->
@@ -145,7 +148,7 @@ class MemoizedMultiFieldValueClassReplacements(
original = valueParameter,
)
}
.grouped()
.grouped(substitutionMap)
newFlattenedParameters.addAll(contextReceivers)
function.extensionReceiverParameter?.let {
val template = ValueParameterTemplate(
@@ -155,11 +158,11 @@ class MemoizedMultiFieldValueClassReplacements(
defaultValue = it.defaultValue,
original = it,
)
newFlattenedParameters.addAll(listOf(template).grouped())
newFlattenedParameters.addAll(listOf(template).grouped(substitutionMap))
}
newFlattenedParameters += function.valueParameters.drop(function.contextReceiverParametersCount).map {
ValueParameterTemplate(it, origin = null)
}.grouped()
}.grouped(substitutionMap)
return newFlattenedParameters
}
@@ -170,6 +173,7 @@ class MemoizedMultiFieldValueClassReplacements(
data class MultiFieldValueClassMapping(
val declarations: MultiFieldValueClassSpecificDeclarations,
val boxedType: IrSimpleType,
override val valueParameters: List<ValueParameterTemplate>,
) : RemappedParameter() {
init {
@@ -181,41 +185,53 @@ class MemoizedMultiFieldValueClassReplacements(
}
val bindingOldFunctionToParameterTemplateStructure: MutableMap<IrFunction, List<RemappedParameter>> = ConcurrentHashMap()
val bindingNewFunctionToParameterTemplateStructure: MutableMap<IrFunction, List<RemappedParameter>> = ConcurrentHashMap()
val bindingNewFunctionToParameterTemplateStructure: MutableMap<IrFunction, List<RemappedParameter>> =
object : ConcurrentHashMap<IrFunction, List<RemappedParameter>>() {
override fun put(key: IrFunction, value: List<RemappedParameter>): List<RemappedParameter>? {
require(key.explicitParametersCount == value.sumOf { it.valueParameters.size }) {
"Illegal structure $value for function ${key.dump()}"
}
return super.put(key, value)
}
}
// todo inline classes pass type arguments correctly
override fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT, noFakeOverride = true) {
originalFunctionForStaticReplacement[this] = function
val newFlattenedParameters = makeGroupedValueParametersFrom(function, includeDispatcherReceiver = true)
typeParameters = listOf()
copyTypeParametersFrom(function.parentAsClass)
val substitutionMap = function.parentAsClass.typeParameters.map { it.symbol }.zip(typeParameters.map { it.defaultType }).toMap()
copyTypeParametersFrom(function, parameterMap = (function.parentAsClass.typeParameters zip typeParameters).toMap())
val newFlattenedParameters = makeGroupedValueParametersFrom(function, includeDispatcherReceiver = true, substitutionMap)
valueParameters = makeValueParametersFromTemplate(newFlattenedParameters)
bindingOldFunctionToParameterTemplateStructure[function] = newFlattenedParameters
bindingNewFunctionToParameterTemplateStructure[this] = newFlattenedParameters
valueParameters = makeValueParametersFromTemplate(newFlattenedParameters)
}
override fun createMethodReplacement(function: IrFunction): IrSimpleFunction = buildReplacement(function, function.origin) {
originalFunctionForMethodReplacement[this] = function
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this, index = -1)
val newFlattenedParameters = makeGroupedValueParametersFrom(function, includeDispatcherReceiver = false)
val newFlattenedParameters = makeGroupedValueParametersFrom(function, includeDispatcherReceiver = false, mapOf())
val receiver = dispatchReceiverParameter
val receiverTemplate = if (receiver != null) ValueParameterTemplate(receiver, origin = receiver.origin) else null
val remappedParameters =
if (receiverTemplate != null) listOf(RemappedParameter.RegularMapping(receiverTemplate)) + newFlattenedParameters else newFlattenedParameters
valueParameters = makeValueParametersFromTemplate(newFlattenedParameters)
bindingOldFunctionToParameterTemplateStructure[function] = remappedParameters
bindingNewFunctionToParameterTemplateStructure[this] = remappedParameters
valueParameters = makeValueParametersFromTemplate(newFlattenedParameters)
}
private fun createConstructorReplacement(@Suppress("UNUSED_PARAMETER") constructor: IrConstructor): IrConstructor {
val newFlattenedParameters = makeGroupedValueParametersFrom(constructor, includeDispatcherReceiver = false)
val newFlattenedParameters = makeGroupedValueParametersFrom(constructor, includeDispatcherReceiver = false, mapOf())
bindingOldFunctionToParameterTemplateStructure[constructor] = newFlattenedParameters
val newConstructor = irFactory.buildConstructor {
updateFrom(constructor)
returnType = constructor.returnType
}.apply {
parent = constructor.parent
copyTypeParametersFrom(constructor)
valueParameters = makeValueParametersFromTemplate(newFlattenedParameters)
annotations = constructor.annotations
parent = constructor.parent
originalConstructorForConstructorReplacement[this] = constructor
}
bindingNewFunctionToParameterTemplateStructure[newConstructor] = newFlattenedParameters
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassTree.InternalNode
import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassTree.Leaf
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.ir.isMultiFieldValueClassType
import org.jetbrains.kotlin.backend.jvm.ir.upperBound
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
@@ -25,87 +26,75 @@ import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrGetField
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.isSubtypeOf
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
sealed class MultiFieldValueClassTree<out TI, out TL> {
sealed class MultiFieldValueClassTree {
abstract val type: IrType
val irClass: IrClass
get() = type.erasedUpperBound
companion object {
@JvmStatic
fun <TI, TL> create(
type: IrType,
defaultInternalNodeValue: TI,
defaultLeafValue: TL,
replacements: MemoizedMultiFieldValueClassReplacements
) =
if (!type.isNullable() && type.isMultiFieldValueClassType())
InternalNode(type, defaultInternalNodeValue, DescriptorVisibilities.PUBLIC, defaultLeafValue, replacements)
else
Leaf(type, defaultLeafValue)
@JvmStatic
fun create(type: IrType, replacements: MemoizedMultiFieldValueClassReplacements) = create(type, Unit, Unit, replacements)
fun create(type: IrSimpleType, replacements: MemoizedMultiFieldValueClassReplacements) =
if (!type.isNullable() && type.isMultiFieldValueClassType()) InternalNode(type, DescriptorVisibilities.PUBLIC, replacements)
else Leaf(type)
}
class Leaf<out TL>(override val type: IrType, val leafValue: TL) : MultiFieldValueClassTree<Nothing, TL>() {
class Leaf(override val type: IrType) : MultiFieldValueClassTree() {
init {
require(type.isNullable() || !type.isMultiFieldValueClassType())
}
fun <RL> map(transformLeaf: Leaf<TL>.(TL) -> RL) = Leaf(type, transformLeaf(leafValue))
}
class InternalNode<out TI, out TL> internal constructor(
override val type: IrType, val internalNodeValue: TI, val fields: List<TreeField<TI, TL>>
) : MultiFieldValueClassTree<TI, TL>() {
class InternalNode internal constructor(
override val type: IrSimpleType, val fields: List<TreeField>
) : MultiFieldValueClassTree() {
constructor(
type: IrType, defaultInternalNodeValue: TI, visibility: DescriptorVisibility, defaultLeafValue: TL,
type: IrSimpleType, visibility: DescriptorVisibility,
replacements: MemoizedMultiFieldValueClassReplacements
) : this(type, defaultInternalNodeValue, 0.let {
) : this(type, 0.let {
val valueClassRepresentation = type.erasedUpperBound.valueClassRepresentation as MultiFieldValueClassRepresentation
val primaryConstructor = type.erasedUpperBound.primaryConstructor!!
val propertiesByName = replacements.getOldMFVCProperties(primaryConstructor.constructedClass).associateBy { it.name }
valueClassRepresentation.underlyingPropertyNamesToTypes.map { (name, type) ->
val typeArguments: List<IrTypeArgument> = type.arguments
val typeParameters = type.erasedUpperBound.typeParameters
val substitutionMap = typeParameters.map { it.symbol }.zip(typeArguments)
.mapNotNull { (param, arg) -> if (arg is IrSimpleType) param to arg else null }.toMap()
valueClassRepresentation.underlyingPropertyNamesToTypes.map { (name, representationType) ->
val property = propertiesByName[name]!!
val innerVisibility = property.visibility
val comparison = visibility.compareTo(innerVisibility)
?: error("Expected comparable visibilities but got $visibility and $innerVisibility")
val newVisibility = if (comparison < 0) visibility else innerVisibility
TreeField(name, type, defaultInternalNodeValue, newVisibility, property.annotations, defaultLeafValue, replacements)
TreeField(
name,
representationType.substitute(substitutionMap) as IrSimpleType,
newVisibility,
property.annotations,
replacements
)
}
})
class TreeField<out TI, out TL>(
class TreeField(
val name: Name, val type: IrType, val visibility: DescriptorVisibility, val annotations: List<IrConstructorCall>,
val node: MultiFieldValueClassTree<TI, TL>
val node: MultiFieldValueClassTree
) {
constructor(
name: Name,
type: IrType,
internalNodeValue: TI,
type: IrSimpleType,
visibility: DescriptorVisibility,
annotations: List<IrConstructorCall>,
defaultLeafValue: TL,
replacements: MemoizedMultiFieldValueClassReplacements
) : this(name, type, visibility, annotations, create(type, internalNodeValue, defaultLeafValue, replacements))
fun <RI, RL> map(
transformInternalNode: InternalNode<TI, TL>.(TI) -> RI,
transformLeaf: Leaf<TL>.(TL) -> RL
): TreeField<RI, RL> = TreeField(name, type, visibility, annotations, node.map(transformInternalNode, transformLeaf))
) : this(name, type, visibility, annotations, create(type, replacements))
}
// todo store real fields, cannot restore them
@@ -118,35 +107,12 @@ sealed class MultiFieldValueClassTree<out TI, out TL> {
private val fieldByName = fields.associateBy { it.name }
operator fun get(name: Name) = fieldByName[name]
override fun <RI, RL> map(
transformInternalNode: InternalNode<TI, TL>.(TI) -> RI,
transformLeaf: Leaf<TL>.(TL) -> RL
): InternalNode<RI, RL> = InternalNode(
type = type,
internalNodeValue = transformInternalNode(internalNodeValue),
fields = fields.map { it.map(transformInternalNode, transformLeaf) },
)
}
}
interface Visitor<out T, in TL, in TI> {
fun visitLeaf(treeNode: Leaf<TL>): T
fun visitInternalNode(treeNode: InternalNode<TI, TL>): T
}
fun <T> visit(visitor: Visitor<T, TL, TI>) = when (this) {
is InternalNode -> visitor.visitInternalNode(this)
is Leaf -> visitor.visitLeaf(this)
}
open fun <RI, RL> map(
transformInternalNode: InternalNode<TI, TL>.(TI) -> RI,
transformLeaf: Leaf<TL>.(TL) -> RL
): MultiFieldValueClassTree<RI, RL> = when (this) {
is InternalNode -> map(transformInternalNode, transformLeaf)
is Leaf -> map(transformLeaf)
}
fun MultiFieldValueClassTree.render(): String = type.render() + when (this) {
is InternalNode -> "\n" + fields.joinToString("\n") { "${it.name.render()}: ${it.node.render().prependIndent(" ")}" }
is Leaf -> ""
}
class MultiFieldValueClassSpecificDeclarations(
@@ -162,39 +128,39 @@ class MultiFieldValueClassSpecificDeclarations(
val loweringRepresentation = MultiFieldValueClassTree.create(valueClass.defaultType, replacements) as InternalNode
val leaves: List<Leaf<Unit>> = ArrayList<Leaf<Unit>>().apply {
loweringRepresentation.visit(object : MultiFieldValueClassTree.Visitor<Unit, Unit, Unit> {
override fun visitLeaf(treeNode: Leaf<Unit>) {
add(treeNode)
val leaves: List<Leaf> = ArrayList<Leaf>().apply {
fun proceed(node: MultiFieldValueClassTree) {
when (node) {
is InternalNode -> node.fields.forEach { proceed(it.node) }
is Leaf -> add(node)
}
}
proceed(loweringRepresentation)
if (size <= 1) {
error("${this@MultiFieldValueClassSpecificDeclarations::class.qualifiedName} for $valueClass must have multiple leaves")
}
}
override fun visitInternalNode(treeNode: InternalNode<Unit, Unit>) {
treeNode.fields.forEach { it.node.visit(this) }
val nodeFullNames: Map<MultiFieldValueClassTree, Name> =
mutableMapOf<MultiFieldValueClassTree, Name>().apply {
val parts = mutableListOf<String>()
fun makeName() = Name.guessByFirstCharacter(parts.joinToString("$"))
fun proceed(node: MultiFieldValueClassTree) {
when (node) {
is InternalNode -> {
if (parts.isNotEmpty()) {
put(node, makeName())
}
for (field in node.fields) {
parts.add(field.name.asString())
proceed(field.node)
parts.removeLast()
}
}
is Leaf -> put(node, makeName())
}
}
})
}.also { if (it.size <= 1) error("${this::class.qualifiedName} for $valueClass must have multiple leaves") }
val nodeFullNames: Map<MultiFieldValueClassTree<Unit, Unit>, Name> =
mutableMapOf<MultiFieldValueClassTree<Unit, Unit>, Name>().apply {
loweringRepresentation.visit(object : MultiFieldValueClassTree.Visitor<Unit, Unit, Unit> {
val parts = mutableListOf<String>()
override fun visitLeaf(treeNode: Leaf<Unit>) {
put(treeNode, makeName())
}
override fun visitInternalNode(treeNode: InternalNode<Unit, Unit>) {
if (parts.isNotEmpty()) {
put(treeNode, makeName())
}
for (field in treeNode.fields) {
parts.add(field.name.asString())
field.node.visit(this)
parts.removeLast()
}
}
private fun makeName() = Name.guessByFirstCharacter(parts.joinToString("$"))
})
proceed(loweringRepresentation)
}
init {
@@ -202,24 +168,24 @@ class MultiFieldValueClassSpecificDeclarations(
}
private val indexByLeaf = leaves.withIndex().associate { it.value to it.index }
private val indexesByInternalNode = mutableMapOf<InternalNode<Unit, Unit>, IntRange>().apply {
private val indexesByInternalNode = mutableMapOf<InternalNode, IntRange>().apply {
var index = 0
loweringRepresentation.visit(object : MultiFieldValueClassTree.Visitor<Unit, Unit, Unit> {
override fun visitLeaf(treeNode: Leaf<Unit>) {
index++
}
override fun visitInternalNode(treeNode: InternalNode<Unit, Unit>) {
val start = index
treeNode.fields.forEach { it.node.visit(this) }
val finish = index
val range = start until finish
if (finish - start <= 1) {
error("Invalid multi-field value class indexes range for class $valueClass: ${range}")
fun proceed(node: MultiFieldValueClassTree) {
when (node) {
is InternalNode -> {
val start = index
node.fields.forEach { proceed(it.node) }
val finish = index
val range = start until finish
if (finish - start <= 1) {
error("Invalid multi-field value class indexes range for class $valueClass: ${range}")
}
put(node, range)
}
put(treeNode, range)
is Leaf -> index++
}
})
}
proceed(loweringRepresentation)
}
val oldPrimaryConstructor = valueClass.primaryConstructor ?: error("Value classes have primary constructors")
@@ -236,61 +202,52 @@ class MultiFieldValueClassSpecificDeclarations(
}
}
private val gettersVisibilities = mutableMapOf<MultiFieldValueClassTree<Unit, Unit>, DescriptorVisibility>().apply {
loweringRepresentation.visit(object : MultiFieldValueClassTree.Visitor<Unit, Unit, Unit> {
val stack = mutableListOf<DescriptorVisibility>()
override fun visitLeaf(treeNode: Leaf<Unit>) {
put(treeNode, stack.last())
}
override fun visitInternalNode(treeNode: InternalNode<Unit, Unit>) {
if (stack.isNotEmpty()) {
put(treeNode, stack.last())
}
for (field in treeNode.fields) {
stack.add(field.visibility)
field.node.visit(this)
stack.pop()
private val gettersVisibilities = mutableMapOf<MultiFieldValueClassTree, DescriptorVisibility>().apply {
val stack = mutableListOf<DescriptorVisibility>()
fun proceed(node: MultiFieldValueClassTree) {
when (node) {
is InternalNode -> {
if (stack.isNotEmpty()) {
put(node, stack.last())
}
for (field in node.fields) {
stack.add(field.visibility)
proceed(field.node)
stack.pop()
}
}
is Leaf -> put(node, stack.last())
}
})
}
proceed(loweringRepresentation)
}
private val gettersAnnotations = mutableMapOf<MultiFieldValueClassTree<Unit, Unit>, List<IrConstructorCall>>().apply {
loweringRepresentation.visit(object : MultiFieldValueClassTree.Visitor<Unit, Unit, Unit> {
val stack = mutableListOf<List<IrConstructorCall>>()
override fun visitLeaf(treeNode: Leaf<Unit>) {
put(treeNode, stack.last())
}
private val gettersAnnotations = mutableMapOf<MultiFieldValueClassTree, List<IrConstructorCall>>().apply {
val stack = mutableListOf<List<IrConstructorCall>>()
override fun visitInternalNode(treeNode: InternalNode<Unit, Unit>) {
if (stack.isNotEmpty()) {
put(treeNode, stack.last())
}
for (field in treeNode.fields) {
stack.add(field.annotations)
field.node.visit(this)
stack.pop()
fun proceed(node: MultiFieldValueClassTree) {
when (node) {
is InternalNode -> {
if (stack.isNotEmpty()) {
put(node, stack.last())
}
for (field in node.fields) {
stack.add(field.annotations)
proceed(field.node)
stack.pop()
}
}
is Leaf -> put(node, stack.last())
}
})
}
proceed(loweringRepresentation)
}
private fun IrBuilderWithScope.fieldGetter(receiver: IrValueParameter, field: IrField): IrGetField =
irGetField(irGet(receiver), field)
private fun IrFunction.fieldGetter(field: IrField): IrBuilderWithScope.() -> IrGetField = { fieldGetter(dispatchReceiverParameter!!, field) }
private val selfImplementationAgnosticDeclarations = ImplementationAgnostic(
fields.map {
VirtualProperty(
type = it.type,
makeGetter = { receiver: IrValueParameter -> irGetField(irGet(receiver), it) },
assigner = { receiver: IrValueParameter, value: IrExpression -> irSetField(irGet(receiver), it, value) },
symbol = null,
)
}
)
private fun IrFunction.fieldGetter(field: IrField): IrBuilderWithScope.() -> IrGetField =
{ fieldGetter(dispatchReceiverParameter!!, field) }
val primaryConstructor: IrConstructor = irFactory.buildConstructor {
updateFrom(oldPrimaryConstructor)
@@ -298,8 +255,10 @@ class MultiFieldValueClassSpecificDeclarations(
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER
returnType = oldPrimaryConstructor.returnType
}.apply {
copyTypeParametersFrom(oldPrimaryConstructor)
addFlattenedClassRepresentationToParameters()
require(oldPrimaryConstructor.typeParameters.isEmpty()) {
"Constructors do not support type parameters yet"
}
addFlattenedClassRepresentationToParameters(mapOf())
val irConstructor = this@apply
parent = valueClass
body = context.createIrBuilder(irConstructor.symbol).irBlockBody(irConstructor) {
@@ -322,11 +281,14 @@ class MultiFieldValueClassSpecificDeclarations(
modality = Modality.FINAL
}.apply {
parent = valueClass
copyTypeParametersFrom(oldPrimaryConstructor)
addFlattenedClassRepresentationToParameters()
copyTypeParametersFrom(valueClass)
addFlattenedClassRepresentationToParameters(classToFunctionTypeParametersMapping())
// body is added in Lowering file
}
private fun IrSimpleFunction.classToFunctionTypeParametersMapping() =
valueClass.typeParameters.map { it.symbol }.zip(typeParameters.map { it.defaultType }).toMap()
val boxMethod = irFactory.buildFun {
name = Name.identifier(KotlinTypeMapper.BOX_JVM_METHOD_NAME)
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER
@@ -334,33 +296,46 @@ class MultiFieldValueClassSpecificDeclarations(
}.apply {
parent = valueClass
copyTypeParametersFrom(valueClass)
addFlattenedClassRepresentationToParameters()
addFlattenedClassRepresentationToParameters(classToFunctionTypeParametersMapping())
// body is added in Lowering file
}
private fun IrFunction.addFlattenedClassRepresentationToParameters() {
private fun IrFunction.addFlattenedClassRepresentationToParameters(substitutionMap: Map<IrTypeParameterSymbol, IrType>) {
for (leaf in leaves) {
addValueParameter {
this.name = nodeFullNames[leaf]!!
this.type = leaf.type
this.type = leaf.type.substitute(substitutionMap)
}
}
}
val specializedEqualsMethod = irFactory.buildFun {
name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
// TODO: Revisit this once we allow user defined equals methods in inline classes.
// TODO: Revisit this once we allow user defined equals methods in value classes.
origin = JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD
returnType = context.irBuiltIns.booleanType
}.apply {
parent = valueClass
copyTypeParametersFrom(oldPrimaryConstructor)
copyTypeParametersFrom(valueClass)
val typeParametersHalf1 = typeParameters.apply {
for (it in this) {
it.name = Name.guessByFirstCharacter("${it.name.asString()}1")
}
}
val substitutionMapHalf1 = (valueClass.typeParameters.map { it.symbol } zip typeParametersHalf1.map { it.defaultType }).toMap()
copyTypeParametersFrom(valueClass)
val typeParametersHalf2 = typeParameters.drop(typeParametersHalf1.size).apply {
for (it in this) {
it.name = Name.guessByFirstCharacter("${it.name.asString()}2")
}
}
val substitutionMapHalf2 = (valueClass.typeParameters.map { it.symbol } zip typeParametersHalf2.map { it.defaultType }).toMap()
for (leaf in leaves) {
addValueParameter {
this.name = Name.guessByFirstCharacter(
"${InlineClassDescriptorResolver.SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME}$${nodeFullNames[leaf]!!.asString()}"
)
this.type = leaf.type
this.type = leaf.type.substitute(substitutionMapHalf1)
}
}
for (leaf in leaves) {
@@ -368,7 +343,7 @@ class MultiFieldValueClassSpecificDeclarations(
this.name = Name.guessByFirstCharacter(
"${InlineClassDescriptorResolver.SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME}$${nodeFullNames[leaf]!!.asString()}"
)
this.type = leaf.type
this.type = leaf.type.substitute(substitutionMapHalf2)
}
}
// body is added in Lowering file
@@ -388,9 +363,10 @@ class MultiFieldValueClassSpecificDeclarations(
}
}
val properties: Map<MultiFieldValueClassTree<Unit, Unit>, IrProperty> =
selfImplementationAgnosticDeclarations.nodeToExpressionGetters.filterKeys { it in nodeFullNames }.mapValues { (node, getter) ->
val propertyName = nodeFullNames[node]!!
val properties: Map<MultiFieldValueClassTree, IrProperty> = run {
val nodes2expressions: Map<MultiFieldValueClassTree, IrBuilderWithScope.(function: IrFunction) -> IrExpression> =
makeNodes2FieldExpressions(fields)
nodeFullNames.mapValues { (node, propertyName) ->
val overrideable = oldProperties[propertyName]
irFactory.buildProperty {
name = propertyName
@@ -404,42 +380,75 @@ class MultiFieldValueClassSpecificDeclarations(
returnType = node.type
origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER
}.apply {
val function = this
overrideable?.getter?.overriddenSymbols?.let { overriddenSymbols = it }
createDispatchReceiverParameter()
body = with(context.createIrBuilder(this.symbol)) {
irExprBody(getter(this, dispatchReceiverParameter!!))
irExprBody(nodes2expressions[node]!!.invoke(this, function))
}
}
}
}
}
data class VirtualProperty<in T>(
companion object {
private fun IrFunctionAccessExpression.copyTypeArgumentsFromType(
type: IrSimpleType, valueClass: IrClass, substitutionMap: Map<IrTypeParameterSymbol, IrType>
) {
require(type.erasedUpperBound == valueClass) {
"Illegal bound ${type.erasedUpperBound.render()} instead of ${valueClass.render()}"
}
require(type.arguments.size == valueClass.typeParameters.size) {
"Unexpected number of type arguments: ${type.arguments.size} for ${valueClass.typeParameters.size} parameters"
}
require(type.arguments.size == symbol.owner.typeParameters.size) {
"Unexpected number of type arguments: ${type.arguments.size} for ${symbol.owner.typeParameters.size} parameters"
}
for (i in valueClass.typeParameters.indices) {
putTypeArgument(i, type.arguments[i].typeOrNull?.substitute(substitutionMap))
}
}
}
fun makeNodes2FieldExpressions(fields: List<IrField>): Map<MultiFieldValueClassTree, IrBuilderWithScope.(IrFunction) -> IrExpression> {
fun proceed(node: MultiFieldValueClassTree): Map<MultiFieldValueClassTree, IrBuilderWithScope.(function: IrFunction) -> IrExpression> =
when (node) {
is InternalNode -> buildMap {
val innerDeclarations =
if (node.irClass == valueClass) this@MultiFieldValueClassSpecificDeclarations
else replacements.getDeclarations(node.irClass)!!
put(node) { function ->
irCall(innerDeclarations.boxMethod).apply {
copyTypeArgumentsFromType(node.type, node.irClass, mapOf())
indexesByInternalNode[node]!!.forEachIndexed { index, nodeIndex ->
putValueArgument(index, fieldGetter(function.dispatchReceiverParameter!!, fields[nodeIndex]))
}
}
}
node.fields.forEach { putAll(proceed(it.node)) }
}
is Leaf -> mapOf(node to { fieldGetter(it.dispatchReceiverParameter!!, fields[indexByLeaf[node]!!]) })
}
return proceed(loweringRepresentation)
}
data class VirtualProperty(
val type: IrType,
val makeGetter: ExpressionGenerator<T>,
val assigner: ExpressionSupplier<T>?,
val makeGetter: ExpressionGenerator,
val assigner: ExpressionSupplier?,
val symbol: IrValueSymbol?
) {
constructor(declaration: IrValueDeclaration) : this(
declaration.type,
{ irGet(declaration) },
if (declaration.isAssignable) { _, value -> irSet(declaration, value) } else null,
if (declaration.isAssignable) { value -> irSet(declaration, value) } else null,
declaration.symbol,
)
val isAssignable: Boolean
get() = assigner != null
fun <R> map(f: (R) -> T) = VirtualProperty<R>(
type = type,
makeGetter = { makeGetter(f(it)) },
assigner = assigner?.let { assigner -> { additionalParam, value -> assigner(f(additionalParam), value) } },
symbol = symbol,
)
}
fun implementationAgnosticFromDeclarations(declarations: List<IrValueDeclaration>) =
ImplementationAgnostic<Any?>(declarations.map { VirtualProperty(it) })
/**
* Multi-field value class instance can be not only a standalone instance but also:
* 1. A slice of fields in another multi-field class instance;
@@ -449,28 +458,31 @@ class MultiFieldValueClassSpecificDeclarations(
* 5. Virtual instance constructed with local fields;
* 6. Some mix of the above ones.
*/
inner class ImplementationAgnostic<T>(val virtualFields: List<VirtualProperty<T>>) {
inner class ImplementationAgnostic constructor(val type: IrSimpleType, val virtualFields: List<VirtualProperty>) {
val regularDeclarations = this@MultiFieldValueClassSpecificDeclarations
val symbols = virtualFields.map { it.symbol }
fun <R> map(f: (R) -> T) = ImplementationAgnostic(virtualFields.map { it.map(f) })
private val substitutionMap = valueClass.typeParameters.map { it.symbol }.zip(type.arguments.map { it.typeOrNull })
.mapNotNull { (parameter, type) -> if (type != null) parameter to type else null }.toMap()
init {
require(virtualFields.size == leaves.size) { "Wrong symbols number given for $leaves, got $virtualFields" }
for ((actual, expected) in virtualFields.map { it.type } zip leaves.map { it.type }) {
require(actual.isSubtypeOf(expected, typeSystemContext)) { "$actual is not a subtype of $expected for MFVC $valueClass" }
require(actual.upperBound.isSubtypeOf(expected.upperBound, typeSystemContext)) {
"${actual.render()} is not a subtype of ${expected.render()} for MFVC ${valueClass.render()}"
}
}
}
private val nodeToSymbols = indexesByInternalNode.mapValues { (_, indexes) -> virtualFields.slice(indexes) } +
indexByLeaf.mapValues { (_, index) -> listOf(virtualFields[index]) }
private val internalNodeToExpressionGetters: Map<InternalNode<Unit, Unit>, ExpressionGenerator<T>> =
private val internalNodeToExpressionGetters: Map<InternalNode, ExpressionGenerator> =
indexesByInternalNode.mapValues { (node, indexes) ->
{ additionalParameter ->
val arguments = virtualFields.slice(indexes).map { it.makeGetter(this, additionalParameter) }
{
val arguments = virtualFields.slice(indexes).map { it.makeGetter(this) }
val innerDeclarations = replacements.getDeclarations(node.irClass)!!
irCall(innerDeclarations.boxMethod).apply {
copyTypeArgumentsFromType(node.type, node.irClass, substitutionMap)
for (i in arguments.indices) {
putValueArgument(i, arguments[i])
}
@@ -478,13 +490,13 @@ class MultiFieldValueClassSpecificDeclarations(
}
}
private val leavesToExpressionGetters: Map<Leaf<Unit>, ExpressionGenerator<T>> =
indexByLeaf.mapValues { (_, index) -> { virtualFields[index].makeGetter(this, it) } }
private val leavesToExpressionGetters: Map<Leaf, ExpressionGenerator> =
indexByLeaf.mapValues { (_, index) -> { virtualFields[index].makeGetter(this) } }
val nodeToExpressionGetters: Map<MultiFieldValueClassTree<Unit, Unit>, ExpressionGenerator<T>> =
val nodeToExpressionGetters: Map<MultiFieldValueClassTree, ExpressionGenerator> =
internalNodeToExpressionGetters + leavesToExpressionGetters
operator fun get(name: Name): Pair<ExpressionGenerator<T>, ImplementationAgnostic<T>?>? =
operator fun get(name: Name): Pair<ExpressionGenerator, ImplementationAgnostic?>? =
when (val field = loweringRepresentation[name]) {
null -> null
else -> nodeToExpressionGetters[field.node]!! to when (field.node) {
@@ -492,24 +504,17 @@ class MultiFieldValueClassSpecificDeclarations(
is InternalNode -> {
val irClass = field.node.irClass
val declarations = replacements.getDeclarations(irClass)!!
declarations.ImplementationAgnostic(nodeToSymbols[field.node]!!)
declarations.ImplementationAgnostic(field.type as IrSimpleType, nodeToSymbols[field.node]!!)
}
}
}
val boxedExpression = nodeToExpressionGetters[loweringRepresentation]!!
// todo type parameters
// todo recursive
// todo cheeeck!!!
// todo equals
// todo toString
// todo hashCode
// todo default parameters
// todo annotations etc.
// todo inline in not value class
}
}
typealias ExpressionGenerator<T> = IrBuilderWithScope.(T) -> IrExpression
typealias ExpressionSupplier<T> = IrBuilderWithScope.(T, IrExpression) -> IrStatement
typealias ExpressionGenerator = IrBuilderWithScope.() -> IrExpression
typealias ExpressionSupplier = IrBuilderWithScope.(IrExpression) -> IrStatement
@@ -12,8 +12,23 @@ interface AbstractPoint<T> {
val y: T
}
interface SomeInterface<X> {
fun <T, R: X> someFunction1(x: X, t: T, r: R) = Unit
fun <T, R: X> someFunction2(x: XPoint<X>, t: XPoint<T>, r: XPoint<R>) = Unit
}
@JvmInline
value class XPoint<X>(override val x: X, override val y: X): AbstractPoint<X>
value class XPoint<X>(override val x: X, override val y: X): AbstractPoint<X>, SomeInterface<X> {
override fun <T, R: X> someFunction1(x: X, t: T, r: R) = Unit
override fun <T, R: X> someFunction2(x: XPoint<X>, t: XPoint<T>, r: XPoint<R>) = Unit
}
@JvmInline
value class YPoint<X>(val x: X)
fun <S: List<Int>> genericFunctionMFVC(v: XPoint<S>) {}
fun <S: List<Int>> genericFunctionIC(v: YPoint<S>) {}
interface GenericMFVCHolder<T> {
var p: T
@@ -2354,6 +2354,8 @@ public final class Overrides_typeParametersKt {
inner (anonymous) class Overrides_typeParametersKt$box$xPointLam$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method equal(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function0, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function0): void
public final static method genericFunctionIC-lld4ICU(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void
public final static method genericFunctionMFVC-sUp7gFk(@org.jetbrains.annotations.NotNull p0: java.util.List, @org.jetbrains.annotations.NotNull p1: java.util.List): void
}
@kotlin.Metadata
@@ -2443,6 +2445,22 @@ public interface ReifiedMFVCHolderWithImpls {
public final inner class ReifiedMFVCHolderWithImpls$DefaultImpls
}
@kotlin.Metadata
public final class SomeInterface$DefaultImpls {
// source: 'overrides_typeParameters.kt'
public static method someFunction1(@org.jetbrains.annotations.NotNull p0: SomeInterface, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object): void
public static method someFunction2-lIoT8es(@org.jetbrains.annotations.NotNull p0: SomeInterface, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object, p4: java.lang.Object, p5: java.lang.Object, p6: java.lang.Object): void
public final inner class SomeInterface$DefaultImpls
}
@kotlin.Metadata
public interface SomeInterface {
// source: 'overrides_typeParameters.kt'
public abstract method someFunction1(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object): void
public abstract method someFunction2-lIoT8es(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object, p4: java.lang.Object, p5: java.lang.Object): void
public final inner class SomeInterface$DefaultImpls
}
@kotlin.Metadata
public interface SomePointInterface {
// source: 'overrides_typeParameters.kt'
@@ -2481,6 +2499,10 @@ public final class XPoint {
public final method getY(): java.lang.Object
public method hashCode(): int
public static method hashCode-impl(p0: java.lang.Object, p1: java.lang.Object): int
public method someFunction1(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object): void
public static method someFunction1-impl(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object, p4: java.lang.Object): void
public method someFunction2-lIoT8es(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object, p4: java.lang.Object, p5: java.lang.Object): void
public static method someFunction2-lIoT8es(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object, p4: java.lang.Object, p5: java.lang.Object, p6: java.lang.Object, p7: java.lang.Object): void
public @org.jetbrains.annotations.NotNull method toString(): java.lang.String
public static method toString-impl(p0: java.lang.Object, p1: java.lang.Object): java.lang.String
public synthetic final method unbox-impl0(): java.lang.Object
@@ -2554,3 +2576,22 @@ public final class XSegment {
public synthetic final method unbox-impl2(): java.lang.Object
public synthetic final method unbox-impl3(): java.lang.Object
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class YPoint {
// source: 'overrides_typeParameters.kt'
private final field x: java.lang.Object
private synthetic method <init>(p0: java.lang.Object): void
public synthetic final static method box-impl(p0: java.lang.Object): YPoint
public static @org.jetbrains.annotations.NotNull method constructor-impl(p0: java.lang.Object): java.lang.Object
public method equals(p0: java.lang.Object): boolean
public static method equals-impl(p0: java.lang.Object, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: java.lang.Object, p1: java.lang.Object): boolean
public final method getX(): java.lang.Object
public method hashCode(): int
public static method hashCode-impl(p0: java.lang.Object): int
public method toString(): java.lang.String
public static method toString-impl(p0: java.lang.Object): java.lang.String
public synthetic final method unbox-impl(): java.lang.Object
}