[IR] Implement MFVC basic flattening
#KT-1179
This commit is contained in:
committed by
teamcity
parent
93ffd9f233
commit
2896f5613d
+4
-2
@@ -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>) =
|
||||
|
||||
+6
@@ -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
|
||||
|
||||
+1
@@ -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 ->
|
||||
|
||||
+1
@@ -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 ||
|
||||
|
||||
+2
-1
@@ -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(),
|
||||
|
||||
+1
-1
@@ -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() {
|
||||
|
||||
+60
-35
@@ -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]
|
||||
|
||||
+694
-31
@@ -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
|
||||
}
|
||||
|
||||
+53
-53
@@ -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
|
||||
|
||||
+5
@@ -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)
|
||||
|
||||
+16
-78
@@ -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 {
|
||||
|
||||
+239
-8
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+98
-4
@@ -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?
|
||||
}
|
||||
+490
@@ -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
|
||||
)
|
||||
|
||||
+1
-1
@@ -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")
|
||||
|
||||
@@ -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))))) }
|
||||
@@ -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
|
||||
}
|
||||
+6
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user