[IR] Implement MFVC basic flattening

#KT-1179
This commit is contained in:
Evgeniy.Zhelenskiy
2022-04-01 01:23:41 +03:00
committed by teamcity
parent 93ffd9f233
commit 2896f5613d
24 changed files with 2030 additions and 224 deletions
@@ -301,14 +301,16 @@ private fun MethodInsnNode.isInlineClassBoxingMethodDescriptor(state: Generation
if (name != KotlinTypeMapper.BOX_JVM_METHOD_NAME) return false
val ownerType = Type.getObjectType(owner)
return desc == Type.getMethodDescriptor(ownerType, unboxedTypeOfInlineClass(ownerType, state))
val unboxedType = unboxedTypeOfInlineClass(ownerType, state) ?: return false
return desc == Type.getMethodDescriptor(ownerType, unboxedType)
}
private fun MethodInsnNode.isInlineClassUnboxingMethodDescriptor(state: GenerationState): Boolean {
if (name != KotlinTypeMapper.UNBOX_JVM_METHOD_NAME) return false
val ownerType = Type.getObjectType(owner)
return desc == Type.getMethodDescriptor(unboxedTypeOfInlineClass(ownerType, state))
val unboxedType = unboxedTypeOfInlineClass(ownerType, state) ?: return false
return desc == Type.getMethodDescriptor(unboxedType)
}
fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) =
@@ -50194,6 +50194,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
public void testEquality() throws Exception {
runTest("compiler/testData/codegen/box/valueClasses/equality.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/valueClasses/simple.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
}
}
@Nested
@@ -346,6 +346,7 @@ abstract class AnnotationCodegen(
declaration.origin.isSynthetic ->
true
declaration.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
declaration.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
declaration.origin == IrDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION ->
true
else ->
@@ -294,6 +294,7 @@ class ExpressionCodegen(
(irFunction is IrConstructor && irFunction.parentAsClass.isAnonymousObject) ||
// TODO: Implement this as a lowering, so that we can more easily exclude generated methods.
irFunction.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
irFunction.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
// Although these are accessible from Java, the functions they bridge to already have the assertions.
irFunction.origin == IrDeclarationOrigin.BRIDGE_SPECIAL ||
irFunction.origin == JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE ||
@@ -37,9 +37,10 @@ object HashCode : IntrinsicMethod() {
val target = context.state.target
when {
irFunction.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
irFunction.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
irFunction.origin == IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER ||
irFunction.origin == IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER -> {
// TODO generate or lower IR for data class / inline class 'hashCode'?
// TODO generate or lower IR for data class / value class 'hashCode'?
DescriptorAsmUtil.genHashCode(mv, mv, receiverType, target)
}
target >= JvmTarget.JVM_1_8 && AsmUtil.isPrimitive(receiverJvmType) -> {
@@ -309,6 +309,7 @@ private val jvmFilePhases = listOf(
forLoopsPhase,
collectionStubMethodLowering,
singleAbstractMethodPhase,
jvmMultiFieldValueClassPhase,
jvmInlineClassPhase,
tailrecPhase,
// makePatchParentsPhase(),
@@ -112,7 +112,7 @@ internal val bridgePhase = makeIrFilePhase(
::BridgeLowering,
name = "Bridge",
description = "Generate bridges",
prerequisite = setOf(jvmInlineClassPhase)
prerequisite = setOf(jvmInlineClassPhase, jvmMultiFieldValueClassPhase)
)
internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
@@ -9,8 +9,6 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.builtins.StandardNames
@@ -24,6 +22,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.transformStatement
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
@@ -43,7 +42,9 @@ val jvmInlineClassPhase = makeIrFilePhase(
// Standard library replacements are done on the unmangled names for UInt and ULong classes.
// Collection stubs may require mangling by inline class rules.
// SAM wrappers may require mangling for fun interfaces with inline class parameters
prerequisite = setOf(forLoopsPhase, jvmBuiltInsPhase, collectionStubMethodLowering, singleAbstractMethodPhase),
prerequisite = setOf(
forLoopsPhase, jvmBuiltInsPhase, collectionStubMethodLowering, singleAbstractMethodPhase, jvmMultiFieldValueClassPhase
),
)
/**
@@ -58,11 +59,56 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
override val replacements: MemoizedValueClassAbstractReplacements
get() = context.inlineClassReplacements
private val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>()
override fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
for ((param, newParam) in original.explicitParameters.zip(replacement.explicitParameters)) {
valueMap[param.symbol] = newParam
}
}
override fun IrClass.isSpecificLoweringLogicApplicable(): Boolean = isSingleFieldValueClass
override fun IrFunction.isSpecificFieldGetter(): Boolean = isInlineClassFieldGetter
override fun IrFunction.isFieldGetterToRemove(): Boolean = isInlineClassFieldGetter
override fun visitClassNew(declaration: IrClass): IrStatement {
// The arguments to the primary constructor are in scope in the initializers of IrFields.
override fun addJvmInlineAnnotation(valueClass: IrClass) {
declaration.primaryConstructor?.let {
replacements.getReplacementFunction(it)?.let { replacement -> addBindingsFor(it, replacement) }
}
declaration.transformDeclarationsFlat { memberDeclaration ->
if (memberDeclaration is IrFunction) {
withinScope(memberDeclaration) {
transformFunctionFlat(memberDeclaration)
}
} else {
memberDeclaration.accept(this, null)
null
}
}
if (declaration.isSpecificLoweringLogicApplicable()) {
handleSpecificNewClass(declaration)
}
return declaration
}
override fun handleSpecificNewClass(declaration: IrClass) {
val irConstructor = declaration.primaryConstructor!!
// The field getter is used by reflection and cannot be removed here unless it is internal.
declaration.declarations.removeIf {
it == irConstructor || (it is IrFunction && it.isFieldGetterToRemove() && !it.visibility.isPublicAPI)
}
buildPrimaryInlineClassConstructor(declaration, irConstructor)
buildBoxFunction(declaration)
buildUnboxFunction(declaration)
buildSpecializedEqualsMethod(declaration)
addJvmInlineAnnotation(declaration)
}
fun addJvmInlineAnnotation(valueClass: IrClass) {
if (valueClass.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME)) return
val constructor = context.ir.symbols.jvmInlineAnnotation.constructors.first()
valueClass.annotations = valueClass.annotations + IrConstructorCallImpl.fromSymbolOwner(
@@ -71,20 +117,10 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
)
}
override fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach {
it.transformChildrenVoid()
it.defaultValue?.patchDeclarationParents(replacement)
}
allScopes.push(createScope(function))
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
allScopes.pop()
replacement.copyAttributes(function)
// Don't create a wrapper for functions which are only used in an unboxed context
if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null)
return listOf(replacement)
override fun createBridgeFunction(
function: IrSimpleFunction,
replacement: IrSimpleFunction
): IrSimpleFunction {
val bridgeFunction = createBridgeDeclaration(
function,
when {
@@ -113,8 +149,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
} else {
createBridgeBody(bridgeFunction, replacement)
}
return listOf(replacement, bridgeFunction)
return bridgeFunction
}
private fun IrSimpleFunction.signatureRequiresMangling() =
@@ -153,7 +188,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
// Secondary constructors for boxed types get translated to static functions returning
// unboxed arguments. We remove the original constructor.
// Primary constructors' case is handled at the start of transformFunctionFlat
override fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
override fun transformSecondaryConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach { it.transformChildrenVoid() }
replacement.body = context.createIrBuilder(replacement.symbol, replacement.startOffset, replacement.endOffset).irBlockBody(
replacement
@@ -211,12 +246,6 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
return listOf(replacement)
}
private fun typedArgumentList(function: IrFunction, expression: IrMemberAccessExpression<*>) =
listOfNotNull(
function.dispatchReceiverParameter?.let { it to expression.dispatchReceiver },
function.extensionReceiverParameter?.let { it to expression.extensionReceiver }
) + function.valueParameters.map { it to expression.getValueArgument(it.index) }
private fun IrMemberAccessExpression<*>.buildReplacement(
originalFunction: IrFunction,
original: IrMemberAccessExpression<*>,
@@ -420,7 +449,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
return super.visitSetValue(expression)
}
override fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
fun buildPrimaryInlineClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
// Add the default primary constructor
valueClass.addConstructor {
updateFrom(irConstructor)
@@ -465,7 +494,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
valueClass.declarations += function
}
override fun buildBoxFunction(valueClass: IrClass) {
fun buildBoxFunction(valueClass: IrClass) {
val function = context.inlineClassReplacements.getBoxFunction(valueClass)
with(context.createIrBuilder(function.symbol)) {
function.body = irExprBody(
@@ -478,10 +507,6 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
valueClass.declarations += function
}
override fun buildUnboxFunctions(valueClass: IrClass) {
buildUnboxFunction(valueClass)
}
private fun buildUnboxFunction(irClass: IrClass) {
val function = context.inlineClassReplacements.getUnboxFunction(irClass)
val field = getInlineClassBackingField(irClass)
@@ -494,7 +519,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
irClass.declarations += function
}
override fun buildSpecializedEqualsMethod(valueClass: IrClass) {
fun buildSpecializedEqualsMethod(valueClass: IrClass) {
val function = context.inlineClassReplacements.getSpecializedEqualsMethod(valueClass, context.irBuiltIns)
val left = function.valueParameters[0]
val right = function.valueParameters[1]
@@ -5,45 +5,398 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
import org.jetbrains.kotlin.backend.jvm.isMultiFieldValueClassFieldGetter
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.ValueParameterTemplate
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.ir.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.ir.isMultiFieldValueClassType
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addGetter
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
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.classOrNull
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.popLast
val jvmMultiFieldValueClassPhase = makeIrFilePhase(
::JvmMultiFieldValueClassLowering,
name = "Multi-field Value Classes",
description = "Lower multi-field value classes",
// Collection stubs may require mangling by multi-field value class rules.
// SAM wrappers may require mangling for fun interfaces with multi-field value class parameters
prerequisite = setOf(collectionStubMethodLowering, singleAbstractMethodPhase),
)
private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) {
override val replacements: MemoizedValueClassAbstractReplacements
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>>()
fun remapSymbol(original: IrValueSymbol, replacement: IrValueDeclaration) {
symbol2getter[original] = { irGet(replacement) }
symbol2setters[original] = listOf(if (replacement.isAssignable) { _, value -> irSet(replacement, value) } else null)
}
fun remapSymbol(original: IrValueSymbol, unboxed: List<VirtualProperty<Unit>>): Unit =
remapSymbol(original, replacements.getDeclarations(original.owner.type.erasedUpperBound)!!.ImplementationAgnostic(unboxed))
fun remapSymbol(original: IrValueSymbol, unboxed: ImplementationAgnostic<Unit>) {
symbol2getter[original] = {
unboxed.boxedExpression(this, Unit).also { irExpression -> knownExpressions[irExpression] = unboxed }
}
symbol2setters[original] = unboxed.virtualFields.map { it.assigner }
}
fun IrBuilderWithScope.getter(original: IrValueSymbol): IrExpression? =
symbol2getter[original]?.invoke(this, Unit)
fun setter(original: IrValueSymbol): List<ExpressionSupplier<Unit>?>? = symbol2setters[original]
fun implementationAgnostic(expression: IrExpression): ImplementationAgnostic<Unit>? = knownExpressions[expression]
fun IrBuilderWithScope.subfield(expression: IrExpression, name: Name): IrExpression? =
implementationAgnostic(expression)?.get(name)?.let { (expressionGenerator, representation) ->
val res = expressionGenerator(this, Unit)
representation?.let { knownExpressions[res] = it }
res
}
/**
* Register value declaration instead of singular expressions when possible
*/
fun registerExpression(getter: IrExpression, representation: ImplementationAgnostic<Unit>) {
knownExpressions[getter] = representation
}
}
private val valueDeclarationsRemapper = ValueDeclarationsRemapper()
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()
override val replacements
get() = context.multiFieldValueClassReplacements
override fun IrClass.isSpecificLoweringLogicApplicable(): Boolean = isMultiFieldValueClass
override fun IrFunction.isSpecificFieldGetter(): Boolean = isMultiFieldValueClassFieldGetter
override fun IrFunction.isFieldGetterToRemove(): Boolean = isMultiFieldValueClassOriginalFieldGetter
override fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
TODO()
override fun visitClassNew(declaration: IrClass): IrStatement {
if (declaration.isSpecificLoweringLogicApplicable()) {
handleSpecificNewClass(declaration)
} else {
handleNonSpecificNewClass(declaration)
}
declaration.transformDeclarationsFlat { memberDeclaration ->
if (memberDeclaration is IrFunction) {
withinScope(memberDeclaration) {
transformFunctionFlat(memberDeclaration)
}
} else {
memberDeclaration.accept(this, null)
null
}
}
return declaration
}
override fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
TODO("Not yet implemented")
private fun handleNonSpecificNewClass(declaration: IrClass) {
declaration.primaryConstructor?.let {
replacements.getReplacementRegularClassConstructor(it)?.let { replacement -> addBindingsFor(it, replacement) }
}
val fieldsToReplace = declaration.fields.filter { !it.type.isNullable() && it.type.isMultiFieldValueClassType() }.toList()
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 ->
context.irFactory.buildField {
name = Name.guessByFirstCharacter("${oldField.name.asString()}$${sourceField.name.asString()}")
type = sourceField.type
origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER
visibility = sourceField.visibility
}.apply {
parent = declaration
initializer
}
}
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) },
)
}
val implementationAgnostic = declarations.ImplementationAgnostic(virtualFields)
val property = oldField.correspondingPropertySymbol?.owner
val mainGetter = property?.getter
val mainSetter = property?.setter
property?.backingField = null
if (mainGetter != null) {
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) ->
if (node == declarations.loweringRepresentation) mainGetter else {
context.irFactory.buildProperty {
val nameAsString = "${oldField.name.asString()}$${declarations.nodeFullNames[node]!!.asString()}"
name = Name.guessByFirstCharacter(nameAsString)
origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER
visibility = oldField.visibility
}.apply {
parent = declaration
addGetter {
returnType = node.type
origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER
}.apply {
createDispatchReceiverParameter()
body = with(context.createIrBuilder(this.symbol)) {
irExprBody(exprGen(this, dispatchReceiverParameter!!))
}
}
}.getter!!.also {
declaration.declarations.add(it)
}
}
}
regularClassMFVCPropertyNodes.putAll(nodesToGetters.map { (node, getter) -> getter to node })
for ((node, getter) in nodesToGetters) {
if (node is InternalNode<Unit, Unit>) {
regularClassMFVCPropertyNextGetters[getter] = node.fields.associate { it.name to nodesToGetters[it.node]!! }
}
}
}
}
for (accessor in listOfNotNull(mainGetter, mainSetter))
accessor.body?.transform(object : IrElementTransformerVoid() {
override fun visitGetField(expression: IrGetField): IrExpression {
if (expression.symbol.owner == oldField) {
require(expression.receiver.let { it is IrGetValue && it.symbol.owner == accessor.dispatchReceiverParameter!! }) {
"Unexpected receiver for IrGetField: ${expression.receiver}"
}
val gettersAndSetters =
newFields.toGettersAndSetters(accessor.dispatchReceiverParameter!!, transformReceiver = true)
val representation = newFields.zip(gettersAndSetters) { newField, (getter, setter) ->
VirtualProperty(newField.type, getter, setter)
}
val fieldRepresentation = declarations.ImplementationAgnostic(representation)
val boxed = fieldRepresentation.boxedExpression(
context.createIrBuilder((currentScope!!.irElement as IrSymbolOwner).symbol), Unit
)
valueDeclarationsRemapper.registerExpression(boxed, fieldRepresentation)
return boxed
}
return super.visitGetField(expression)
}
}, null)
oldField.initializer?.let { initializer ->
context.irFactory.createAnonymousInitializer(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET, origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER,
symbol = IrAnonymousInitializerSymbolImpl()
).apply {
parent = declaration
body = context.createIrBuilder(this.symbol).irBlockBody {
+irBlock {
flattenExpressionTo(
initializer.expression.transform(this@JvmMultiFieldValueClassLowering, null),
newFields.toGettersAndSetters(declaration.thisReceiver!!)
)
}
}
oldFieldToInitializers[oldField] = this
}
}
newFields
}
for (i in declaration.declarations.indices) {
oldFieldToInitializers[declaration.declarations[i]]?.let { initializer ->
declaration.declarations[i] = initializer
}
}
declaration.declarations.addAll(newFields.flatten())
declaration.declarations.removeAll(fieldsToReplace)
}
override fun buildBoxFunction(valueClass: IrClass) {
TODO("Not yet implemented")
override fun handleSpecificNewClass(declaration: IrClass) {
replacements.setOldFields(declaration, declaration.fields.toList())
val newDeclarations = replacements.getDeclarations(declaration)!!
if (newDeclarations.valueClass != declaration) error("Unexpected IrClass ${newDeclarations.valueClass} instead of $declaration")
newDeclarations.replaceFields()
newDeclarations.replaceProperties()
newDeclarations.buildPrimaryMultiFieldValueClassConstructor()
newDeclarations.buildBoxFunction()
newDeclarations.buildUnboxFunctions()
newDeclarations.buildSpecializedEqualsMethod()
}
override fun buildUnboxFunctions(valueClass: IrClass) {
TODO("Not yet implemented")
override fun transformSecondaryConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach { it.transformChildrenVoid() }
replacement.body = context.createIrBuilder(replacement.symbol).irBlockBody {
val thisVar = irTemporary(irType = replacement.returnType, nameHint = "\$this")
constructor.body?.statements?.forEach { statement ->
+statement.transformStatement(object : IrElementTransformerVoid() {
override fun visitClass(declaration: IrClass): IrStatement = declaration
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
val oldPrimaryConstructor = replacements.getDeclarations(constructor.constructedClass)!!.oldPrimaryConstructor
thisVar.initializer = irCall(oldPrimaryConstructor).apply {
copyTypeAndValueArgumentsFrom(expression)
}
return irBlock {}
}
override fun visitGetValue(expression: IrGetValue): IrExpression = when (expression.symbol.owner) {
constructor.constructedClass.thisReceiver!! -> irGet(thisVar)
else -> super.visitGetValue(expression)
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid()
if (expression.returnTargetSymbol != constructor.symbol)
return expression
return irReturn(irBlock(expression.startOffset, expression.endOffset) {
+expression.value
+irGet(thisVar)
})
}
})
}
+irReturn(irGet(thisVar))
}
.also { addBindingsFor(constructor, replacement) }
.transform(this@JvmMultiFieldValueClassLowering, null)
.patchDeclarationParents(replacement)
return listOf(replacement)
}
override fun buildSpecializedEqualsMethod(valueClass: IrClass) {
TODO("Not yet implemented")
private fun MultiFieldValueClassSpecificDeclarations.replaceFields() {
valueClass.declarations.removeIf { it is IrField }
valueClass.declarations += fields
for (field in fields) {
field.parent = valueClass
}
}
override fun addJvmInlineAnnotation(valueClass: IrClass) = Unit
private fun MultiFieldValueClassSpecificDeclarations.replaceProperties() {
valueClass.declarations.removeIf { it is IrFunction && it.isFieldGetterToRemove() }
properties.values.forEach {
it.parent = valueClass
}
valueClass.declarations += properties.values.map { it.getter!!.apply { parent = valueClass } }
}
@Suppress("UNUSED_PARAMETER")
override fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
TODO()
override fun createBridgeFunction(function: IrSimpleFunction, replacement: IrSimpleFunction): IrSimpleFunction? {
return null // todo
// todo change return type to non-nullable for base class
}
override fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
val parametersStructure = replacements.bindingParameterTemplateStructure[original]!!
require(parametersStructure.size == original.explicitParameters.size) {
"Wrong value parameters structure: $parametersStructure"
}
require(parametersStructure.sumOf { it.size } == replacement.explicitParameters.size) {
"Wrong value parameters structure: $parametersStructure"
}
val old2newList = original.explicitParameters.zip(
parametersStructure.scan(0) { partial: Int, templates: List<ValueParameterTemplate> -> partial + templates.size }
.zipWithNext { start: Int, finish: Int -> replacement.explicitParameters.slice(start until finish) }
)
for ((param, newParamList) in old2newList) {
val single = newParamList.singleOrNull()
if (single != null) {
valueDeclarationsRemapper.remapSymbol(param.symbol, single)
} else {
valueDeclarationsRemapper.remapSymbol(param.symbol, newParamList.map { VirtualProperty(it) })
}
}
}
fun MultiFieldValueClassSpecificDeclarations.buildPrimaryMultiFieldValueClassConstructor() {
valueClass.declarations.removeIf { it is IrConstructor && it.isPrimary }
val primaryConstructorReplacements = listOf(primaryConstructor, primaryConstructorImpl)
for (exConstructor in primaryConstructorReplacements) {
exConstructor.parent = valueClass
}
valueClass.declarations += primaryConstructorReplacements
val initializersStatements = valueClass.declarations.filterIsInstance<IrAnonymousInitializer>().flatMap { it.body.statements }
valueDeclarationsRemapper.remapSymbol(
oldPrimaryConstructor.constructedClass.thisReceiver!!.symbol,
primaryConstructorImpl.valueParameters.map { VirtualProperty(it) }
)
primaryConstructorImpl.body = context.createIrBuilder(primaryConstructorImpl.symbol).irBlockBody {
for (stmt in initializersStatements) {
+stmt.transformStatement(this@JvmMultiFieldValueClassLowering).patchDeclarationParents(primaryConstructorImpl)
}
}
valueClass.declarations.removeIf { it is IrAnonymousInitializer }
}
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]))
}
})
}
valueClass.declarations += boxMethod
boxMethod.parent = valueClass
}
fun MultiFieldValueClassSpecificDeclarations.buildUnboxFunctions() {
valueClass.declarations += unboxMethods
}
@Suppress("unused")
fun MultiFieldValueClassSpecificDeclarations.buildSpecializedEqualsMethod() {
// todo defaults
specializedEqualsMethod.parent = valueClass
specializedEqualsMethod.body = with(context.createIrBuilder(specializedEqualsMethod.symbol)) {
// TODO: Revisit this once we allow user defined equals methods in inline/multi-field value classes.
leaves.indices.map {
val left = irGet(specializedEqualsMethod.valueParameters[it])
val right = irGet(specializedEqualsMethod.valueParameters[it + leaves.size])
irEquals(left, right)
}.reduce { acc, current ->
irCall(context.irBuiltIns.andandSymbol).apply {
putValueArgument(0, acc)
putValueArgument(1, current)
}
}.let { irExprBody(it) }
}
valueClass.declarations += specializedEqualsMethod
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
@@ -52,27 +405,337 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
}
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
// todo implement
return super.visitFunctionAccess(expression)
val function = expression.symbol.owner
val replacement = replacements.getReplacementFunction(function)
val currentScope = currentScope!!.irElement as IrDeclaration
return when {
function is IrConstructor && function.isPrimary && function.constructedClass.isMultiFieldValueClass &&
currentScope.origin != JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER -> {
context.createIrBuilder(currentScope.symbol).irBlock {
val thisReplacement = irTemporary(expression)
+irGet(thisReplacement)
}.transform(this, null) // transform with visitVariable
}
replacement != null -> context.createIrBuilder(currentScope.symbol).irBlock {
buildReplacement(function, expression, replacement)
}
else ->
when (val newConstructor = (function as? IrConstructor)?.let { replacements.getReplacementRegularClassConstructor(it) }) {
null -> super.visitFunctionAccess(expression)
else -> context.createIrBuilder(currentScope.symbol).irBlock {
buildReplacement(function, expression, newConstructor)
}
}
}
}
override fun visitCall(expression: IrCall): IrExpression {
// todo implement
val callee = expression.symbol.owner
val receiver = expression.dispatchReceiver?.transform(this, null)
val name = callee.correspondingPropertySymbol?.owner?.name
if (callee.isMultiFieldValueClassOriginalFieldGetter && receiver != null) {
with(valueDeclarationsRemapper) {
with(context.createIrBuilder(expression.symbol)) {
subfield(receiver, name!!)?.let { return it }
}
}
}
if (callee.isGetter && name != null && receiver is IrCall) {
val nextFunction = regularClassMFVCPropertyNextGetters[receiver.symbol.owner]?.get(name)
if (nextFunction != null) {
return with(context.createIrBuilder(expression.symbol)) {
irCall(nextFunction).apply {
this.dispatchReceiver = receiver.dispatchReceiver
}
}
}
}
@Suppress("ControlFlowWithEmptyBody")
if (expression.isSpecializedInlineClassEqEq) {
// todo
}
return super.visitCall(expression)
}
override fun visitGetField(expression: IrGetField): IrExpression {
// todo implement
return super.visitGetField(expression)
private fun makeLeavesGetters(currentGetter: IrSimpleFunction): List<IrSimpleFunction>? =
when (val node = regularClassMFVCPropertyNodes[currentGetter]) {
null -> null
is MultiFieldValueClassTree.Leaf<Unit> -> listOf(currentGetter)
is InternalNode -> node.fields.flatMap { makeLeavesGetters(regularClassMFVCPropertyNextGetters[currentGetter]!![it.name]!!)!! }
}
private fun IrBlockBuilder.buildReplacement(
originalFunction: IrFunction,
original: IrMemberAccessExpression<*>,
replacement: IrFunction
) {
val parameter2expression = typedArgumentList(originalFunction, original)
val structure = replacements.bindingParameterTemplateStructure[originalFunction]!!
require(parameter2expression.size == structure.size)
require(structure.sumOf { it.size } == replacement.explicitParametersCount)
val newArguments: List<IrExpression?> = makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure)
+irCall(replacement.symbol).apply {
copyTypeArgumentsFrom(original)
for ((parameter, argument) in replacement.explicitParameters zip newArguments) {
if (argument == null) continue
putArgument(replacement, parameter, argument.transform(this@JvmMultiFieldValueClassLowering, null))
}
}
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
// todo implement
return super.visitGetValue(expression)
private fun IrBlockBuilder.makeNewArguments(
oldArguments: List<IrExpression?>,
structure: List<List<ValueParameterTemplate>>
): List<IrExpression?> {
val variables = structure.flatMap { argTemplate -> argTemplate.map { irTemporary(irType = it.type) } }
val subVariables = structure.scan(0) { acc: Int, templates: List<ValueParameterTemplate> -> acc + templates.size }
.zipWithNext().map { (start, finish) -> variables.slice(start until finish) }
return (oldArguments zip subVariables).flatMap { (oldArgument, curSubVariables) ->
val oldArgumentTransformed = oldArgument?.transform(this@JvmMultiFieldValueClassLowering, null)
when {
oldArgumentTransformed == null -> List(curSubVariables.size) { null }
curSubVariables.size == 1 -> listOf(oldArgumentTransformed)
else -> flattenExpressionTo(oldArgumentTransformed, curSubVariables.toGettersAndSetters())
.let { curSubVariables.map { irGet(it) } }
}
}
}
// Note that reference equality (x === y) is not allowed on values of inline class type,
// so it is enough to check for eqeq.
private val IrCall.isSpecializedInlineClassEqEq: Boolean
get() = symbol == context.irBuiltIns.eqeqSymbol &&
getValueArgument(0)?.type?.classOrNull?.owner?.takeIf { it.isMultiFieldValueClass } != null
override fun visitGetField(expression: IrGetField): IrExpression {
val field = expression.symbol.owner
val parent = field.parent
return when {
field.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD &&
parent is IrClass &&
parent.multiFieldValueClassRepresentation?.containsPropertyWithName(field.name) == true -> {
val receiver = expression.receiver!!.transform(this, null)
with(valueDeclarationsRemapper) {
with(context.createIrBuilder(expression.symbol)) {
subfield(receiver, field.name) ?: run {
expression.receiver = receiver
expression
}
}
}
}
field.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD && !field.type.isNullable() && field.type.isMultiFieldValueClassType() -> {
val getter = regularClassMFVCPropertyMainGetters[expression.receiver!!.type.erasedUpperBound]?.get(field.name)
?: return super.visitGetField(expression)
with(context.createIrBuilder(expression.symbol)) {
irCall(getter).apply {
dispatchReceiver = expression.receiver!!.transform(this@JvmMultiFieldValueClassLowering, null)
}
}
}
else -> super.visitGetField(expression)
}
}
override fun visitSetField(expression: IrSetField): IrExpression {
val field = expression.symbol.owner
val replacementFields = regularClassMFVCPropertyFieldsMapping[field] ?: return super.visitSetField(expression)
return context.createIrBuilder(expression.symbol).irBlock {
val thisVar = irTemporary(expression.receiver!!.transform(this@JvmMultiFieldValueClassLowering, null))
val variables = replacementFields.map { irTemporary(irType = it.type, nameHint = it.name.asString()) }
// We flatten to temp variables because code can throw an exception otherwise and partially update variables
flattenExpressionTo(expression.value.transform(this@JvmMultiFieldValueClassLowering, null), variables.toGettersAndSetters())
for ((replacementField, variable) in replacementFields zip variables) {
+irSetField(irGet(thisVar), replacementField, irGet(variable))
}
}
}
override fun visitGetValue(expression: IrGetValue): IrExpression = with(context.createIrBuilder(expression.symbol)) {
with(valueDeclarationsRemapper) {
getter(expression.symbol) ?: super.visitGetValue(expression)
}
}
override fun visitSetValue(expression: IrSetValue): IrExpression {
// todo implement
return super.visitSetValue(expression)
val setters = valueDeclarationsRemapper.setter(expression.symbol) ?: return super.visitSetValue(expression)
return context.createIrBuilder(expression.symbol).irBlock {
val declarations = replacements.getDeclarations(expression.symbol.owner.type.erasedUpperBound)!!
val variables = declarations.leaves.map { irTemporary(irType = it.type) }
// We flatten to temp variables because code can throw an exception otherwise and partially update variables
flattenExpressionTo(expression.value.transform(this@JvmMultiFieldValueClassLowering, null), variables.toGettersAndSetters())
for ((setter, variable) in setters zip variables) {
setter?.invoke(this, Unit, irGet(variable))?.let { +it }
}
}
}
}
override fun visitVariable(declaration: IrVariable): IrStatement {
val initializer = declaration.initializer
if (declaration.type.isMultiFieldValueClassType() && !declaration.type.isNullable()) {
val irClass = declaration.type.erasedUpperBound
val declarations = replacements.getDeclarations(irClass)!!
return context.createIrBuilder((currentScope!!.irElement as IrSymbolOwner).symbol).irComposite {
val variables = declarations.leaves.map { leaf ->
irTemporary(
nameHint = "${declaration.name.asString()}$${declarations.nodeFullNames[leaf]!!.asString()}",
irType = leaf.type,
isMutable = declaration.isVar
)
}
initializer?.let {
flattenExpressionTo(it, variables.toGettersAndSetters())
}
valueDeclarationsRemapper.remapSymbol(declaration.symbol, variables.map { VirtualProperty(it) })
}
}
return super.visitVariable(declaration)
}
private fun List<IrVariable>.toGettersAndSetters() = map { variable ->
Pair<ExpressionGenerator<Unit>, ExpressionSupplier<Unit>>(
{ irGet(variable) },
{ _, value: IrExpression -> irSet(variable, value) }
)
}
private fun List<IrField>.toGettersAndSetters(receiver: IrValueParameter, transformReceiver: Boolean = false) = map { field ->
Pair<ExpressionGenerator<Unit>, ExpressionSupplier<Unit>>(
{
val initialGetReceiver = irGet(receiver)
val resultReceiver =
if (transformReceiver) initialGetReceiver.transform(this@JvmMultiFieldValueClassLowering, null)
else initialGetReceiver
irGetField(resultReceiver, field)
},
{ _, value: IrExpression ->
val initialGetReceiver = irGet(receiver)
val resultReceiver =
if (transformReceiver) initialGetReceiver.transform(this@JvmMultiFieldValueClassLowering, null)
else initialGetReceiver
irSetField(resultReceiver, field, value)
},
)
}
fun IrBlockBuilder.flattenExpressionTo(
expression: IrExpression, variables: List<Pair<ExpressionGenerator<Unit>, ExpressionSupplier<Unit>>>
) {
valueDeclarationsRemapper.implementationAgnostic(expression)?.virtualFields?.map { it.makeGetter(this, Unit) }?.let {
require(variables.size == it.size)
for ((variable, subExpression) in variables zip it) {
+variable.second(this, Unit, subExpression)
}
return
}
if (expression.type.isNullable() || !expression.type.isMultiFieldValueClassType()) {
require(variables.size == 1)
+variables.single().second(this, Unit, expression.transform(this@JvmMultiFieldValueClassLowering, null))
return
}
val declarations = replacements.getDeclarations(expression.type.erasedUpperBound)!!
require(variables.size == declarations.leaves.size)
if (expression is IrConstructorCall) {
val constructor = expression.symbol.owner
if (constructor.isPrimary && constructor.constructedClass.isMultiFieldValueClass) {
val oldArguments = List(expression.valueArgumentsCount) { expression.getValueArgument(it) }
val root = declarations.loweringRepresentation
require(root.fields.size == oldArguments.size) {
"$constructor must have ${root.fields.size} arguments but got ${oldArguments.size}"
}
var curOffset = 0
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
}
val subVariables = variables.slice(curOffset until (curOffset + size)).also { curOffset += size }
argument?.let { flattenExpressionTo(it, subVariables) } ?: List(size) { null }
}
+irCall(declarations.primaryConstructorImpl).apply {
variables.forEachIndexed { index, variable -> putValueArgument(index, variable.first(this@flattenExpressionTo, Unit)) }
}
return
}
}
val transformedExpression = expression.transform(this@JvmMultiFieldValueClassLowering, null)
if (transformedExpression is IrCall) {
val callee = transformedExpression.symbol.owner
if (callee == declarations.boxMethod) {
require(transformedExpression.valueArgumentsCount == declarations.fields.size) {
"Bad arguments number for box-method: ${transformedExpression.valueArgumentsCount}"
}
for ((variable, argument) in variables zip List(transformedExpression.valueArgumentsCount) {
transformedExpression.getValueArgument(it)
}) {
if (argument != null) {
+variable.second(this, Unit, argument)
}
}
return
}
}
(transformedExpression as? IrCall)?.let { makeLeavesGetters(it.symbol.owner) }?.let { geters ->
val receiver = irTemporary(transformedExpression.dispatchReceiver)
require(geters.size == variables.size) { "Number of getters must be equal to number of variables" }
for ((variable, getter) in variables zip geters) {
+variable.second(this, Unit, irCall(getter).apply { dispatchReceiver = irGet(receiver) })
}
return
}
if (transformedExpression is IrContainerExpression && transformedExpression.statements.isNotEmpty()) {
val last = transformedExpression.statements.popLast()
if (last is IrExpression) {
transformedExpression.statements.forEach { +it }
flattenExpressionTo(last, variables)
return
}
}
val boxed = irTemporary(transformedExpression)
for ((variable, unboxMethod) in variables zip declarations.unboxMethods) {
+variable.second(this, Unit, irCall(unboxMethod).apply { dispatchReceiver = irGet(boxed) })
}
}
override fun visitStatementContainer(container: IrStatementContainer) {
super.visitStatementContainer(container)
deleteUselessBoxes(container)
}
private fun deleteUselessBoxes(container: IrStatementContainer) {
val statements = container.statements
// Removing box-impl's but not getters because they are auto-generated
fun IrExpression.isBoxCallStatement() = this is IrCall && valueDeclarationsRemapper.implementationAgnostic(this) != null &&
List(valueArgumentsCount) { getValueArgument(it) }.all { it == null || it is IrGetField || it is IrGetValue }
fun IrStatement.coercionToUnitArgument() =
(this as? IrTypeOperatorCall)?.takeIf { it.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT }?.argument
fun IrStatement.isBoxCallStatement(): Boolean {
val coercionToUnitArgument = coercionToUnitArgument()
if (coercionToUnitArgument?.isBoxCallStatement() == true || this is IrExpression && this.isBoxCallStatement()) {
return true
}
if (coercionToUnitArgument is IrStatementContainer && (coercionToUnitArgument.statements.lastOrNull() as? IrExpression)?.isBoxCallStatement() == true) {
coercionToUnitArgument.statements.popLast()
}
return false
}
if (statements.isEmpty()) return
val last = statements.popLast()
statements.removeIf { it.isBoxCallStatement() }
for (statement in statements) {
if (statement is IrStatementContainer && statement.statements.lastOrNull()?.isBoxCallStatement() == true) {
statement.statements.removeLast()
}
}
statements.add(last) // we don't check delete it anyway
}
private fun IrSimpleFunction.isDefaultGetter(field: IrField): Boolean =
((body?.statements?.singleOrNull() as? IrReturn)?.value as? IrGetField)?.symbol?.owner == field
}
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.jvm.InlineClassAbi
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
@@ -15,63 +17,28 @@ import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.transformStatement
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendContext) : FileLoweringPass,
IrElementTransformerVoidWithContext() {
abstract val replacements: MemoizedValueClassAbstractReplacements
protected val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>()
private fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
for ((param, newParam) in original.explicitParameters.zip(replacement.explicitParameters)) {
valueMap[param.symbol] = newParam
}
}
final override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid()
}
abstract fun IrClass.isSpecificLoweringLogicApplicable(): Boolean
abstract fun IrFunction.isSpecificFieldGetter(): Boolean
abstract fun IrFunction.isFieldGetterToRemove(): Boolean
final override fun visitClassNew(declaration: IrClass): IrStatement {
// The arguments to the primary constructor are in scope in the initializers of IrFields.
declaration.primaryConstructor?.let {
replacements.getReplacementFunction(it)?.let { replacement -> addBindingsFor(it, replacement) }
}
abstract override fun visitClassNew(declaration: IrClass): IrStatement
declaration.transformDeclarationsFlat { memberDeclaration ->
if (memberDeclaration is IrFunction) {
withinScope(memberDeclaration) {
transformFunctionFlat(memberDeclaration)
}
} else {
memberDeclaration.accept(this, null)
null
}
}
if (declaration.isSpecificLoweringLogicApplicable()) {
val irConstructor = declaration.primaryConstructor!!
// The field getter is used by reflection and cannot be removed here unless it is internal.
declaration.declarations.removeIf {
it == irConstructor || (it is IrFunction && it.isSpecificFieldGetter() && !it.visibility.isPublicAPI)
}
buildPrimaryValueClassConstructor(declaration, irConstructor)
buildBoxFunction(declaration)
buildUnboxFunctions(declaration)
buildSpecializedEqualsMethod(declaration)
addJvmInlineAnnotation(declaration)
}
return declaration
}
abstract fun handleSpecificNewClass(declaration: IrClass)
protected fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
if (function is IrConstructor && function.isPrimary && function.constructedClass.isSpecificLoweringLogicApplicable()) {
@@ -79,7 +46,15 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
}
val replacement = replacements.getReplacementFunction(function)
if (replacement == null) {
if (function is IrConstructor) {
val constructorReplacement = replacements.getReplacementRegularClassConstructor(function)
if (constructorReplacement != null) {
addBindingsFor(function, constructorReplacement)
return transformFlattenedConstructor(function, constructorReplacement)
}
}
function.transformChildrenVoid()
// Non-mangled functions can override mangled functions under some conditions, e.g., a function
// `fun f(): Nothing` can override a function `fun f(): UInt`. The former is not mangled, while
@@ -106,30 +81,48 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
addBindingsFor(function, replacement)
return when (function) {
is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement)
is IrConstructor -> transformConstructorFlat(function, replacement)
is IrConstructor -> transformSecondaryConstructorFlat(function, replacement)
else -> throw IllegalStateException()
}
}
private fun transformFlattenedConstructor(function: IrConstructor, replacement: IrConstructor): List<IrDeclaration>? {
replacement.valueParameters.forEach {
it.transformChildrenVoid()
it.defaultValue?.patchDeclarationParents(replacement)
}
allScopes.push(createScope(function))
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
allScopes.pop()
return listOf(replacement)
}
private fun IrFunction.hashSuffix(): String? = InlineClassAbi.hashSuffix(
this,
context.state.functionsWithInlineClassReturnTypesMangled,
context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures
)
protected abstract fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration>
protected abstract fun transformSecondaryConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration>
protected abstract fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration>
private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach {
it.transformChildrenVoid()
it.defaultValue?.patchDeclarationParents(replacement)
}
allScopes.push(createScope(function))
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
allScopes.pop()
replacement.copyAttributes(function)
protected abstract fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor)
// Don't create a wrapper for functions which are only used in an unboxed context
if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null)
return listOf(replacement)
protected abstract fun buildBoxFunction(valueClass: IrClass)
val bridgeFunction = createBridgeFunction(function, replacement)
protected abstract fun buildUnboxFunctions(valueClass: IrClass)
protected abstract fun buildSpecializedEqualsMethod(valueClass: IrClass) // todo hashCode
protected abstract fun addJvmInlineAnnotation(valueClass: IrClass)
return listOfNotNull(replacement, bridgeFunction)
}
final override fun visitReturn(expression: IrReturn): IrExpression {
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
@@ -146,7 +139,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
return super.visitReturn(expression)
}
private fun visitStatementContainer(container: IrStatementContainer) {
protected open fun visitStatementContainer(container: IrStatementContainer) {
container.statements.transformFlat { statement ->
if (statement is IrFunction)
transformFunctionFlat(statement)
@@ -172,4 +165,11 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
else
super.visitAnonymousInitializerNew(declaration)
protected abstract fun addBindingsFor(original: IrFunction, replacement: IrFunction)
protected abstract fun createBridgeFunction(function: IrSimpleFunction, replacement: IrSimpleFunction): IrSimpleFunction?
protected fun typedArgumentList(function: IrFunction, expression: IrMemberAccessExpression<*>) = listOfNotNull(
function.dispatchReceiverParameter?.let { it to expression.dispatchReceiver },
function.extensionReceiverParameter?.let { it to expression.extensionReceiver }
) + function.valueParameters.map { it to expression.getValueArgument(it.index) }
}
@@ -60,14 +60,14 @@ object InlineClassAbi {
fun mangledNameFor(irFunction: IrFunction, mangleReturnTypes: Boolean, useOldMangleRules: Boolean): Name {
if (irFunction is IrConstructor) {
// Note that we might drop this convention and use standard mangling for constructors too, see KT-37186.
assert(irFunction.constructedClass.isSingleFieldValueClass) {
assert(irFunction.constructedClass.isValue) {
"Should not mangle names of non-inline class constructors: ${irFunction.render()}"
}
return Name.identifier("constructor-impl")
}
val suffix = hashSuffix(irFunction, mangleReturnTypes, useOldMangleRules)
if (suffix == null && ((irFunction.parent as? IrClass)?.isSingleFieldValueClass != true || irFunction.origin == IrDeclarationOrigin.IR_BUILTINS_STUB)) {
if (suffix == null && ((irFunction.parent as? IrClass)?.isValue != true || irFunction.origin == IrDeclarationOrigin.IR_BUILTINS_STUB)) {
return irFunction.name
}
@@ -127,16 +127,16 @@ object InlineClassAbi {
val IrType.requiresMangling: Boolean
get() {
val irClass = erasedUpperBound
return irClass.isSingleFieldValueClass && irClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME
return irClass.isValue && irClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME
}
val IrFunction.fullValueParameterList: List<IrValueParameter>
get() = listOfNotNull(extensionReceiverParameter) + valueParameters
val IrFunction.hasMangledParameters: Boolean
get() = dispatchReceiverParameter != null && parentAsClass.isSingleFieldValueClass ||
get() = dispatchReceiverParameter != null && parentAsClass.isValue ||
fullValueParameterList.any { it.type.requiresMangling } ||
(this is IrConstructor && constructedClass.isSingleFieldValueClass)
(this is IrConstructor && constructedClass.isValue)
val IrFunction.hasMangledReturnType: Boolean
get() = returnType.isInlineClassType() && parentClassOrNull?.isFileClass != true
@@ -148,7 +148,7 @@ val IrFunction.isInlineClassFieldGetter: Boolean
get() = (parent as? IrClass)?.isSingleFieldValueClass == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.name == parentAsClass.inlineClassFieldName } == true
val IrFunction.isMultiFieldValueClassFieldGetter: Boolean
val IrFunction.isMultiFieldValueClassOriginalFieldGetter: Boolean
get() = (parent as? IrClass)?.isMultiFieldValueClass == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
correspondingPropertySymbol?.let {
val multiFieldValueClassRepresentation = parentAsClass.multiFieldValueClassRepresentation
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName
@@ -143,7 +144,8 @@ class JvmBackendContext(
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
val multiFieldValueClassReplacements = MemoizedMultiFieldValueClassReplacements(irFactory, this)
val multiFieldValueClassReplacements =
MemoizedMultiFieldValueClassReplacements(irFactory, this, IrTypeSystemContextImpl(irBuiltIns))
val continuationClassesVarsCountByType: MutableMap<IrAttributeContainer, Map<Type, Int>> = hashMapOf()
@@ -197,6 +199,20 @@ class JvmBackendContext(
multifileFacade.setValue(newPartClasses.toMutableList())
}
for ((staticReplacement, original) in multiFieldValueClassReplacements.originalFunctionForStaticReplacement) {
if (staticReplacement !is IrSimpleFunction) continue
val newOriginal = functionSymbolMap[original.symbol]?.owner ?: continue
val newStaticReplacement = multiFieldValueClassReplacements.getReplacementFunction(newOriginal) ?: continue
functionSymbolMap[staticReplacement.symbol] = newStaticReplacement.symbol
}
for ((methodReplacement, original) in multiFieldValueClassReplacements.originalFunctionForMethodReplacement) {
if (methodReplacement !is IrSimpleFunction) continue
val newOriginal = functionSymbolMap[original.symbol]?.owner ?: continue
val newMethodReplacement = multiFieldValueClassReplacements.getReplacementFunction(newOriginal) ?: continue
functionSymbolMap[methodReplacement.symbol] = newMethodReplacement.symbol
}
for ((staticReplacement, original) in inlineClassReplacements.originalFunctionForStaticReplacement) {
if (staticReplacement !is IrSimpleFunction) continue
val newOriginal = functionSymbolMap[original.symbol]?.owner ?: continue
@@ -29,9 +29,14 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object ENUM_MAPPINGS_FOR_WHEN : IrDeclarationOriginImpl("ENUM_MAPPINGS_FOR_WHEN", isSynthetic = true)
object ENUM_MAPPINGS_FOR_ENTRIES : IrDeclarationOriginImpl("ENUM_MAPPINGS_FOR_ENTRIES", isSynthetic = true)
object SYNTHETIC_INLINE_CLASS_MEMBER : IrDeclarationOriginImpl("SYNTHETIC_INLINE_CLASS_MEMBER", isSynthetic = true)
object SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER :
IrDeclarationOriginImpl("SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER", isSynthetic = true)
object INLINE_CLASS_GENERATED_IMPL_METHOD : IrDeclarationOriginImpl("INLINE_CLASS_GENERATED_IMPL_METHOD")
object MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD : IrDeclarationOriginImpl("MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD")
object STATIC_INLINE_CLASS_REPLACEMENT : IrDeclarationOriginImpl("STATIC_INLINE_CLASS_REPLACEMENT")
object STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT : IrDeclarationOriginImpl("STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT")
object STATIC_INLINE_CLASS_CONSTRUCTOR : IrDeclarationOriginImpl("STATIC_INLINE_CLASS_CONSTRUCTOR")
object STATIC_MULTI_FIELD_VALUE_CLASS_CONSTRUCTOR : IrDeclarationOriginImpl("STATIC_MULTI_FIELD_VALUE_CLASS_CONSTRUCTOR")
object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true)
object GENERATED_EXTENDED_MAIN : IrDeclarationOriginImpl("GENERATED_EXTENDED_MAIN", isSynthetic = true)
object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true)
@@ -5,6 +5,10 @@
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParameters
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
import org.jetbrains.kotlin.backend.jvm.ir.*
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -12,18 +16,17 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.types.isInt
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.alwaysNull
import java.util.concurrent.ConcurrentHashMap
/**
@@ -35,9 +38,8 @@ class MemoizedInlineClassReplacements(
context: JvmBackendContext
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
private val storageManager = LockBasedStorageManager("inline-class-replacements")
private val propertyMap = ConcurrentHashMap<IrPropertySymbol, IrProperty>()
override val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
/**
@@ -49,7 +51,9 @@ class MemoizedInlineClassReplacements(
// Don't mangle anonymous or synthetic functions, except for generated SAM wrapper methods
(it.isLocal && it is IrSimpleFunction && it.overriddenSymbols.isEmpty()) ||
(it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR && it.visibility == DescriptorVisibilities.LOCAL) ||
it.isStaticInlineClassReplacement ||
it.isStaticValueClassReplacement ||
it.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
it.origin == IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER ||
it.origin.isSynthetic && it.origin != IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION ->
null
@@ -64,7 +68,7 @@ class MemoizedInlineClassReplacements(
when {
it.isRemoveAtSpecialBuiltinStub() ->
null
it.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
it.isValueClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB ->
createMethodReplacement(it)
else ->
@@ -80,29 +84,6 @@ class MemoizedInlineClassReplacements(
}
}
private fun IrFunction.isRemoveAtSpecialBuiltinStub() =
origin == IrDeclarationOrigin.IR_BUILTINS_STUB &&
name.asString() == "remove" &&
valueParameters.size == 1 &&
valueParameters[0].type.isInt()
private fun IrFunction.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod(): Boolean {
if (this !is IrSimpleFunction) return false
if (!this.isFakeOverride) return false
val parentClass = parentClassOrNull ?: return false
if (!parentClass.isSingleFieldValueClass) return false
val overridden = resolveFakeOverride() ?: return false
if (!overridden.parentAsClass.isJvmInterface) return false
if (overridden.modality == Modality.ABSTRACT) return false
// We have a non-abstract interface member.
// It is a JVM default interface method if one of the following conditions are true:
// - it is a Java method,
// - it is a Kotlin function compiled to JVM default interface method.
return overridden.isFromJava() || overridden.isCompiledToJvmDefault(context.state.jvmDefaultMode)
}
/**
* Get the box function for an inline class. Concretely, this is a synthetic
* static function named "box-impl" which takes an unboxed value and returns
@@ -148,7 +129,7 @@ class MemoizedInlineClassReplacements(
return specializedEqualsCache.computeIfAbsent(irClass) {
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 inline/multi-field value classes.
origin = JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
returnType = irBuiltIns.booleanType
}.apply {
@@ -168,7 +149,7 @@ class MemoizedInlineClassReplacements(
}
}
private fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
override fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, function.origin) {
originalFunctionForMethodReplacement[this] = function
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this, index = -1)
@@ -186,7 +167,7 @@ class MemoizedInlineClassReplacements(
}
}
private fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
override fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT, noFakeOverride = true) {
originalFunctionForStaticReplacement[this] = function
@@ -246,8 +227,7 @@ class MemoizedInlineClassReplacements(
noFakeOverride: Boolean,
useOldManglingScheme: Boolean,
body: IrFunction.() -> Unit,
): IrSimpleFunction = irFactory.buildFun {
updateFrom(function)
): IrSimpleFunction = commonBuildReplacementInner(function, noFakeOverride, body) {
if (function is IrConstructor) {
// The [updateFrom] call will set the modality to FINAL for constructors, while the JVM backend would use OPEN here.
modality = Modality.OPEN
@@ -260,53 +240,11 @@ class MemoizedInlineClassReplacements(
else ->
replacementOrigin
}
if (noFakeOverride) {
isFakeOverride = false
}
name = InlineClassAbi.mangledNameFor(function, mangleReturnTypes, useOldManglingScheme)
returnType = function.returnType
}.apply {
parent = function.parent
annotations = function.annotations
copyTypeParameters(function.allTypeParameters)
if (function.metadata != null) {
metadata = function.metadata
function.metadata = null
}
copyAttributes(function as? IrAttributeContainer)
if (function is IrSimpleFunction) {
val propertySymbol = function.correspondingPropertySymbol
if (propertySymbol != null) {
val property = propertyMap.getOrPut(propertySymbol) {
irFactory.buildProperty {
name = propertySymbol.owner.name
updateFrom(propertySymbol.owner)
}.apply {
parent = propertySymbol.owner.parent
copyAttributes(propertySymbol.owner)
annotations = propertySymbol.owner.annotations
// In case this property is declared in an object in another file which is not yet lowered, its backing field will
// be made static later. We have to handle it here though, because this new property will be saved to the cache
// and reused when lowering the same call in all subsequent files, which would be incorrect if it was unlowered.
backingField = context.cachedDeclarations.getStaticBackingField(propertySymbol.owner)
?: propertySymbol.owner.backingField
}
}
correspondingPropertySymbol = property.symbol
when (function) {
propertySymbol.owner.getter -> property.getter = this
propertySymbol.owner.setter -> property.setter = this
else -> error("Orphaned property getter/setter: ${function.render()}")
}
}
overriddenSymbols = replaceOverriddenSymbols(function)
}
body()
}
override val getReplacementRegularClassConstructor: (IrConstructor) -> IrConstructor? = alwaysNull()
override val replaceOverriddenSymbols: (IrSimpleFunction) -> List<IrSimpleFunctionSymbol> =
storageManager.createMemoizedFunction { irSimpleFunction ->
irSimpleFunction.overriddenSymbols.map {
@@ -5,11 +5,24 @@
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.jvm.ir.extensionReceiverName
import org.jetbrains.kotlin.backend.jvm.ir.isMultiFieldValueClassType
import org.jetbrains.kotlin.backend.jvm.ir.isStaticValueClassReplacement
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.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.concurrent.ConcurrentHashMap
/**
@@ -17,21 +30,239 @@ import java.util.concurrent.ConcurrentHashMap
*/
class MemoizedMultiFieldValueClassReplacements(
irFactory: IrFactory,
context: JvmBackendContext
context: JvmBackendContext,
private val typeSystemContext: IrTypeSystemContext
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
private val storageManager = LockBasedStorageManager("multi-field-value-class-replacements")
override val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalConstructorForConstructorReplacement: MutableMap<IrConstructor, IrConstructor> = ConcurrentHashMap()
val getDeclarations: (IrClass) -> MultiFieldValueClassSpecificDeclarations? =
storageManager.createMemoizedFunctionWithNullableValues {
if (it.isMultiFieldValueClass)
MultiFieldValueClassSpecificDeclarations(it, typeSystemContext, irFactory, context, this)
else
null
}
private val oldFields: MutableMap<IrClass, List<IrField>> =
ConcurrentHashMap<IrClass, List<IrField>>().withDefault { it.fields.toList() }
fun setOldFields(irClass: IrClass, fields: List<IrField>) {
oldFields[irClass] = fields
}
fun getOldFields(irClass: IrClass): List<IrField> = oldFields.getOrPut(irClass) { irClass.fields.toList() }
class ValueParameterTemplate(
val name: String,
val type: IrType,
val origin: IrDeclarationOrigin?,
val defaultValue: IrExpressionBody?,
val original: IrValueParameter,
) {
fun toParameter(irFunction: IrFunction, index: Int): IrValueParameter = original.copyTo(
irFunction = irFunction,
index = index,
name = Name.guessByFirstCharacter(name),
origin = origin ?: original.origin,
defaultValue = null,
type = type,
).also {
// Assuming that constructors and non-override functions are always replaced with the unboxed
// equivalent, deep-copying the value here is unnecessary.
it.defaultValue = defaultValue?.patchDeclarationParents(irFunction)
}
}
private fun IrFunction.makeValueParametersFromTemplate(newFlattenedParameters: List<List<ValueParameterTemplate>>) =
newFlattenedParameters.flatten().mapIndexed { index: Int, template -> template.toParameter(this, index) }
private fun List<ValueParameterTemplate>.grouped(
originWhenFlattenedAndNotSpecified: IrDeclarationOrigin? = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER
): List<List<ValueParameterTemplate>> = map { parameter ->
val declaration = parameter.type.takeIf { !it.isNullable() }?.getClass()?.let { getDeclarations(it) }
?: return@map listOf(parameter)
require(!parameter.original.hasDefaultValue()) { "Default parameters values are not supported for multi-field value classes" }
declaration.leaves.map { leaf ->
ValueParameterTemplate(
name = "${parameter.name}$${declaration.nodeFullNames[leaf]!!}",
type = leaf.type,
origin = parameter.origin ?: originWhenFlattenedAndNotSpecified,
defaultValue = null,
original = parameter.original,
)
}
}
private fun buildReplacement(
function: IrFunction,
replacementOrigin: IrDeclarationOrigin,
noFakeOverride: Boolean = false,
body: IrFunction.() -> Unit,
): IrSimpleFunction = commonBuildReplacementInner(function, noFakeOverride, body) {
origin = when {
function.origin == IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER ->
JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD
function is IrConstructor && function.constructedClass.isMultiFieldValueClass ->
JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_CONSTRUCTOR
else -> replacementOrigin
}
name = InlineClassAbi.mangledNameFor(function, mangleReturnTypes = false, useOldMangleRules = false)
}
private fun makeGroupedValueParametersFrom(
function: IrFunction, includeDispatcherReceiver: Boolean
): List<List<ValueParameterTemplate>> {
val newFlattenedParameters = mutableListOf<List<ValueParameterTemplate>>()
if (function.dispatchReceiverParameter != null && includeDispatcherReceiver) {
val template = ValueParameterTemplate(
name = "\$dispatchReceiver",
type = function.parentAsClass.defaultType,
origin = IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER,
defaultValue = null,
original = function.parentAsClass.thisReceiver!!,
)
newFlattenedParameters.addAll(listOf(template).grouped())
}
val contextReceivers = function.valueParameters.take(function.contextReceiverParametersCount)
.mapIndexed { index: Int, valueParameter: IrValueParameter ->
ValueParameterTemplate(
name = "contextReceiver$index",
origin = IrDeclarationOrigin.MOVED_CONTEXT_RECEIVER,
type = valueParameter.type,
defaultValue = valueParameter.defaultValue,
original = valueParameter,
)
}
.grouped()
newFlattenedParameters.addAll(contextReceivers)
function.extensionReceiverParameter?.let {
val template = ValueParameterTemplate(
name = Name.identifier(function.extensionReceiverName(context.state)).asString(),
origin = IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER,
type = it.type,
defaultValue = it.defaultValue,
original = it,
)
newFlattenedParameters.addAll(listOf(template).grouped())
}
newFlattenedParameters += function.valueParameters.drop(function.contextReceiverParametersCount).map {
ValueParameterTemplate(
name = it.name.asString(),
type = it.type,
defaultValue = it.defaultValue,
original = it,
origin = null,
)
}.grouped()
return newFlattenedParameters
}
val bindingParameterTemplateStructure: MutableMap<IrFunction, List<List<ValueParameterTemplate>>> = ConcurrentHashMap()
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)
bindingParameterTemplateStructure[function] = 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 receiver = dispatchReceiverParameter
val receiverTemplate = if (receiver != null) ValueParameterTemplate(
name = receiver.name.asString(),
type = receiver.type,
origin = receiver.origin,
defaultValue = receiver.defaultValue,
original = receiver,
) else null
bindingParameterTemplateStructure[function] =
if (receiverTemplate != null) listOf(listOf(receiverTemplate)) + newFlattenedParameters else newFlattenedParameters
valueParameters = makeValueParametersFromTemplate(newFlattenedParameters)
}
private fun createConstructorReplacement(@Suppress("UNUSED_PARAMETER") constructor: IrConstructor): IrConstructor {
val newFlattenedParameters = makeGroupedValueParametersFrom(constructor, includeDispatcherReceiver = false)
bindingParameterTemplateStructure[constructor] = newFlattenedParameters
return irFactory.buildConstructor {
updateFrom(constructor)
returnType = constructor.returnType
}.apply {
copyTypeParametersFrom(constructor)
valueParameters = makeValueParametersFromTemplate(newFlattenedParameters)
annotations = constructor.annotations
parent = constructor.parent
originalConstructorForConstructorReplacement[this] = constructor
}
}
private fun createGetterReplacement(function: IrFunction): IrSimpleFunction {
val declarations = getDeclarations(function.parentAsClass)!!
val name = (function as IrSimpleFunction).correspondingPropertySymbol!!.owner.name
val node = declarations.loweringRepresentation[name]!!.node
val newGetter = declarations.properties[node]!!.getter!!
originalFunctionForMethodReplacement[newGetter] = function
val receiver = function.dispatchReceiverParameter!!
val receiverTemplate = ValueParameterTemplate(
name = receiver.name.asString(),
type = receiver.type,
origin = receiver.origin,
defaultValue = receiver.defaultValue,
original = receiver
)
bindingParameterTemplateStructure[function] = listOf(listOf(receiverTemplate))
return newGetter
}
/**
* Get a replacement for a function or a constructor.
*/
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? = storageManager.createMemoizedFunctionWithNullableValues {
TODO()
}
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
storageManager.createMemoizedFunctionWithNullableValues { function ->
when {
(function.isLocal && function is IrSimpleFunction && function.overriddenSymbols.isEmpty()) ||
(function.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR && function.visibility == DescriptorVisibilities.LOCAL) ||
function.isStaticValueClassReplacement ||
function.origin == IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER && function.isAccessor ||
function.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
function.origin.isSynthetic && function.origin != IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION -> null
function.isMultiFieldValueClassOriginalFieldGetter -> createGetterReplacement(function)
function.parent.safeAs<IrClass>()?.isMultiFieldValueClass == true -> when {
function.isRemoveAtSpecialBuiltinStub() ->
null
function.isValueClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
function.origin == IrDeclarationOrigin.IR_BUILTINS_STUB ->
createMethodReplacement(function)
else ->
createStaticReplacement(function)
}
function is IrSimpleFunction && !function.isFromJava() &&
function.fullValueParameterList.any { it.type.isMultiFieldValueClassType() && !it.type.isNullable() } ->
createMethodReplacement(function)
else -> null
}
}
override val getReplacementRegularClassConstructor: (IrConstructor) -> IrConstructor? =
storageManager.createMemoizedFunctionWithNullableValues { constructor ->
when {
constructor.constructedClass.isMultiFieldValueClass -> null
constructor.isFromJava() -> null
constructor.fullValueParameterList.any { !it.type.isNullable() && it.type.isMultiFieldValueClassType() } ->
createConstructorReplacement(constructor)
else -> null
}
}
override val replaceOverriddenSymbols: (IrSimpleFunction) -> List<IrSimpleFunctionSymbol> = storageManager.createMemoizedFunction {
TODO()
}
}
@@ -5,18 +5,112 @@
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.backend.common.ir.copyTypeParameters
import org.jetbrains.kotlin.backend.jvm.ir.isCompiledToJvmDefault
import org.jetbrains.kotlin.backend.jvm.ir.isJvmInterface
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.declarations.IrFunctionBuilder
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.types.isInt
import org.jetbrains.kotlin.ir.util.*
import java.util.concurrent.ConcurrentHashMap
abstract class MemoizedValueClassAbstractReplacements(protected val irFactory: IrFactory, protected val context: JvmBackendContext) {
protected val propertyMap = ConcurrentHashMap<IrPropertySymbol, IrProperty>()
/**
* Get a replacement for a function or a constructor.
*/
abstract val getReplacementFunction: (IrFunction) -> IrSimpleFunction?
abstract val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction>
protected fun IrFunction.isRemoveAtSpecialBuiltinStub() =
origin == IrDeclarationOrigin.IR_BUILTINS_STUB &&
name.asString() == "remove" &&
valueParameters.size == 1 &&
valueParameters[0].type.isInt()
protected fun IrFunction.isValueClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod(): Boolean {
if (this !is IrSimpleFunction) return false
if (!this.isFakeOverride) return false
val parentClass = parentClassOrNull ?: return false
require(parentClass.isValue)
val overridden = resolveFakeOverride() ?: return false
if (!overridden.parentAsClass.isJvmInterface) return false
if (overridden.modality == Modality.ABSTRACT) return false
// We have a non-abstract interface member.
// It is a JVM default interface method if one of the following conditions are true:
// - it is a Java method,
// - it is a Kotlin function compiled to JVM default interface method.
return overridden.isFromJava() || overridden.isCompiledToJvmDefault(context.state.jvmDefaultMode)
}
protected abstract fun createStaticReplacement(function: IrFunction): IrSimpleFunction
protected abstract fun createMethodReplacement(function: IrFunction): IrSimpleFunction
protected fun commonBuildReplacementInner(
function: IrFunction,
noFakeOverride: Boolean,
body: IrFunction.() -> Unit,
builderBody: IrFunctionBuilder.() -> Unit,
): IrSimpleFunction = irFactory.buildFun {
updateFrom(function)
builderBody()
if (noFakeOverride) {
isFakeOverride = false
}
returnType = function.returnType
}.apply {
parent = function.parent
annotations = function.annotations
copyTypeParameters(function.allTypeParameters)
if (function.metadata != null) {
metadata = function.metadata
function.metadata = null
}
copyAttributes(function as? IrAttributeContainer)
if (function is IrSimpleFunction) {
val propertySymbol = function.correspondingPropertySymbol
if (propertySymbol != null) {
val property = propertyMap.getOrPut(propertySymbol) {
irFactory.buildProperty {
name = propertySymbol.owner.name
updateFrom(propertySymbol.owner)
}.apply {
parent = propertySymbol.owner.parent
copyAttributes(propertySymbol.owner)
annotations = propertySymbol.owner.annotations
// In case this property is declared in an object in another file which is not yet lowered, its backing field will
// be made static later. We have to handle it here though, because this new property will be saved to the cache
// and reused when lowering the same call in all subsequent files, which would be incorrect if it was unlowered.
backingField = context.cachedDeclarations.getStaticBackingField(propertySymbol.owner)
?: propertySymbol.owner.backingField
}
}
correspondingPropertySymbol = property.symbol
when (function) {
propertySymbol.owner.getter -> property.getter = this
propertySymbol.owner.setter -> property.setter = this
else -> error("Orphaned property getter/setter: ${function.render()}")
}
}
overriddenSymbols = function.overriddenSymbols.map {
getReplacementFunction(it.owner)?.symbol ?: it
}
}
body()
}
abstract val replaceOverriddenSymbols: (IrSimpleFunction) -> List<IrSimpleFunctionSymbol>
abstract val getReplacementRegularClassConstructor: (IrConstructor) -> IrConstructor?
}
@@ -0,0 +1,490 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.pop
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.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.MultiFieldValueClassRepresentation
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
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.IrGetField
import org.jetbrains.kotlin.ir.types.*
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.name.Name
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
sealed class MultiFieldValueClassTree<out TI, out TL> {
abstract val type: IrType
val irClass: IrClass?
get() = type.getClass()
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)
}
data class Leaf<out TL>(override val type: IrType, val leafValue: TL) : MultiFieldValueClassTree<Nothing, TL>() {
init {
require(type.isNullable() || !type.isMultiFieldValueClassType())
}
fun <RL> map(transformLeaf: Leaf<TL>.(TL) -> RL) = Leaf(type, transformLeaf(leafValue))
}
data class InternalNode<out TI, out TL> internal constructor(
override val type: IrType, val internalNodeValue: TI, val fields: List<TreeField<TI, TL>>
) : MultiFieldValueClassTree<TI, TL>() {
constructor(
type: IrType, defaultInternalNodeValue: TI, visibility: DescriptorVisibility, defaultLeafValue: TL,
replacements: MemoizedMultiFieldValueClassReplacements
) : this(type, defaultInternalNodeValue, 0.let {
val valueClassRepresentation = type.erasedUpperBound.valueClassRepresentation as MultiFieldValueClassRepresentation
val primaryConstructor = type.erasedUpperBound.primaryConstructor!!
val fieldsByName = replacements.getOldFields(primaryConstructor.constructedClass).associateBy { it.name }
valueClassRepresentation.underlyingPropertyNamesToTypes.map { (name, type) ->
val field = fieldsByName[name]!!
val innerVisibility = field.correspondingPropertySymbol!!.owner.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, field.annotations, defaultLeafValue, replacements)
}
})
data class TreeField<out TI, out TL>(
val name: Name, val type: IrType, val visibility: DescriptorVisibility, val annotations: List<IrConstructorCall>,
val node: MultiFieldValueClassTree<TI, TL>
) {
constructor(
name: Name,
type: IrType,
internalNodeValue: TI,
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))
}
// todo store real fields, cannot restore them
// todo or store fields not pairs
init {
require(!type.isNullable() && type.isMultiFieldValueClassType())
}
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)
}
}
class MultiFieldValueClassSpecificDeclarations(
val valueClass: IrClass,
private val typeSystemContext: IrTypeSystemContext,
private val irFactory: IrFactory,
private val context: JvmBackendContext,
private val replacements: MemoizedMultiFieldValueClassReplacements,
) {
init {
require(valueClass.isMultiFieldValueClass) { "Cannot build ${this::class.simpleName} for not multi-field value class: $valueClass" }
}
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)
}
override fun visitInternalNode(treeNode: InternalNode<Unit, Unit>) {
treeNode.fields.forEach { it.node.visit(this) }
}
})
}.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("$"))
})
}
init {
require(nodeFullNames.size == nodeFullNames.values.distinct().size) { "Ambiguous names found: ${nodeFullNames.values}" }
}
private val indexByLeaf = leaves.withIndex().associate { it.value to it.index }
private val indexesByInternalNode = mutableMapOf<InternalNode<Unit, Unit>, 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}")
}
put(treeNode, range)
}
})
}
val oldPrimaryConstructor = valueClass.primaryConstructor ?: error("Value classes have primary constructors")
val fields = leaves.map { leaf ->
irFactory.buildField {
this.name = nodeFullNames[leaf]!!
this.type = leaf.type
visibility = DescriptorVisibilities.PRIVATE
}
}
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 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())
}
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 IrBuilderWithScope.fieldGetter(receiver: IrValueParameter, field: IrField): IrGetField =
irGetField(irGet(receiver), field)
fun IrFunction.fieldGetter(field: IrField): IrBuilderWithScope.() -> IrGetField = { fieldGetter(dispatchReceiverParameter!!, field) }
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) },
)
}
)
val primaryConstructor: IrConstructor = irFactory.buildConstructor {
updateFrom(oldPrimaryConstructor)
visibility = DescriptorVisibilities.PRIVATE
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER
returnType = oldPrimaryConstructor.returnType
}.apply {
copyTypeParametersFrom(oldPrimaryConstructor)
addFlattenedClassRepresentationToParameters()
val irConstructor = this@apply
body = context.createIrBuilder(irConstructor.symbol).irBlockBody(irConstructor) {
+irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single())
for (i in leaves.indices) {
+irSetField(
receiver = irGet(valueClass.thisReceiver!!),
field = fields[i],
value = irGet(irConstructor.valueParameters[i])
)
}
}
}
val primaryConstructorImpl: IrSimpleFunction = irFactory.buildFun {
name = InlineClassAbi.mangledNameFor(oldPrimaryConstructor, false, false)
visibility = oldPrimaryConstructor.visibility
origin = JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_CONSTRUCTOR
returnType = context.irBuiltIns.unitType
modality = Modality.FINAL
}.apply {
copyTypeParametersFrom(oldPrimaryConstructor)
addFlattenedClassRepresentationToParameters()
// body is added in Lowering file
}
val boxMethod = irFactory.buildFun {
name = Name.identifier(KotlinTypeMapper.BOX_JVM_METHOD_NAME)
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER
returnType = valueClass.defaultType
}.apply {
copyTypeParametersFrom(valueClass)
addFlattenedClassRepresentationToParameters()
// body is added in Lowering file
}
private fun IrFunction.addFlattenedClassRepresentationToParameters() {
for (leaf in leaves) {
addValueParameter {
this.name = nodeFullNames[leaf]!!
this.type = leaf.type
}
}
}
val specializedEqualsMethod = irFactory.buildFun {
name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
// TODO: Revisit this once we allow user defined equals methods in inline classes.
origin = JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD
returnType = context.irBuiltIns.booleanType
}.apply {
copyTypeParametersFrom(oldPrimaryConstructor)
for (leaf in leaves) {
addValueParameter {
this.name = Name.guessByFirstCharacter(
"${InlineClassDescriptorResolver.SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME}$${nodeFullNames[leaf]!!.asString()}"
)
this.type = leaf.type
}
}
for (leaf in leaves) {
addValueParameter {
this.name = Name.guessByFirstCharacter(
"${InlineClassDescriptorResolver.SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME}$${nodeFullNames[leaf]!!.asString()}"
)
this.type = leaf.type
}
}
// body is added in Lowering file
}
val unboxMethods = fields.mapIndexed { index: Int, field: IrField ->
irFactory.buildFun {
name = Name.identifier(KotlinTypeMapper.UNBOX_JVM_METHOD_NAME + index)
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER
returnType = field.type
}.apply {
parent = valueClass
createDispatchReceiverParameter()
body = with(context.createIrBuilder(this.symbol)) {
irExprBody(fieldGetter(field)())
}
}
}
val properties: Map<MultiFieldValueClassTree<Unit, Unit>, IrProperty> =
selfImplementationAgnosticDeclarations.nodeToExpressionGetters.filterKeys { it in nodeFullNames }.mapValues { (node, getter) ->
irFactory.buildProperty {
name = nodeFullNames[node]!!
origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER
visibility = gettersVisibilities[node]!!
}.apply {
annotations = gettersAnnotations[node]!!
parent = valueClass
addGetter {
returnType = node.type
origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER
}.apply {
createDispatchReceiverParameter()
body = with(context.createIrBuilder(this.symbol)) {
irExprBody(getter(this, dispatchReceiverParameter!!))
}
}
}
}
data class VirtualProperty<in T>(val type: IrType, val makeGetter: ExpressionGenerator<T>, val assigner: ExpressionSupplier<T>?) {
constructor(declaration: IrValueDeclaration) : this(
declaration.type,
{ irGet(declaration) },
if (declaration.isAssignable) { _, value -> irSet(declaration, value) } else null,
)
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) } },
)
}
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;
* 2. A slice of fields in another regular class instance;
* 3. Virtual instance constructed with local variables;
* 4. Virtual instance constructed with local parameters;
* 5. Virtual instance constructed with local fields;
* 6. Some mix of the above ones.
*/
inner class ImplementationAgnostic<T>(val virtualFields: List<VirtualProperty<T>>) {
val regularDeclarations = this@MultiFieldValueClassSpecificDeclarations
fun <R> map(f: (R) -> T) = ImplementationAgnostic(virtualFields.map { it.map(f) })
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" }
}
}
val nodeToSymbols = indexesByInternalNode.mapValues { (_, indexes) -> virtualFields.slice(indexes) } +
indexByLeaf.mapValues { (_, index) -> listOf(virtualFields[index]) }
private val internalNodeToExpressionGetters: Map<InternalNode<Unit, Unit>, ExpressionGenerator<T>> =
indexesByInternalNode.mapValues { (node, indexes) ->
{ additionalParameter ->
val arguments = virtualFields.slice(indexes).map { it.makeGetter(this, additionalParameter) }
val innerDeclarations = replacements.getDeclarations(node.irClass!!)!!
irCall(innerDeclarations.boxMethod).apply {
for (i in arguments.indices) {
putValueArgument(i, arguments[i])
}
}
}
}
private val leavesToExpressionGetters: Map<Leaf<Unit>, ExpressionGenerator<T>> =
indexByLeaf.mapValues { (_, index) -> { virtualFields[index].makeGetter(this, it) } }
val nodeToExpressionGetters: Map<MultiFieldValueClassTree<Unit, Unit>, ExpressionGenerator<T>> =
internalNodeToExpressionGetters + leavesToExpressionGetters
operator fun get(name: Name): Pair<ExpressionGenerator<T>, ImplementationAgnostic<T>?>? =
when (val field = loweringRepresentation[name]) {
null -> null
else -> nodeToExpressionGetters[field.node]!! to when (field.node) {
is Leaf -> null
is InternalNode -> {
val irClass = field.node.irClass!!
val declarations = replacements.getDeclarations(irClass)!!
declarations.ImplementationAgnostic(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
@@ -117,8 +117,11 @@ fun IrType.defaultValue(startOffset: Int, endOffset: Int, context: JvmBackendCon
}
}
fun IrType.isInlineClassType(): Boolean =
erasedUpperBound.isSingleFieldValueClass
fun IrType.isInlineClassType(): Boolean = erasedUpperBound.isSingleFieldValueClass
fun IrType.isMultiFieldValueClassType(): Boolean = erasedUpperBound.isMultiFieldValueClass
fun IrType.isValueClassType(): Boolean = erasedUpperBound.isValue
val IrType.upperBound: IrType
get() = erasedUpperBound.symbol.starProjectedType
@@ -256,6 +256,13 @@ val IrDeclaration.isStaticInlineClassReplacement: Boolean
get() = origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT
|| origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR
val IrDeclaration.isStaticMultiFieldValueClassReplacement: Boolean
get() = origin == JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT
|| origin == JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_CONSTRUCTOR
val IrDeclaration.isStaticValueClassReplacement: Boolean
get() = isStaticMultiFieldValueClassReplacement || isStaticInlineClassReplacement
// On the IR backend we represent raw types as star projected types with a special synthetic annotation.
// See `TypeTranslator.translateTypeAnnotations`.
private fun JvmBackendContext.makeRawTypeAnnotation() =
@@ -305,7 +312,7 @@ val IrClass.isSyntheticSingleton: Boolean
fun IrSimpleFunction.suspendFunctionOriginal(): IrSimpleFunction =
if (isSuspend &&
!isStaticInlineClassReplacement &&
!isStaticValueClassReplacement &&
!isOrOverridesDefaultParameterStub() &&
parentAsClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS
)
@@ -422,7 +422,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext, private val
// TODO: get rid of this (probably via some special lowering)
private fun mapOverriddenSpecialBuiltinIfNeeded(callee: IrFunction, superCall: Boolean): JvmMethodSignature? {
// Do not remap calls to static replacements of inline class methods, since they have completely different signatures.
if (callee.isStaticInlineClassReplacement) return null
if (callee.isStaticValueClassReplacement) return null
val overriddenSpecialBuiltinFunction =
(callee.toIrBasedDescriptor().getOverriddenBuiltinReflectingJvmDescriptor() as IrBasedSimpleFunctionDescriptor?)?.owner
if (overriddenSpecialBuiltinFunction != null && !superCall) {
@@ -43,6 +43,7 @@ interface IrDeclarationOrigin {
object GENERATED_DATA_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_DATA_CLASS_MEMBER")
object GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER")
object GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER")
object GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER : IrDeclarationOriginImpl("GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER")
object LOCAL_FUNCTION : IrDeclarationOriginImpl("LOCAL_FUNCTION")
object LOCAL_FUNCTION_FOR_LAMBDA : IrDeclarationOriginImpl("LOCAL_FUNCTION_FOR_LAMBDA")
object CATCH_PARAMETER : IrDeclarationOriginImpl("CATCH_PARAMETER")
+134
View File
@@ -0,0 +1,134 @@
// CHECK_BYTECODE_LISTING
// WITH_STDLIB
// TARGET_BACKEND: JVM_IR
// WORKS_WHEN_VALUE_CLASS
// LANGUAGE: +ValueClasses
@JvmInline
value class A<T : Any>(val x: List<T>)
@JvmInline
value class B(val x: UInt)
@JvmInline
value class C(val x: Int, val y: B, val z: String = "3")
@JvmInline
value class D(val x: C) {
constructor(x: Int, y: UInt, z: Int) : this(C(x, B(y), z.toString()))
init {
println(x.x)
}
}
inline fun inlined(x: Int, y: UInt, z: Int): D {
return D(C(x, B(y), z.toString()))
}
fun notInlined(x: Int, y: UInt, z: Int) = D(C(x, B(y), z.toString()))
@JvmInline
value class E(val x: D) {
var withNonTrivialSetters: D
get() = TODO()
set(_) = TODO()
}
@JvmInline
value class R<T : Any>(val x: Int, val y: UInt, val z: E, val t: A<T>)
fun <T : List<Int>> f(r: R<T>) {
println(r)
println(r.x)
println(r.y)
println(r.z)
println(r.t)
println(r.t.x)
println(r.z.x)
println(r.z.x.x)
println(r.z.x.x.x)
println(r.z.x.x.y)
println(r.z.x.x.z)
println(r.z.x.x.y.x)
}
fun g(e: E) {
}
fun <T : List<Int>> h(r: R<T>) {
g(r.z)
f(r)
r
C(2, B(3U), "") // todo fix box
D(C(2, B(3U), ""))
val x = D(C(2, B(3U), ""))
var y = D(C(4, B(5U), "1"))
println(y)
y = D(C(6, B(7U), "2"))
y = D(6, 7U, 2)
y = inlined(6, 7U, 2)
y = notInlined(6, 7U, 2)
println(y)
}
fun h1() {
var y = inlined(1, 2U, 3) // todo fix box
println(y)
y = inlined(4, 5U, 6)
println(y)
}
class NotInlined(var l: R<List<Int>>, var y: Int) {
override fun toString(): String = l.toString() + l.z.x.x.z
init {
l = l
}
fun trySetter() {
l = l
}
var withNonTrivialSetters: R<List<Int>>
get() = TODO()
set(_) = TODO()
var withNonTrivialSettersWithBF: R<List<Int>> = l
get() {
println("1")
field
field.t
field == field
return field
}
set(value) {
println("3")
field = value
field = field
println("4")
}
val withNonTrivialGettersWithBF: R<List<Int>> = l
get() {
println("1")
field
field.t
field == field
return field
}
}
fun ekeke(x: NotInlined) {
x.l.toString()
var y = x.l
y.toString()
y = x.l
println(y)
x.l = x.l
x.l = R<List<Int>>(x.l.x, x.l.y, x.l.z, x.l.t)
}
// todo add default parameters
fun box() = "bad"//.also { h(R(1, 2U, E(D(C(3, B(4U), "5"))), A(listOf(listOf(6))))) }
+181
View File
@@ -0,0 +1,181 @@
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class A {
// source: 'simple.kt'
private final @org.jetbrains.annotations.NotNull field x: java.util.List
private synthetic method <init>(p0: java.util.List): void
public synthetic final static method box-impl(p0: java.util.List): A
public static @org.jetbrains.annotations.NotNull method constructor-impl(@org.jetbrains.annotations.NotNull p0: java.util.List): java.util.List
public method equals(p0: java.lang.Object): boolean
public static method equals-impl(p0: java.util.List, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: java.util.List, p1: java.util.List): boolean
public final @org.jetbrains.annotations.NotNull method getX(): java.util.List
public method hashCode(): int
public static method hashCode-impl(p0: java.util.List): int
public method toString(): java.lang.String
public static method toString-impl(p0: java.util.List): java.lang.String
public synthetic final method unbox-impl(): java.util.List
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class B {
// source: 'simple.kt'
private final field x: int
private synthetic method <init>(p0: int): void
public synthetic final static method box-impl(p0: int): B
public static method constructor-impl(p0: int): int
public method equals(p0: java.lang.Object): boolean
public static method equals-impl(p0: int, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: int, p1: int): boolean
public final method getX-pVg5ArA(): int
public method hashCode(): int
public static method hashCode-impl(p0: int): int
public method toString(): java.lang.String
public static method toString-impl(p0: int): java.lang.String
public synthetic final method unbox-impl(): int
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class C {
// source: 'simple.kt'
private field x: int
private field y: int
private @org.jetbrains.annotations.NotNull field z: java.lang.String
private synthetic method <init>(p0: int, p1: int, p2: java.lang.String): void
public synthetic final static method box-impl(p0: int, p1: int, p2: java.lang.String): C
public final static method constructor-impl(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void
public static method equals-impl(p0: int, p1: int, p2: java.lang.String, p3: java.lang.Object): boolean
public final static method equals-impl0(p0: int, p1: int, p2: java.lang.String, p3: int, p4: int, p5: java.lang.String): boolean
public final method getX(): int
public final method getY(): int
public final @org.jetbrains.annotations.NotNull method getZ(): java.lang.String
public static method hashCode-impl(p0: int, p1: int, p2: java.lang.String): int
public static method toString-impl(p0: int, p1: int, p2: java.lang.String): java.lang.String
public synthetic final method unbox-impl0(): int
public synthetic final method unbox-impl1(): int
public synthetic final method unbox-impl2(): java.lang.String
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class D {
// source: 'simple.kt'
private field x$x: int
private field x$y: int
private @org.jetbrains.annotations.NotNull field x$z: java.lang.String
private synthetic method <init>(p0: int, p1: int, p2: java.lang.String): void
public synthetic final static method box-impl(p0: int, p1: int, p2: java.lang.String): D
public final static method constructor-impl(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void
public final static @org.jetbrains.annotations.NotNull method constructor-impl(p0: int, p1: int, p2: int): D
public static method equals-impl(p0: int, p1: int, p2: java.lang.String, p3: java.lang.Object): boolean
public final static method equals-impl0(p0: int, p1: int, p2: java.lang.String, p3: int, p4: int, p5: java.lang.String): boolean
public final method getX$x(): int
public final method getX$y(): int
public final @org.jetbrains.annotations.NotNull method getX$z(): java.lang.String
public final @org.jetbrains.annotations.NotNull method getX(): C
public static method hashCode-impl(p0: int, p1: int, p2: java.lang.String): int
public static method toString-impl(p0: int, p1: int, p2: java.lang.String): java.lang.String
public synthetic final method unbox-impl0(): int
public synthetic final method unbox-impl1(): int
public synthetic final method unbox-impl2(): java.lang.String
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class E {
// source: 'simple.kt'
private field x$x$x: int
private field x$x$y: int
private @org.jetbrains.annotations.NotNull field x$x$z: java.lang.String
private synthetic method <init>(p0: int, p1: int, p2: java.lang.String): void
public synthetic final static method box-impl(p0: int, p1: int, p2: java.lang.String): E
public final static method constructor-impl(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void
public static method equals-impl(p0: int, p1: int, p2: java.lang.String, p3: java.lang.Object): boolean
public final static method equals-impl0(p0: int, p1: int, p2: java.lang.String, p3: int, p4: int, p5: java.lang.String): boolean
public final method getX$x$x(): int
public final method getX$x$y(): int
public final @org.jetbrains.annotations.NotNull method getX$x$z(): java.lang.String
public final @org.jetbrains.annotations.NotNull method getX$x(): C
public final @org.jetbrains.annotations.NotNull method getX(): D
public static method hashCode-impl(p0: int, p1: int, p2: java.lang.String): int
public static method toString-impl(p0: int, p1: int, p2: java.lang.String): java.lang.String
public synthetic final method unbox-impl0(): int
public synthetic final method unbox-impl1(): int
public synthetic final method unbox-impl2(): java.lang.String
}
@kotlin.Metadata
public final class NotInlined {
// source: 'simple.kt'
private @org.jetbrains.annotations.NotNull field l$t: java.util.List
private field l$y: int
private field l$z$x$x$x$1: int
private field l$z$x$x$x: int
private field l$z$x$x$y: int
private @org.jetbrains.annotations.NotNull field l$z$x$x$z: java.lang.String
private field y: int
private method <init>(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List, p6: int): void
public synthetic method <init>(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List, p6: int, p7: kotlin.jvm.internal.DefaultConstructorMarker): void
public final @org.jetbrains.annotations.NotNull method getL$t(): java.util.List
public final method getL$y(): int
public final method getL$z$x$x$x(): int
public final method getL$z$x$x$y(): int
public final @org.jetbrains.annotations.NotNull method getL$z$x$x$z(): java.lang.String
public final @org.jetbrains.annotations.NotNull method getL$z$x$x(): C
public final @org.jetbrains.annotations.NotNull method getL$z$x(): D
public final @org.jetbrains.annotations.NotNull method getL$z(): E
public final @org.jetbrains.annotations.NotNull method getL(): R
public final method getY(): int
public final method setL-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void
public final method setY(p0: int): void
public @org.jetbrains.annotations.NotNull method toString(): java.lang.String
public final method trySetter(): void
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class R {
// source: 'simple.kt'
private @org.jetbrains.annotations.NotNull field t: java.util.List
private field y: int
private field z$x$x$x$1: int
private field z$x$x$x: int
private field z$x$x$y: int
private @org.jetbrains.annotations.NotNull field z$x$x$z: java.lang.String
private synthetic method <init>(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List): void
public synthetic final static method box-impl(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List): R
public final static method constructor-impl(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void
public static method equals-impl(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List, p6: java.lang.Object): boolean
public final static method equals-impl0(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List, p6: int, p7: int, p8: int, p9: int, p10: java.lang.String, p11: java.util.List): boolean
public final @org.jetbrains.annotations.NotNull method getT(): java.util.List
public final method getY(): int
public final method getZ$x$x$x(): int
public final method getZ$x$x$y(): int
public final @org.jetbrains.annotations.NotNull method getZ$x$x$z(): java.lang.String
public final @org.jetbrains.annotations.NotNull method getZ$x$x(): C
public final @org.jetbrains.annotations.NotNull method getZ$x(): D
public final @org.jetbrains.annotations.NotNull method getZ(): E
public static method hashCode-impl(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List): int
public static method toString-impl(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List): java.lang.String
public synthetic final method unbox-impl0(): int
public synthetic final method unbox-impl1(): int
public synthetic final method unbox-impl2(): int
public synthetic final method unbox-impl3(): int
public synthetic final method unbox-impl4(): java.lang.String
public synthetic final method unbox-impl5(): java.util.List
}
@kotlin.Metadata
public final class SimpleKt {
// source: 'simple.kt'
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method ekeke(@org.jetbrains.annotations.NotNull p0: NotInlined): void
public final static method f-sUp7gFk-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void
public final static method g-sUp7gFk-ZTiL54U(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void
public final static method h-sUp7gFk-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void
public final static method h1(): void
public final static @org.jetbrains.annotations.NotNull method inlined-OsBMiQA(p0: int, p1: int, p2: int): D
public final static @org.jetbrains.annotations.NotNull method notInlined-OsBMiQA(p0: int, p1: int, p2: int): D
}
@@ -50194,6 +50194,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
public void testEquality() throws Exception {
runTest("compiler/testData/codegen/box/valueClasses/equality.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/valueClasses/simple.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
}
}
@Nested