diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index ba3f3e1a1f9..498a601d5a8 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt @@ -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++])) } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt index d166824b715..00208a60e16 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt @@ -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++])) diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt index 8b8937b7d1b..03e72b7ed76 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt @@ -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>() - private val symbol2setters = mutableMapOf?>>() - private val knownExpressions = mutableMapOf>() + private val symbol2getter = mutableMapOf() + private val symbol2setters = mutableMapOf>() + private val knownExpressions = mutableMapOf() 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>, + unboxed: List, declarations: MultiFieldValueClassSpecificDeclarations ): Unit = - remapSymbol(original, declarations.ImplementationAgnostic(unboxed)) + remapSymbol(original, declarations.ImplementationAgnostic(original.owner.type as IrSimpleType, unboxed)) - fun remapSymbol(original: IrValueSymbol, unboxed: ImplementationAgnostic) { + 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?>? = symbol2setters[original] + fun setter(original: IrValueSymbol): List? = symbol2setters[original] - fun implementationAgnostic(expression: IrExpression): ImplementationAgnostic? = 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) { + fun registerExpression(getter: IrExpression, representation: ImplementationAgnostic) { knownExpressions[getter] = representation } } @@ -108,7 +104,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV private val regularClassMFVCPropertyMainGetters: MutableMap> = mutableMapOf() private val regularClassMFVCPropertyNextGetters: MutableMap> = mutableMapOf() private val regularClassMFVCPropertyFieldsMapping: MutableMap> = mutableMapOf() - private val regularClassMFVCPropertyNodes: MutableMap> = mutableMapOf() + private val regularClassMFVCPropertyNodes: MutableMap = mutableMapOf() private val regularClassMFVCPropertyAllPrimitiveGetters: MutableSet = mutableSetOf() override val replacements @@ -148,7 +144,6 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV val oldFieldToInitializers: MutableMap = mutableMapOf() // we need to preserve order of initializations - // todo test it val newFields: List> = 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> = 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) { + 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? = replacements.bindingNewFunctionToParameterTemplateStructure[source] val targetStructure: List? = 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? = when (val node = regularClassMFVCPropertyNodes[currentGetter]) { null -> null - is MultiFieldValueClassTree.Leaf -> 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 = 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.toGettersAndSetters() = map { variable -> - Pair, ExpressionSupplier>( + Pair( { irGet(variable) }, - { _, value: IrExpression -> irSet(variable, value) } + { value: IrExpression -> irSet(variable, value) } ) } private fun List.toGettersAndSetters(receiver: IrValueParameter, transformReceiver: Boolean = false) = map { field -> - Pair, ExpressionSupplier>( + Pair( { 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, ExpressionSupplier>> + expression: IrExpression, variables: List> ) { 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) }) } } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt index 8b896cf7e84..40e8064b5b8 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt @@ -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) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt index 73bcfb88ac7..c09ca4f79c1 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt @@ -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.grouped( + substitutionMap: Map, originWhenFlattenedAndNotSpecified: IrDeclarationOrigin? = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER ): List = 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 ): List { val newFlattenedParameters = mutableListOf() 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, ) : RemappedParameter() { init { @@ -181,41 +185,53 @@ class MemoizedMultiFieldValueClassReplacements( } val bindingOldFunctionToParameterTemplateStructure: MutableMap> = ConcurrentHashMap() - val bindingNewFunctionToParameterTemplateStructure: MutableMap> = ConcurrentHashMap() - + val bindingNewFunctionToParameterTemplateStructure: MutableMap> = + object : ConcurrentHashMap>() { + override fun put(key: IrFunction, value: List): List? { + 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 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MultiFieldValueClassSpecificDeclarations.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MultiFieldValueClassSpecificDeclarations.kt index 23fca569f14..6cbc3c95831 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MultiFieldValueClassSpecificDeclarations.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MultiFieldValueClassSpecificDeclarations.kt @@ -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 { +sealed class MultiFieldValueClassTree { abstract val type: IrType val irClass: IrClass get() = type.erasedUpperBound companion object { @JvmStatic - fun 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(override val type: IrType, val leafValue: TL) : MultiFieldValueClassTree() { + class Leaf(override val type: IrType) : MultiFieldValueClassTree() { init { require(type.isNullable() || !type.isMultiFieldValueClassType()) } - - fun map(transformLeaf: Leaf.(TL) -> RL) = Leaf(type, transformLeaf(leafValue)) } - class InternalNode internal constructor( - override val type: IrType, val internalNodeValue: TI, val fields: List> - ) : MultiFieldValueClassTree() { + class InternalNode internal constructor( + override val type: IrSimpleType, val fields: List + ) : 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 = 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( + class TreeField( val name: Name, val type: IrType, val visibility: DescriptorVisibility, val annotations: List, - val node: MultiFieldValueClassTree + val node: MultiFieldValueClassTree ) { constructor( name: Name, - type: IrType, - internalNodeValue: TI, + type: IrSimpleType, visibility: DescriptorVisibility, annotations: List, - defaultLeafValue: TL, replacements: MemoizedMultiFieldValueClassReplacements - ) : this(name, type, visibility, annotations, create(type, internalNodeValue, defaultLeafValue, replacements)) - - fun map( - transformInternalNode: InternalNode.(TI) -> RI, - transformLeaf: Leaf.(TL) -> RL - ): TreeField = 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 { private val fieldByName = fields.associateBy { it.name } operator fun get(name: Name) = fieldByName[name] - - override fun map( - transformInternalNode: InternalNode.(TI) -> RI, - transformLeaf: Leaf.(TL) -> RL - ): InternalNode = InternalNode( - type = type, - internalNodeValue = transformInternalNode(internalNodeValue), - fields = fields.map { it.map(transformInternalNode, transformLeaf) }, - ) - } +} - interface Visitor { - fun visitLeaf(treeNode: Leaf): T - fun visitInternalNode(treeNode: InternalNode): T - } - - fun visit(visitor: Visitor) = when (this) { - is InternalNode -> visitor.visitInternalNode(this) - is Leaf -> visitor.visitLeaf(this) - } - - open fun map( - transformInternalNode: InternalNode.(TI) -> RI, - transformLeaf: Leaf.(TL) -> RL - ): MultiFieldValueClassTree = 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> = ArrayList>().apply { - loweringRepresentation.visit(object : MultiFieldValueClassTree.Visitor { - override fun visitLeaf(treeNode: Leaf) { - add(treeNode) + val leaves: List = ArrayList().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) { - treeNode.fields.forEach { it.node.visit(this) } + val nodeFullNames: Map = + mutableMapOf().apply { + val parts = mutableListOf() + 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, Name> = - mutableMapOf, Name>().apply { - loweringRepresentation.visit(object : MultiFieldValueClassTree.Visitor { - val parts = mutableListOf() - override fun visitLeaf(treeNode: Leaf) { - put(treeNode, makeName()) - } - - override fun visitInternalNode(treeNode: InternalNode) { - 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, IntRange>().apply { + private val indexesByInternalNode = mutableMapOf().apply { var index = 0 - loweringRepresentation.visit(object : MultiFieldValueClassTree.Visitor { - override fun visitLeaf(treeNode: Leaf) { - index++ - } - - override fun visitInternalNode(treeNode: InternalNode) { - 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, DescriptorVisibility>().apply { - loweringRepresentation.visit(object : MultiFieldValueClassTree.Visitor { - val stack = mutableListOf() - override fun visitLeaf(treeNode: Leaf) { - put(treeNode, stack.last()) - } - - override fun visitInternalNode(treeNode: InternalNode) { - 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().apply { + val stack = mutableListOf() + 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, List>().apply { - loweringRepresentation.visit(object : MultiFieldValueClassTree.Visitor { - val stack = mutableListOf>() - override fun visitLeaf(treeNode: Leaf) { - put(treeNode, stack.last()) - } + private val gettersAnnotations = mutableMapOf>().apply { + val stack = mutableListOf>() - override fun visitInternalNode(treeNode: InternalNode) { - 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) { 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, IrProperty> = - selfImplementationAgnosticDeclarations.nodeToExpressionGetters.filterKeys { it in nodeFullNames }.mapValues { (node, getter) -> - val propertyName = nodeFullNames[node]!! + val properties: Map = run { + val nodes2expressions: Map 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( + companion object { + private fun IrFunctionAccessExpression.copyTypeArgumentsFromType( + type: IrSimpleType, valueClass: IrClass, substitutionMap: Map + ) { + 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): Map IrExpression> { + fun proceed(node: MultiFieldValueClassTree): Map 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, - val assigner: ExpressionSupplier?, + 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 map(f: (R) -> T) = VirtualProperty( - type = type, - makeGetter = { makeGetter(f(it)) }, - assigner = assigner?.let { assigner -> { additionalParam, value -> assigner(f(additionalParam), value) } }, - symbol = symbol, - ) } - fun implementationAgnosticFromDeclarations(declarations: List) = - ImplementationAgnostic(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(val virtualFields: List>) { + inner class ImplementationAgnostic constructor(val type: IrSimpleType, val virtualFields: List) { val regularDeclarations = this@MultiFieldValueClassSpecificDeclarations val symbols = virtualFields.map { it.symbol } - - fun 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, ExpressionGenerator> = + private val internalNodeToExpressionGetters: Map = 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, ExpressionGenerator> = - indexByLeaf.mapValues { (_, index) -> { virtualFields[index].makeGetter(this, it) } } + private val leavesToExpressionGetters: Map = + indexByLeaf.mapValues { (_, index) -> { virtualFields[index].makeGetter(this) } } - val nodeToExpressionGetters: Map, ExpressionGenerator> = + val nodeToExpressionGetters: Map = internalNodeToExpressionGetters + leavesToExpressionGetters - operator fun get(name: Name): Pair, ImplementationAgnostic?>? = + operator fun get(name: Name): Pair? = 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 = IrBuilderWithScope.(T) -> IrExpression -typealias ExpressionSupplier = IrBuilderWithScope.(T, IrExpression) -> IrStatement \ No newline at end of file +typealias ExpressionGenerator = IrBuilderWithScope.() -> IrExpression +typealias ExpressionSupplier = IrBuilderWithScope.(IrExpression) -> IrStatement \ No newline at end of file diff --git a/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.kt b/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.kt index ab2f95d1ae0..ecb515cdfbd 100644 --- a/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.kt +++ b/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.kt @@ -12,8 +12,23 @@ interface AbstractPoint { val y: T } +interface SomeInterface { + fun someFunction1(x: X, t: T, r: R) = Unit + fun someFunction2(x: XPoint, t: XPoint, r: XPoint) = Unit + +} + @JvmInline -value class XPoint(override val x: X, override val y: X): AbstractPoint +value class XPoint(override val x: X, override val y: X): AbstractPoint, SomeInterface { + override fun someFunction1(x: X, t: T, r: R) = Unit + override fun someFunction2(x: XPoint, t: XPoint, r: XPoint) = Unit +} + +@JvmInline +value class YPoint(val x: X) + +fun > genericFunctionMFVC(v: XPoint) {} +fun > genericFunctionIC(v: YPoint) {} interface GenericMFVCHolder { var p: T diff --git a/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.txt b/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.txt index 5e1b04dfa71..9d3ff1f850c 100644 --- a/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.txt +++ b/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.txt @@ -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 (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 +}