[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
|
if (name != KotlinTypeMapper.BOX_JVM_METHOD_NAME) return false
|
||||||
|
|
||||||
val ownerType = Type.getObjectType(owner)
|
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 {
|
private fun MethodInsnNode.isInlineClassUnboxingMethodDescriptor(state: GenerationState): Boolean {
|
||||||
if (name != KotlinTypeMapper.UNBOX_JVM_METHOD_NAME) return false
|
if (name != KotlinTypeMapper.UNBOX_JVM_METHOD_NAME) return false
|
||||||
|
|
||||||
val ownerType = Type.getObjectType(owner)
|
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>) =
|
fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) =
|
||||||
|
|||||||
+6
@@ -50194,6 +50194,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
public void testEquality() throws Exception {
|
public void testEquality() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/valueClasses/equality.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
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
|
@Nested
|
||||||
|
|||||||
+1
@@ -346,6 +346,7 @@ abstract class AnnotationCodegen(
|
|||||||
declaration.origin.isSynthetic ->
|
declaration.origin.isSynthetic ->
|
||||||
true
|
true
|
||||||
declaration.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
|
declaration.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
|
||||||
|
declaration.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
|
||||||
declaration.origin == IrDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION ->
|
declaration.origin == IrDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION ->
|
||||||
true
|
true
|
||||||
else ->
|
else ->
|
||||||
|
|||||||
+1
@@ -294,6 +294,7 @@ class ExpressionCodegen(
|
|||||||
(irFunction is IrConstructor && irFunction.parentAsClass.isAnonymousObject) ||
|
(irFunction is IrConstructor && irFunction.parentAsClass.isAnonymousObject) ||
|
||||||
// TODO: Implement this as a lowering, so that we can more easily exclude generated methods.
|
// 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.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.
|
// Although these are accessible from Java, the functions they bridge to already have the assertions.
|
||||||
irFunction.origin == IrDeclarationOrigin.BRIDGE_SPECIAL ||
|
irFunction.origin == IrDeclarationOrigin.BRIDGE_SPECIAL ||
|
||||||
irFunction.origin == JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE ||
|
irFunction.origin == JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE ||
|
||||||
|
|||||||
+2
-1
@@ -37,9 +37,10 @@ object HashCode : IntrinsicMethod() {
|
|||||||
val target = context.state.target
|
val target = context.state.target
|
||||||
when {
|
when {
|
||||||
irFunction.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
|
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_DATA_CLASS_MEMBER ||
|
||||||
irFunction.origin == IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_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)
|
DescriptorAsmUtil.genHashCode(mv, mv, receiverType, target)
|
||||||
}
|
}
|
||||||
target >= JvmTarget.JVM_1_8 && AsmUtil.isPrimitive(receiverJvmType) -> {
|
target >= JvmTarget.JVM_1_8 && AsmUtil.isPrimitive(receiverJvmType) -> {
|
||||||
|
|||||||
@@ -309,6 +309,7 @@ private val jvmFilePhases = listOf(
|
|||||||
forLoopsPhase,
|
forLoopsPhase,
|
||||||
collectionStubMethodLowering,
|
collectionStubMethodLowering,
|
||||||
singleAbstractMethodPhase,
|
singleAbstractMethodPhase,
|
||||||
|
jvmMultiFieldValueClassPhase,
|
||||||
jvmInlineClassPhase,
|
jvmInlineClassPhase,
|
||||||
tailrecPhase,
|
tailrecPhase,
|
||||||
// makePatchParentsPhase(),
|
// makePatchParentsPhase(),
|
||||||
|
|||||||
+1
-1
@@ -112,7 +112,7 @@ internal val bridgePhase = makeIrFilePhase(
|
|||||||
::BridgeLowering,
|
::BridgeLowering,
|
||||||
name = "Bridge",
|
name = "Bridge",
|
||||||
description = "Generate bridges",
|
description = "Generate bridges",
|
||||||
prerequisite = setOf(jvmInlineClassPhase)
|
prerequisite = setOf(jvmInlineClassPhase, jvmMultiFieldValueClassPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
|
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.irBlockBody
|
||||||
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
|
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
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.*
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||||
import org.jetbrains.kotlin.builtins.StandardNames
|
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.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
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.transformStatement
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
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.
|
// Standard library replacements are done on the unmangled names for UInt and ULong classes.
|
||||||
// Collection stubs may require mangling by inline class rules.
|
// Collection stubs may require mangling by inline class rules.
|
||||||
// SAM wrappers may require mangling for fun interfaces with inline class parameters
|
// 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
|
override val replacements: MemoizedValueClassAbstractReplacements
|
||||||
get() = context.inlineClassReplacements
|
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 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
|
if (valueClass.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME)) return
|
||||||
val constructor = context.ir.symbols.jvmInlineAnnotation.constructors.first()
|
val constructor = context.ir.symbols.jvmInlineAnnotation.constructors.first()
|
||||||
valueClass.annotations = valueClass.annotations + IrConstructorCallImpl.fromSymbolOwner(
|
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> {
|
override fun createBridgeFunction(
|
||||||
replacement.valueParameters.forEach {
|
function: IrSimpleFunction,
|
||||||
it.transformChildrenVoid()
|
replacement: IrSimpleFunction
|
||||||
it.defaultValue?.patchDeclarationParents(replacement)
|
): IrSimpleFunction {
|
||||||
}
|
|
||||||
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)
|
|
||||||
|
|
||||||
val bridgeFunction = createBridgeDeclaration(
|
val bridgeFunction = createBridgeDeclaration(
|
||||||
function,
|
function,
|
||||||
when {
|
when {
|
||||||
@@ -113,8 +149,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
|||||||
} else {
|
} else {
|
||||||
createBridgeBody(bridgeFunction, replacement)
|
createBridgeBody(bridgeFunction, replacement)
|
||||||
}
|
}
|
||||||
|
return bridgeFunction
|
||||||
return listOf(replacement, bridgeFunction)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrSimpleFunction.signatureRequiresMangling() =
|
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
|
// Secondary constructors for boxed types get translated to static functions returning
|
||||||
// unboxed arguments. We remove the original constructor.
|
// unboxed arguments. We remove the original constructor.
|
||||||
// Primary constructors' case is handled at the start of transformFunctionFlat
|
// 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.valueParameters.forEach { it.transformChildrenVoid() }
|
||||||
replacement.body = context.createIrBuilder(replacement.symbol, replacement.startOffset, replacement.endOffset).irBlockBody(
|
replacement.body = context.createIrBuilder(replacement.symbol, replacement.startOffset, replacement.endOffset).irBlockBody(
|
||||||
replacement
|
replacement
|
||||||
@@ -211,12 +246,6 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
|||||||
return listOf(replacement)
|
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(
|
private fun IrMemberAccessExpression<*>.buildReplacement(
|
||||||
originalFunction: IrFunction,
|
originalFunction: IrFunction,
|
||||||
original: IrMemberAccessExpression<*>,
|
original: IrMemberAccessExpression<*>,
|
||||||
@@ -420,7 +449,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
|||||||
return super.visitSetValue(expression)
|
return super.visitSetValue(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
|
fun buildPrimaryInlineClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
|
||||||
// Add the default primary constructor
|
// Add the default primary constructor
|
||||||
valueClass.addConstructor {
|
valueClass.addConstructor {
|
||||||
updateFrom(irConstructor)
|
updateFrom(irConstructor)
|
||||||
@@ -465,7 +494,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
|||||||
valueClass.declarations += function
|
valueClass.declarations += function
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun buildBoxFunction(valueClass: IrClass) {
|
fun buildBoxFunction(valueClass: IrClass) {
|
||||||
val function = context.inlineClassReplacements.getBoxFunction(valueClass)
|
val function = context.inlineClassReplacements.getBoxFunction(valueClass)
|
||||||
with(context.createIrBuilder(function.symbol)) {
|
with(context.createIrBuilder(function.symbol)) {
|
||||||
function.body = irExprBody(
|
function.body = irExprBody(
|
||||||
@@ -478,10 +507,6 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
|||||||
valueClass.declarations += function
|
valueClass.declarations += function
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun buildUnboxFunctions(valueClass: IrClass) {
|
|
||||||
buildUnboxFunction(valueClass)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildUnboxFunction(irClass: IrClass) {
|
private fun buildUnboxFunction(irClass: IrClass) {
|
||||||
val function = context.inlineClassReplacements.getUnboxFunction(irClass)
|
val function = context.inlineClassReplacements.getUnboxFunction(irClass)
|
||||||
val field = getInlineClassBackingField(irClass)
|
val field = getInlineClassBackingField(irClass)
|
||||||
@@ -494,7 +519,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
|||||||
irClass.declarations += function
|
irClass.declarations += function
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun buildSpecializedEqualsMethod(valueClass: IrClass) {
|
fun buildSpecializedEqualsMethod(valueClass: IrClass) {
|
||||||
val function = context.inlineClassReplacements.getSpecializedEqualsMethod(valueClass, context.irBuiltIns)
|
val function = context.inlineClassReplacements.getSpecializedEqualsMethod(valueClass, context.irBuiltIns)
|
||||||
val left = function.valueParameters[0]
|
val left = function.valueParameters[0]
|
||||||
val right = function.valueParameters[1]
|
val right = function.valueParameters[1]
|
||||||
|
|||||||
+694
-31
@@ -5,45 +5,398 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm.lower
|
package org.jetbrains.kotlin.backend.jvm.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
|
||||||
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
|
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
|
||||||
import org.jetbrains.kotlin.backend.jvm.isMultiFieldValueClassFieldGetter
|
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.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
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) {
|
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
|
get() = context.multiFieldValueClassReplacements
|
||||||
|
|
||||||
override fun IrClass.isSpecificLoweringLogicApplicable(): Boolean = isMultiFieldValueClass
|
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> {
|
override fun visitClassNew(declaration: IrClass): IrStatement {
|
||||||
TODO()
|
|
||||||
|
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) {
|
private fun handleNonSpecificNewClass(declaration: IrClass) {
|
||||||
TODO("Not yet implemented")
|
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) {
|
override fun handleSpecificNewClass(declaration: IrClass) {
|
||||||
TODO("Not yet implemented")
|
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) {
|
override fun transformSecondaryConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
|
||||||
TODO("Not yet implemented")
|
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) {
|
private fun MultiFieldValueClassSpecificDeclarations.replaceFields() {
|
||||||
TODO("Not yet implemented")
|
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 createBridgeFunction(function: IrSimpleFunction, replacement: IrSimpleFunction): IrSimpleFunction? {
|
||||||
override fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
|
return null // todo
|
||||||
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 {
|
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||||
@@ -52,27 +405,337 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||||
// todo implement
|
val function = expression.symbol.owner
|
||||||
return super.visitFunctionAccess(expression)
|
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 {
|
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)
|
return super.visitCall(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetField(expression: IrGetField): IrExpression {
|
private fun makeLeavesGetters(currentGetter: IrSimpleFunction): List<IrSimpleFunction>? =
|
||||||
// todo implement
|
when (val node = regularClassMFVCPropertyNodes[currentGetter]) {
|
||||||
return super.visitGetField(expression)
|
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 {
|
private fun IrBlockBuilder.makeNewArguments(
|
||||||
// todo implement
|
oldArguments: List<IrExpression?>,
|
||||||
return super.visitGetValue(expression)
|
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 {
|
override fun visitSetValue(expression: IrSetValue): IrExpression {
|
||||||
// todo implement
|
val setters = valueDeclarationsRemapper.setter(expression.symbol) ?: return super.visitSetValue(expression)
|
||||||
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.FileLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
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.InlineClassAbi
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
|
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.builders.irReturn
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.transformStatement
|
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
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendContext) : FileLoweringPass,
|
internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendContext) : FileLoweringPass,
|
||||||
IrElementTransformerVoidWithContext() {
|
IrElementTransformerVoidWithContext() {
|
||||||
abstract val replacements: MemoizedValueClassAbstractReplacements
|
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) {
|
final override fun lower(irFile: IrFile) {
|
||||||
irFile.transformChildrenVoid()
|
irFile.transformChildrenVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun IrClass.isSpecificLoweringLogicApplicable(): Boolean
|
abstract fun IrClass.isSpecificLoweringLogicApplicable(): Boolean
|
||||||
|
|
||||||
abstract fun IrFunction.isSpecificFieldGetter(): Boolean
|
abstract fun IrFunction.isFieldGetterToRemove(): Boolean
|
||||||
|
|
||||||
final override fun visitClassNew(declaration: IrClass): IrStatement {
|
abstract 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) }
|
|
||||||
}
|
|
||||||
|
|
||||||
declaration.transformDeclarationsFlat { memberDeclaration ->
|
abstract fun handleSpecificNewClass(declaration: IrClass)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
|
protected fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
|
||||||
if (function is IrConstructor && function.isPrimary && function.constructedClass.isSpecificLoweringLogicApplicable()) {
|
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)
|
val replacement = replacements.getReplacementFunction(function)
|
||||||
|
|
||||||
if (replacement == null) {
|
if (replacement == null) {
|
||||||
|
if (function is IrConstructor) {
|
||||||
|
val constructorReplacement = replacements.getReplacementRegularClassConstructor(function)
|
||||||
|
if (constructorReplacement != null) {
|
||||||
|
addBindingsFor(function, constructorReplacement)
|
||||||
|
return transformFlattenedConstructor(function, constructorReplacement)
|
||||||
|
}
|
||||||
|
}
|
||||||
function.transformChildrenVoid()
|
function.transformChildrenVoid()
|
||||||
// Non-mangled functions can override mangled functions under some conditions, e.g., a function
|
// 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
|
// `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)
|
addBindingsFor(function, replacement)
|
||||||
return when (function) {
|
return when (function) {
|
||||||
is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement)
|
is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement)
|
||||||
is IrConstructor -> transformConstructorFlat(function, replacement)
|
is IrConstructor -> transformSecondaryConstructorFlat(function, replacement)
|
||||||
else -> throw IllegalStateException()
|
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(
|
private fun IrFunction.hashSuffix(): String? = InlineClassAbi.hashSuffix(
|
||||||
this,
|
this,
|
||||||
context.state.functionsWithInlineClassReturnTypesMangled,
|
context.state.functionsWithInlineClassReturnTypesMangled,
|
||||||
context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures
|
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)
|
return listOfNotNull(replacement, bridgeFunction)
|
||||||
|
}
|
||||||
protected abstract fun buildSpecializedEqualsMethod(valueClass: IrClass) // todo hashCode
|
|
||||||
|
|
||||||
protected abstract fun addJvmInlineAnnotation(valueClass: IrClass)
|
|
||||||
|
|
||||||
final override fun visitReturn(expression: IrReturn): IrExpression {
|
final override fun visitReturn(expression: IrReturn): IrExpression {
|
||||||
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
|
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
|
||||||
@@ -146,7 +139,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
|
|||||||
return super.visitReturn(expression)
|
return super.visitReturn(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitStatementContainer(container: IrStatementContainer) {
|
protected open fun visitStatementContainer(container: IrStatementContainer) {
|
||||||
container.statements.transformFlat { statement ->
|
container.statements.transformFlat { statement ->
|
||||||
if (statement is IrFunction)
|
if (statement is IrFunction)
|
||||||
transformFunctionFlat(statement)
|
transformFunctionFlat(statement)
|
||||||
@@ -172,4 +165,11 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
|
|||||||
else
|
else
|
||||||
super.visitAnonymousInitializerNew(declaration)
|
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 {
|
fun mangledNameFor(irFunction: IrFunction, mangleReturnTypes: Boolean, useOldMangleRules: Boolean): Name {
|
||||||
if (irFunction is IrConstructor) {
|
if (irFunction is IrConstructor) {
|
||||||
// Note that we might drop this convention and use standard mangling for constructors too, see KT-37186.
|
// 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()}"
|
"Should not mangle names of non-inline class constructors: ${irFunction.render()}"
|
||||||
}
|
}
|
||||||
return Name.identifier("constructor-impl")
|
return Name.identifier("constructor-impl")
|
||||||
}
|
}
|
||||||
|
|
||||||
val suffix = hashSuffix(irFunction, mangleReturnTypes, useOldMangleRules)
|
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
|
return irFunction.name
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,16 +127,16 @@ object InlineClassAbi {
|
|||||||
val IrType.requiresMangling: Boolean
|
val IrType.requiresMangling: Boolean
|
||||||
get() {
|
get() {
|
||||||
val irClass = erasedUpperBound
|
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>
|
val IrFunction.fullValueParameterList: List<IrValueParameter>
|
||||||
get() = listOfNotNull(extensionReceiverParameter) + valueParameters
|
get() = listOfNotNull(extensionReceiverParameter) + valueParameters
|
||||||
|
|
||||||
val IrFunction.hasMangledParameters: Boolean
|
val IrFunction.hasMangledParameters: Boolean
|
||||||
get() = dispatchReceiverParameter != null && parentAsClass.isSingleFieldValueClass ||
|
get() = dispatchReceiverParameter != null && parentAsClass.isValue ||
|
||||||
fullValueParameterList.any { it.type.requiresMangling } ||
|
fullValueParameterList.any { it.type.requiresMangling } ||
|
||||||
(this is IrConstructor && constructedClass.isSingleFieldValueClass)
|
(this is IrConstructor && constructedClass.isValue)
|
||||||
|
|
||||||
val IrFunction.hasMangledReturnType: Boolean
|
val IrFunction.hasMangledReturnType: Boolean
|
||||||
get() = returnType.isInlineClassType() && parentClassOrNull?.isFileClass != true
|
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 &&
|
get() = (parent as? IrClass)?.isSingleFieldValueClass == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
|
||||||
correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.name == parentAsClass.inlineClassFieldName } == true
|
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 &&
|
get() = (parent as? IrClass)?.isMultiFieldValueClass == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
|
||||||
correspondingPropertySymbol?.let {
|
correspondingPropertySymbol?.let {
|
||||||
val multiFieldValueClassRepresentation = parentAsClass.multiFieldValueClassRepresentation
|
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.symbols.*
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
|
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.types.defaultType
|
||||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -143,7 +144,8 @@ class JvmBackendContext(
|
|||||||
|
|
||||||
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
|
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()
|
val continuationClassesVarsCountByType: MutableMap<IrAttributeContainer, Map<Type, Int>> = hashMapOf()
|
||||||
|
|
||||||
@@ -197,6 +199,20 @@ class JvmBackendContext(
|
|||||||
multifileFacade.setValue(newPartClasses.toMutableList())
|
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) {
|
for ((staticReplacement, original) in inlineClassReplacements.originalFunctionForStaticReplacement) {
|
||||||
if (staticReplacement !is IrSimpleFunction) continue
|
if (staticReplacement !is IrSimpleFunction) continue
|
||||||
val newOriginal = functionSymbolMap[original.symbol]?.owner ?: 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_WHEN : IrDeclarationOriginImpl("ENUM_MAPPINGS_FOR_WHEN", isSynthetic = true)
|
||||||
object ENUM_MAPPINGS_FOR_ENTRIES : IrDeclarationOriginImpl("ENUM_MAPPINGS_FOR_ENTRIES", 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_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 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_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_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_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true)
|
||||||
object GENERATED_EXTENDED_MAIN : IrDeclarationOriginImpl("GENERATED_EXTENDED_MAIN", isSynthetic = true)
|
object GENERATED_EXTENDED_MAIN : IrDeclarationOriginImpl("GENERATED_EXTENDED_MAIN", isSynthetic = true)
|
||||||
object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", 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
|
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.backend.jvm.ir.*
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
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.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
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.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
|
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||||
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
|
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.ir.util.*
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
|
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
|
||||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
import org.jetbrains.kotlin.utils.alwaysNull
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,9 +38,8 @@ class MemoizedInlineClassReplacements(
|
|||||||
context: JvmBackendContext
|
context: JvmBackendContext
|
||||||
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
|
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
|
||||||
private val storageManager = LockBasedStorageManager("inline-class-replacements")
|
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()
|
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
|
// Don't mangle anonymous or synthetic functions, except for generated SAM wrapper methods
|
||||||
(it.isLocal && it is IrSimpleFunction && it.overriddenSymbols.isEmpty()) ||
|
(it.isLocal && it is IrSimpleFunction && it.overriddenSymbols.isEmpty()) ||
|
||||||
(it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR && it.visibility == DescriptorVisibilities.LOCAL) ||
|
(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 ->
|
it.origin.isSynthetic && it.origin != IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION ->
|
||||||
null
|
null
|
||||||
|
|
||||||
@@ -64,7 +68,7 @@ class MemoizedInlineClassReplacements(
|
|||||||
when {
|
when {
|
||||||
it.isRemoveAtSpecialBuiltinStub() ->
|
it.isRemoveAtSpecialBuiltinStub() ->
|
||||||
null
|
null
|
||||||
it.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
|
it.isValueClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
|
||||||
it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB ->
|
it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB ->
|
||||||
createMethodReplacement(it)
|
createMethodReplacement(it)
|
||||||
else ->
|
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
|
* 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
|
* static function named "box-impl" which takes an unboxed value and returns
|
||||||
@@ -148,7 +129,7 @@ class MemoizedInlineClassReplacements(
|
|||||||
return specializedEqualsCache.computeIfAbsent(irClass) {
|
return specializedEqualsCache.computeIfAbsent(irClass) {
|
||||||
irFactory.buildFun {
|
irFactory.buildFun {
|
||||||
name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
|
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
|
origin = JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
|
||||||
returnType = irBuiltIns.booleanType
|
returnType = irBuiltIns.booleanType
|
||||||
}.apply {
|
}.apply {
|
||||||
@@ -168,7 +149,7 @@ class MemoizedInlineClassReplacements(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
|
override fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
|
||||||
buildReplacement(function, function.origin) {
|
buildReplacement(function, function.origin) {
|
||||||
originalFunctionForMethodReplacement[this] = function
|
originalFunctionForMethodReplacement[this] = function
|
||||||
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this, index = -1)
|
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) {
|
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT, noFakeOverride = true) {
|
||||||
originalFunctionForStaticReplacement[this] = function
|
originalFunctionForStaticReplacement[this] = function
|
||||||
|
|
||||||
@@ -246,8 +227,7 @@ class MemoizedInlineClassReplacements(
|
|||||||
noFakeOverride: Boolean,
|
noFakeOverride: Boolean,
|
||||||
useOldManglingScheme: Boolean,
|
useOldManglingScheme: Boolean,
|
||||||
body: IrFunction.() -> Unit,
|
body: IrFunction.() -> Unit,
|
||||||
): IrSimpleFunction = irFactory.buildFun {
|
): IrSimpleFunction = commonBuildReplacementInner(function, noFakeOverride, body) {
|
||||||
updateFrom(function)
|
|
||||||
if (function is IrConstructor) {
|
if (function is IrConstructor) {
|
||||||
// The [updateFrom] call will set the modality to FINAL for constructors, while the JVM backend would use OPEN here.
|
// The [updateFrom] call will set the modality to FINAL for constructors, while the JVM backend would use OPEN here.
|
||||||
modality = Modality.OPEN
|
modality = Modality.OPEN
|
||||||
@@ -260,53 +240,11 @@ class MemoizedInlineClassReplacements(
|
|||||||
else ->
|
else ->
|
||||||
replacementOrigin
|
replacementOrigin
|
||||||
}
|
}
|
||||||
if (noFakeOverride) {
|
|
||||||
isFakeOverride = false
|
|
||||||
}
|
|
||||||
name = InlineClassAbi.mangledNameFor(function, mangleReturnTypes, useOldManglingScheme)
|
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> =
|
override val replaceOverriddenSymbols: (IrSimpleFunction) -> List<IrSimpleFunctionSymbol> =
|
||||||
storageManager.createMemoizedFunction { irSimpleFunction ->
|
storageManager.createMemoizedFunction { irSimpleFunction ->
|
||||||
irSimpleFunction.overriddenSymbols.map {
|
irSimpleFunction.overriddenSymbols.map {
|
||||||
|
|||||||
+239
-8
@@ -5,11 +5,24 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm
|
package org.jetbrains.kotlin.backend.jvm
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
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.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.storage.LockBasedStorageManager
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,21 +30,239 @@ import java.util.concurrent.ConcurrentHashMap
|
|||||||
*/
|
*/
|
||||||
class MemoizedMultiFieldValueClassReplacements(
|
class MemoizedMultiFieldValueClassReplacements(
|
||||||
irFactory: IrFactory,
|
irFactory: IrFactory,
|
||||||
context: JvmBackendContext
|
context: JvmBackendContext,
|
||||||
|
private val typeSystemContext: IrTypeSystemContext
|
||||||
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
|
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
|
||||||
private val storageManager = LockBasedStorageManager("multi-field-value-class-replacements")
|
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 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.
|
* Get a replacement for a function or a constructor.
|
||||||
*/
|
*/
|
||||||
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? = storageManager.createMemoizedFunctionWithNullableValues {
|
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
|
||||||
TODO()
|
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 {
|
override val replaceOverriddenSymbols: (IrSimpleFunction) -> List<IrSimpleFunctionSymbol> = storageManager.createMemoizedFunction {
|
||||||
TODO()
|
TODO()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+98
-4
@@ -5,18 +5,112 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm
|
package org.jetbrains.kotlin.backend.jvm
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
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) {
|
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.
|
* Get a replacement for a function or a constructor.
|
||||||
*/
|
*/
|
||||||
abstract val getReplacementFunction: (IrFunction) -> IrSimpleFunction?
|
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 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 =
|
fun IrType.isInlineClassType(): Boolean = erasedUpperBound.isSingleFieldValueClass
|
||||||
erasedUpperBound.isSingleFieldValueClass
|
|
||||||
|
fun IrType.isMultiFieldValueClassType(): Boolean = erasedUpperBound.isMultiFieldValueClass
|
||||||
|
|
||||||
|
fun IrType.isValueClassType(): Boolean = erasedUpperBound.isValue
|
||||||
|
|
||||||
val IrType.upperBound: IrType
|
val IrType.upperBound: IrType
|
||||||
get() = erasedUpperBound.symbol.starProjectedType
|
get() = erasedUpperBound.symbol.starProjectedType
|
||||||
|
|||||||
@@ -256,6 +256,13 @@ val IrDeclaration.isStaticInlineClassReplacement: Boolean
|
|||||||
get() = origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT
|
get() = origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT
|
||||||
|| origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR
|
|| 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.
|
// On the IR backend we represent raw types as star projected types with a special synthetic annotation.
|
||||||
// See `TypeTranslator.translateTypeAnnotations`.
|
// See `TypeTranslator.translateTypeAnnotations`.
|
||||||
private fun JvmBackendContext.makeRawTypeAnnotation() =
|
private fun JvmBackendContext.makeRawTypeAnnotation() =
|
||||||
@@ -305,7 +312,7 @@ val IrClass.isSyntheticSingleton: Boolean
|
|||||||
|
|
||||||
fun IrSimpleFunction.suspendFunctionOriginal(): IrSimpleFunction =
|
fun IrSimpleFunction.suspendFunctionOriginal(): IrSimpleFunction =
|
||||||
if (isSuspend &&
|
if (isSuspend &&
|
||||||
!isStaticInlineClassReplacement &&
|
!isStaticValueClassReplacement &&
|
||||||
!isOrOverridesDefaultParameterStub() &&
|
!isOrOverridesDefaultParameterStub() &&
|
||||||
parentAsClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS
|
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)
|
// TODO: get rid of this (probably via some special lowering)
|
||||||
private fun mapOverriddenSpecialBuiltinIfNeeded(callee: IrFunction, superCall: Boolean): JvmMethodSignature? {
|
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.
|
// 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 =
|
val overriddenSpecialBuiltinFunction =
|
||||||
(callee.toIrBasedDescriptor().getOverriddenBuiltinReflectingJvmDescriptor() as IrBasedSimpleFunctionDescriptor?)?.owner
|
(callee.toIrBasedDescriptor().getOverriddenBuiltinReflectingJvmDescriptor() as IrBasedSimpleFunctionDescriptor?)?.owner
|
||||||
if (overriddenSpecialBuiltinFunction != null && !superCall) {
|
if (overriddenSpecialBuiltinFunction != null && !superCall) {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ interface IrDeclarationOrigin {
|
|||||||
object GENERATED_DATA_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_DATA_CLASS_MEMBER")
|
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_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_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 : IrDeclarationOriginImpl("LOCAL_FUNCTION")
|
||||||
object LOCAL_FUNCTION_FOR_LAMBDA : IrDeclarationOriginImpl("LOCAL_FUNCTION_FOR_LAMBDA")
|
object LOCAL_FUNCTION_FOR_LAMBDA : IrDeclarationOriginImpl("LOCAL_FUNCTION_FOR_LAMBDA")
|
||||||
object CATCH_PARAMETER : IrDeclarationOriginImpl("CATCH_PARAMETER")
|
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 {
|
public void testEquality() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/valueClasses/equality.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
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
|
@Nested
|
||||||
|
|||||||
Reference in New Issue
Block a user