Revert "rra/zhelenskiy/flattening_impl"

This reverts commit b7f5c039a6.
This commit is contained in:
Evgeniy.Zhelenskiy
2022-10-07 12:40:25 +02:00
committed by teamcity
parent 71381ec8e2
commit 93ffd9f233
64 changed files with 332 additions and 15451 deletions
@@ -346,7 +346,6 @@ abstract class AnnotationCodegen(
declaration.origin.isSynthetic ->
true
declaration.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
declaration.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
declaration.origin == IrDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION ->
true
else ->
@@ -294,7 +294,6 @@ class ExpressionCodegen(
(irFunction is IrConstructor && irFunction.parentAsClass.isAnonymousObject) ||
// TODO: Implement this as a lowering, so that we can more easily exclude generated methods.
irFunction.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
irFunction.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
// Although these are accessible from Java, the functions they bridge to already have the assertions.
irFunction.origin == IrDeclarationOrigin.BRIDGE_SPECIAL ||
irFunction.origin == JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE ||
@@ -37,10 +37,9 @@ object HashCode : IntrinsicMethod() {
val target = context.state.target
when {
irFunction.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
irFunction.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
irFunction.origin == IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER ||
irFunction.origin == IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER -> {
// TODO generate or lower IR for data class / value class 'hashCode'?
// TODO generate or lower IR for data class / inline class 'hashCode'?
DescriptorAsmUtil.genHashCode(mv, mv, receiverType, target)
}
target >= JvmTarget.JVM_1_8 && AsmUtil.isPrimitive(receiverJvmType) -> {
@@ -309,7 +309,6 @@ private val jvmFilePhases = listOf(
forLoopsPhase,
collectionStubMethodLowering,
singleAbstractMethodPhase,
jvmMultiFieldValueClassPhase,
jvmInlineClassPhase,
tailrecPhase,
// makePatchParentsPhase(),
@@ -11,9 +11,9 @@ import org.jetbrains.kotlin.backend.common.lower.VariableRemapper
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irNot
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.MultiFieldValueClassMapping
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.RegularMapping
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.SpecialBridge
import org.jetbrains.kotlin.backend.jvm.ir.*
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
import kotlin.collections.set
/*
* Generate bridge methods to fix virtual dispatch after type erasure and to adapt Kotlin collections to
@@ -113,7 +112,7 @@ internal val bridgePhase = makeIrFilePhase(
::BridgeLowering,
name = "Bridge",
description = "Generate bridges",
prerequisite = setOf(jvmInlineClassPhase, jvmMultiFieldValueClassPhase, inheritedDefaultMethodsOnClassesPhase)
prerequisite = setOf(jvmInlineClassPhase)
)
internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
@@ -235,7 +234,6 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
irClass.declarations.remove(irFunction)
irClass.addAbstractMethodStub(irFunction)
}
irFunction.modality != Modality.FINAL -> {
// If we have a non-abstract, non-final fake-override we need to put in an additional bridge which uses
// INVOKESPECIAL to call the special bridge implementation in the superclass.
@@ -264,7 +262,6 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
irClass.addSpecialBridge(superBridge, superTarget)
}
}
else -> {
// If the method is final,
// then we will not override it in a subclass and we do not need to generate an additional stub method.
@@ -434,11 +431,6 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
}.apply {
copyAttributes(target)
copyParametersWithErasure(this@addBridge, bridge.overridden)
with(context.multiFieldValueClassReplacements) {
bindingNewFunctionToParameterTemplateStructure[bridge.overridden]?.also {
bindingNewFunctionToParameterTemplateStructure[this@apply] = it
}
}
// If target is a throwing stub, bridge also should just throw UnsupportedOperationException.
// Otherwise, it might throw ClassCastException when downcasting bridge argument to expected type.
@@ -494,11 +486,6 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
context.functionsWithSpecialBridges.add(target)
copyParametersWithErasure(this@addSpecialBridge, specialBridge.overridden, specialBridge.substitutedParameterTypes)
with(context.multiFieldValueClassReplacements) {
bindingNewFunctionToParameterTemplateStructure[specialBridge.overridden]?.also {
bindingNewFunctionToParameterTemplateStructure[this@apply] = it
}
}
body = context.createIrBuilder(symbol, startOffset, endOffset).irBlockBody {
specialBridge.methodInfo?.let { info ->
@@ -623,132 +610,22 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
target: IrSimpleFunction,
superQualifierSymbol: IrClassSymbol? = null
) =
irCastIfNeeded(irBlock {
+irReturn(irCall(target, origin = IrStatementOrigin.BRIDGE_DELEGATION, superQualifierSymbol = superQualifierSymbol).apply {
val targetStructure = getStructure(target)
val bridgeStructure = getStructure(bridge)
if (targetStructure == null && bridgeStructure == null) {
for ((param, targetParam) in bridge.explicitParameters.zip(target.explicitParameters)) {
putArgument(targetParam, irGetOrCast(bridge, param, targetParam))
}
} else {
this@irBlock.addBoxedAndUnboxedMfvcArguments(targetStructure, bridgeStructure, target, bridge, this)
irCastIfNeeded(
irCall(target, origin = IrStatementOrigin.BRIDGE_DELEGATION, superQualifierSymbol = superQualifierSymbol).apply {
for ((param, targetParam) in bridge.explicitParameters.zip(target.explicitParameters)) {
putArgument(
targetParam,
irGet(param).let { argument ->
if (param == bridge.dispatchReceiverParameter)
argument
else
irCastIfNeeded(argument, targetParam.type.upperBound)
}
)
}
})
}.unwrapBlock(), bridge.returnType.upperBound)
private fun getStructure(function: IrSimpleFunction): List<MemoizedMultiFieldValueClassReplacements.RemappedParameter>? {
val mfvcOrOriginal = context.inlineClassReplacements.originalFunctionForMethodReplacement[function]
?: context.inlineClassReplacements.originalFunctionForStaticReplacement[function]
?: function
val structure = context.multiFieldValueClassReplacements
.bindingNewFunctionToParameterTemplateStructure[mfvcOrOriginal] ?: return null
require(structure.sumOf { it.valueParameters.size } == function.explicitParametersCount) {
"Bad parameters structure: $structure"
}
return structure
}
private fun IrBlockBuilder.addBoxedAndUnboxedMfvcArguments(
targetStructure: List<MemoizedMultiFieldValueClassReplacements.RemappedParameter>?,
bridgeStructure: List<MemoizedMultiFieldValueClassReplacements.RemappedParameter>?,
target: IrSimpleFunction,
bridge: IrSimpleFunction,
irCall: IrCall
) {
require(
targetStructure == null || bridgeStructure == null ||
bridgeStructure.size == targetStructure.size &&
(targetStructure zip bridgeStructure).none { (targetParameter, bridgeParameter) ->
targetParameter is MultiFieldValueClassMapping && bridgeParameter is MultiFieldValueClassMapping &&
targetParameter.rootMfvcNode != bridgeParameter.rootMfvcNode
}
) { "Incompatible structures: $bridgeStructure and $targetStructure" }
val targetExplicitParameters = target.explicitParameters
val bridgeExplicitParameters = bridge.explicitParameters
var targetIndex = 0
var bridgeIndex = 0
var structureIndex = 0
while (targetIndex < targetExplicitParameters.size && bridgeIndex < bridgeExplicitParameters.size) {
val targetRemappedParameter = targetStructure?.get(structureIndex)
val bridgeRemappedParameter = bridgeStructure?.get(structureIndex)
when (targetRemappedParameter) {
is MultiFieldValueClassMapping -> when (bridgeRemappedParameter) {
is MultiFieldValueClassMapping -> {
require(bridgeRemappedParameter.rootMfvcNode == targetRemappedParameter.rootMfvcNode) {
"Incompatible parameters: $bridgeRemappedParameter, $targetRemappedParameter"
}
repeat(bridgeRemappedParameter.valueParameters.size) {
val bridgeParameter = bridgeExplicitParameters[bridgeIndex++]
val targetParameter = targetExplicitParameters[targetIndex++]
irCall.putArgument(targetParameter, irGetOrCast(bridge, bridgeParameter, targetParameter))
}
}
is RegularMapping, null -> {
val bridgeParameter = bridgeExplicitParameters[bridgeIndex++]
val targetParameterType = targetRemappedParameter.rootMfvcNode.mfvc.defaultType
val instance = targetRemappedParameter.rootMfvcNode.createInstanceFromBox(
this,
irCastIfNeeded(irGet(bridgeParameter), targetParameterType),
getOptimizedPublicAccess(target, targetRemappedParameter.rootMfvcNode.mfvc)
) { error("Not applicable") }
val newArguments = instance.makeFlattenedGetterExpressions(this)
for (newArgument in newArguments) {
irCall.putArgument(targetExplicitParameters[targetIndex++], newArgument)
}
}
}
is RegularMapping, null -> {
val targetParameter = targetExplicitParameters[targetIndex]
when (bridgeRemappedParameter) {
is MultiFieldValueClassMapping -> {
val valueArguments = List(bridgeRemappedParameter.rootMfvcNode.leavesCount) {
irGet(bridgeExplicitParameters[bridgeIndex++])
}
val boxCall = bridgeRemappedParameter.rootMfvcNode.makeBoxedExpression(
this, bridgeRemappedParameter.typeArguments, valueArguments
)
irCall.putArgument(targetParameter, irCastIfNeeded(boxCall, targetParameter.type.upperBound))
}
is RegularMapping, null -> {
val bridgeParameter = bridgeExplicitParameters[bridgeIndex++]
irCall.putArgument(targetParameter, irGetOrCast(bridge, bridgeParameter, targetParameter))
}
}
targetIndex++
}
}
structureIndex++
}
require(targetIndex == targetExplicitParameters.size && bridgeIndex == bridgeExplicitParameters.size) {
"Incorrect bridge:\n${bridge.dump()}\n\nfor target\n${target.dump()}"
}
require((targetStructure == null || structureIndex == targetStructure.size)) {
"Invalid structure index $structureIndex for $targetStructure"
}
require((bridgeStructure == null || structureIndex == bridgeStructure.size)) {
"Invalid structure index $structureIndex for $bridgeStructure"
}
}
private fun IrBuilderWithScope.irGetOrCast(
bridge: IrSimpleFunction,
bridgeParameter: IrValueParameter,
targetParameter: IrValueParameter
) =
irGet(bridgeParameter).let { argument ->
if (bridgeParameter == bridge.dispatchReceiverParameter)
argument
else
irCastIfNeeded(argument, targetParameter.type.upperBound)
}
},
bridge.returnType.upperBound
)
private fun IrBuilderWithScope.irCastIfNeeded(expression: IrExpression, to: IrType): IrExpression =
if (expression.type == to || to.isAny() || to.isNullableAny()) expression else irImplicitCast(expression, to)
@@ -12,11 +12,7 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.MultiFieldValueClassMapping
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.RegularMapping
import org.jetbrains.kotlin.backend.jvm.fullValueParameterList
import org.jetbrains.kotlin.backend.jvm.ir.*
import org.jetbrains.kotlin.backend.jvm.makeBoxedExpression
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -95,7 +91,6 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo
val superClassType = superMethod.parentAsClass.defaultType
val defaultImplFun = context.cachedDeclarations.getDefaultImplsFunction(superMethod)
val classStartOffset = classOverride.parentAsClass.startOffset
val backendContext = context
context.createIrBuilder(irFunction.symbol, classStartOffset, classStartOffset).apply {
irFunction.body = irBlockBody {
+irReturn(
@@ -105,50 +100,15 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo
}
passTypeArgumentsFrom(irFunction, offset = superMethod.parentAsClass.typeParameters.size)
var offset = 0
irFunction.dispatchReceiverParameter?.let {
putValueArgument(0, irGet(it).reinterpretAsDispatchReceiverOfType(superClassType))
}
val mfvcOrOriginal = backendContext.inlineClassReplacements.originalFunctionForMethodReplacement[classOverride]
?: classOverride
val bindingNewFunctionToParameterTemplateStructure = backendContext.multiFieldValueClassReplacements
.bindingNewFunctionToParameterTemplateStructure
val structure = bindingNewFunctionToParameterTemplateStructure[mfvcOrOriginal]?.let { structure ->
require(structure.sumOf { it.valueParameters.size } == classOverride.explicitParametersCount) {
"Bad parameters structure: $structure"
}
if (defaultImplFun.explicitParametersCount == irFunction.explicitParametersCount) {
null
} else {
require(structure.size == defaultImplFun.explicitParametersCount) { "Bad parameters structure: $structure" }
structure
}
}
require(structure == null || structure.first() is RegularMapping) {
"Dispatch receiver for method replacement cannot be flattened"
}
val sourceFullValueParameterList = irFunction.fullValueParameterList
if (structure == null) {
for ((index, parameter) in sourceFullValueParameterList.withIndex()) {
putValueArgument(1 + index, irGet(parameter))
}
} else {
var flattenedIndex = 0
for (i in 1 until structure.size) {
when (val remappedParameter = structure[i]) {
is MultiFieldValueClassMapping -> {
val valueArguments = remappedParameter.valueParameters.indices.map {
irGet(sourceFullValueParameterList[flattenedIndex++])
}
val boxedExpression = remappedParameter.rootMfvcNode.makeBoxedExpression(
this@irBlockBody, remappedParameter.typeArguments, valueArguments
)
putValueArgument(i, boxedExpression)
}
is RegularMapping -> putValueArgument(i, irGet(sourceFullValueParameterList[flattenedIndex++]))
}
}
putValueArgument(
offset++,
irGet(it).reinterpretAsDispatchReceiverOfType(superClassType)
)
}
irFunction.extensionReceiverParameter?.let { putValueArgument(offset++, irGet(it)) }
irFunction.valueParameters.mapIndexed { i, parameter -> putValueArgument(i + offset, irGet(parameter)) }
}
)
}
@@ -47,7 +47,7 @@ private class JvmDefaultConstructorLowering(val context: JvmBackendContext) : Cl
if (DescriptorVisibilities.isPrivate(primaryConstructor.visibility))
return
if (primaryConstructor.hasMangledParameters())
if (primaryConstructor.hasMangledParameters)
return
if (primaryConstructor.valueParameters.isEmpty() || !primaryConstructor.valueParameters.all { it.hasDefaultValue() })
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.builtins.StandardNames
@@ -22,7 +24,6 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.transformStatement
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
@@ -30,6 +31,7 @@ import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
@@ -41,9 +43,7 @@ val jvmInlineClassPhase = makeIrFilePhase(
// Standard library replacements are done on the unmangled names for UInt and ULong classes.
// Collection stubs may require mangling by inline class rules.
// SAM wrappers may require mangling for fun interfaces with inline class parameters
prerequisite = setOf(
forLoopsPhase, jvmBuiltInsPhase, collectionStubMethodLowering, singleAbstractMethodPhase, jvmMultiFieldValueClassPhase
),
prerequisite = setOf(forLoopsPhase, jvmBuiltInsPhase, collectionStubMethodLowering, singleAbstractMethodPhase),
)
/**
@@ -58,15 +58,73 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
override val replacements: MemoizedValueClassAbstractReplacements
get() = context.inlineClassReplacements
private val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>()
override fun IrClass.isSpecificLoweringLogicApplicable(): Boolean = isSingleFieldValueClass
override fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
for ((param, newParam) in original.explicitParameters.zip(replacement.explicitParameters)) {
valueMap[param.symbol] = newParam
}
override fun IrFunction.isSpecificFieldGetter(): Boolean = isInlineClassFieldGetter
override fun addJvmInlineAnnotation(valueClass: IrClass) {
if (valueClass.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME)) return
val constructor = context.ir.symbols.jvmInlineAnnotation.constructors.first()
valueClass.annotations = valueClass.annotations + IrConstructorCallImpl.fromSymbolOwner(
constructor.owner.returnType,
constructor
)
}
override fun createBridgeDeclaration(source: IrSimpleFunction, replacement: IrSimpleFunction, mangledName: Name): IrSimpleFunction =
override fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach {
it.transformChildrenVoid()
it.defaultValue?.patchDeclarationParents(replacement)
}
allScopes.push(createScope(function))
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
allScopes.pop()
replacement.copyAttributes(function)
// Don't create a wrapper for functions which are only used in an unboxed context
if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null)
return listOf(replacement)
val bridgeFunction = createBridgeDeclaration(
function,
when {
// If the original function has signature which need mangling we still need to replace it with a mangled version.
(!function.isFakeOverride || function.findInterfaceImplementation(context.state.jvmDefaultMode) != null) &&
function.signatureRequiresMangling() ->
replacement.name
// Since we remove the corresponding property symbol from the bridge we need to resolve getter/setter
// names at this point.
replacement.isGetter ->
Name.identifier(JvmAbi.getterName(replacement.correspondingPropertySymbol!!.owner.name.asString()))
replacement.isSetter ->
Name.identifier(JvmAbi.setterName(replacement.correspondingPropertySymbol!!.owner.name.asString()))
else ->
function.name
}
)
// Update the overridden symbols to point to their inline class replacements
bridgeFunction.overriddenSymbols = replacement.overriddenSymbols
// Replace the function body with a wrapper
if (bridgeFunction.isFakeOverride && bridgeFunction.parentAsClass.isSingleFieldValueClass) {
// Fake overrides redirect from the replacement to the original function, which is in turn replaced during interfacePhase.
createBridgeBody(replacement, bridgeFunction)
} else {
createBridgeBody(bridgeFunction, replacement)
}
return listOf(replacement, bridgeFunction)
}
private fun IrSimpleFunction.signatureRequiresMangling() =
fullValueParameterList.any { it.type.requiresMangling } ||
context.state.functionsWithInlineClassReturnTypesMangled && returnType.requiresMangling
// We may need to add a bridge method for inline class methods with static replacements. Ideally, we'd do this in BridgeLowering,
// but unfortunately this is a special case in the old backend. The bridge method is not marked as such and does not follow the normal
// visibility rules for bridge methods.
private fun createBridgeDeclaration(source: IrSimpleFunction, mangledName: Name) =
context.irFactory.buildFun {
updateFrom(source)
name = mangledName
@@ -81,59 +139,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
copyAttributes(source)
}
override fun IrClass.isSpecificLoweringLogicApplicable(): Boolean = isSingleFieldValueClass
override val specificMangle: SpecificMangle
get() = SpecificMangle.Inline
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 ->
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.isInlineClassFieldGetter && !it.visibility.isPublicAPI)
}
buildPrimaryInlineClassConstructor(declaration, irConstructor)
buildBoxFunction(declaration)
buildUnboxFunction(declaration)
buildSpecializedEqualsMethod(declaration)
addJvmInlineAnnotation(declaration)
}
fun addJvmInlineAnnotation(valueClass: IrClass) {
if (valueClass.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME)) return
val constructor = context.ir.symbols.jvmInlineAnnotation.constructors.first()
valueClass.annotations = valueClass.annotations + IrConstructorCallImpl.fromSymbolOwner(
constructor.owner.returnType,
constructor
)
}
override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) {
private fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction) {
source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run {
irExprBody(irCall(target).apply {
passTypeArgumentsFrom(source)
@@ -147,7 +153,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
// Secondary constructors for boxed types get translated to static functions returning
// unboxed arguments. We remove the original constructor.
// Primary constructors' case is handled at the start of transformFunctionFlat
override fun transformSecondaryConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
override fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach { it.transformChildrenVoid() }
replacement.body = context.createIrBuilder(replacement.symbol, replacement.startOffset, replacement.endOffset).irBlockBody(
replacement
@@ -205,6 +211,12 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
return listOf(replacement)
}
private fun typedArgumentList(function: IrFunction, expression: IrMemberAccessExpression<*>) =
listOfNotNull(
function.dispatchReceiverParameter?.let { it to expression.dispatchReceiver },
function.extensionReceiverParameter?.let { it to expression.extensionReceiver }
) + function.valueParameters.map { it to expression.getValueArgument(it.index) }
private fun IrMemberAccessExpression<*>.buildReplacement(
originalFunction: IrFunction,
original: IrMemberAccessExpression<*>,
@@ -275,8 +287,6 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
private fun IrExpression.coerceToUnboxed() =
coerceInlineClasses(this, this.type, this.type.unboxInlineClass())
override fun keepOldFunctionInsteadOfNew(function: IrFunction): Boolean = false
// Precondition: left has an inline class type, but may not be unboxed
private fun IrBuilderWithScope.specializeEqualsCall(left: IrExpression, right: IrExpression): IrExpression? {
// There's already special handling for null-comparisons in the Equals intrinsic.
@@ -353,7 +363,6 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
.specializeEqualsCall(expression.getValueArgument(0)!!, expression.getValueArgument(1)!!)
?: expression
}
else ->
super.visitCall(expression)
}
@@ -411,7 +420,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
return super.visitSetValue(expression)
}
fun buildPrimaryInlineClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
override fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
// Add the default primary constructor
valueClass.addConstructor {
updateFrom(irConstructor)
@@ -456,7 +465,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
valueClass.declarations += function
}
fun buildBoxFunction(valueClass: IrClass) {
override fun buildBoxFunction(valueClass: IrClass) {
val function = context.inlineClassReplacements.getBoxFunction(valueClass)
with(context.createIrBuilder(function.symbol)) {
function.body = irExprBody(
@@ -469,6 +478,10 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
valueClass.declarations += function
}
override fun buildUnboxFunctions(valueClass: IrClass) {
buildUnboxFunction(valueClass)
}
private fun buildUnboxFunction(irClass: IrClass) {
val function = context.inlineClassReplacements.getUnboxFunction(irClass)
val field = getInlineClassBackingField(irClass)
@@ -481,7 +494,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
irClass.declarations += function
}
fun buildSpecializedEqualsMethod(valueClass: IrClass) {
override fun buildSpecializedEqualsMethod(valueClass: IrClass) {
val function = context.inlineClassReplacements.getSpecializedEqualsMethod(valueClass, context.irBuiltIns)
val left = function.valueParameters[0]
val right = function.valueParameters[1]
@@ -10,10 +10,10 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.getRequiresMangling
import org.jetbrains.kotlin.backend.jvm.hasMangledReturnType
import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters
import org.jetbrains.kotlin.backend.jvm.ir.needsAccessor
import org.jetbrains.kotlin.backend.jvm.requiresMangling
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -222,7 +222,7 @@ class JvmPropertiesLowering(private val backendContext: JvmBackendContext) : IrE
val getter = property.getter
if (getter != null) {
val needsMangling =
getter.extensionReceiverParameter?.type?.getRequiresMangling(includeInline = true, includeMFVC = false) == true ||
getter.extensionReceiverParameter?.type?.requiresMangling == true ||
(state.functionsWithInlineClassReturnTypesMangled && getter.hasMangledReturnType)
val mangled = if (needsMangling) inlineClassReplacements.getReplacementFunction(getter) else null
defaultMethodSignatureMapper.mapFunctionName(mangled ?: getter)
@@ -8,32 +8,70 @@ package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.InlineClassAbi
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.transformStatement
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendContext) : FileLoweringPass,
IrElementTransformerVoidWithContext() {
abstract val replacements: MemoizedValueClassAbstractReplacements
protected val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>()
private fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
for ((param, newParam) in original.explicitParameters.zip(replacement.explicitParameters)) {
valueMap[param.symbol] = newParam
}
}
final override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid()
}
abstract fun IrClass.isSpecificLoweringLogicApplicable(): Boolean
abstract override fun visitClassNew(declaration: IrClass): IrStatement
abstract fun IrFunction.isSpecificFieldGetter(): Boolean
abstract fun handleSpecificNewClass(declaration: IrClass)
final override fun visitClassNew(declaration: IrClass): IrStatement {
// The arguments to the primary constructor are in scope in the initializers of IrFields.
declaration.primaryConstructor?.let {
replacements.getReplacementFunction(it)?.let { replacement -> addBindingsFor(it, replacement) }
}
declaration.transformDeclarationsFlat { memberDeclaration ->
if (memberDeclaration is IrFunction) {
withinScope(memberDeclaration) {
transformFunctionFlat(memberDeclaration)
}
} else {
memberDeclaration.accept(this, null)
null
}
}
if (declaration.isSpecificLoweringLogicApplicable()) {
val irConstructor = declaration.primaryConstructor!!
// The field getter is used by reflection and cannot be removed here unless it is internal.
declaration.declarations.removeIf {
it == irConstructor || (it is IrFunction && it.isSpecificFieldGetter() && !it.visibility.isPublicAPI)
}
buildPrimaryValueClassConstructor(declaration, irConstructor)
buildBoxFunction(declaration)
buildUnboxFunctions(declaration)
buildSpecializedEqualsMethod(declaration)
addJvmInlineAnnotation(declaration)
}
return declaration
}
protected fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
if (function is IrConstructor && function.isPrimary && function.constructedClass.isSpecificLoweringLogicApplicable()) {
@@ -41,21 +79,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
}
val replacement = replacements.getReplacementFunction(function)
if (keepOldFunctionInsteadOfNew(function)) {
function.transformChildrenVoid()
addBindingsFor(function, replacement!!)
return null
}
if (replacement == null) {
if (function is IrConstructor) {
val constructorReplacement = replacements.getReplacementForRegularClassConstructor(function)
if (constructorReplacement != null) {
addBindingsFor(function, constructorReplacement)
return transformFlattenedConstructor(function, constructorReplacement)
}
}
function.transformChildrenVoid()
// Non-mangled functions can override mangled functions under some conditions, e.g., a function
// `fun f(): Nothing` can override a function `fun f(): UInt`. The former is not mangled, while
@@ -82,50 +106,30 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
addBindingsFor(function, replacement)
return when (function) {
is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement)
is IrConstructor -> transformSecondaryConstructorFlat(function, replacement)
is IrConstructor -> transformConstructorFlat(function, replacement)
else -> throw IllegalStateException()
}
}
private fun transformFlattenedConstructor(function: IrConstructor, replacement: IrConstructor): List<IrDeclaration>? {
replacement.valueParameters.forEach {
it.transformChildrenVoid()
it.defaultValue?.patchDeclarationParents(replacement)
}
allScopes.push(createScope(function))
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
allScopes.pop()
return listOf(replacement)
}
private fun IrFunction.hashSuffix(): String? = InlineClassAbi.hashSuffix(
this,
context.state.functionsWithInlineClassReturnTypesMangled,
context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures
)
protected abstract fun transformSecondaryConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration>
protected abstract fun transformConstructorFlat(constructor: IrConstructor, 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(replacement))
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
allScopes.pop()
replacement.copyAttributes(function)
protected abstract fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration>
// 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 buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor)
val bridgeFunction = createBridgeFunction(function, replacement)
protected abstract fun buildBoxFunction(valueClass: IrClass)
return listOf(replacement, bridgeFunction)
}
protected abstract fun buildUnboxFunctions(valueClass: IrClass)
abstract fun keepOldFunctionInsteadOfNew(function: IrFunction): Boolean
protected abstract fun buildSpecializedEqualsMethod(valueClass: IrClass) // todo hashCode
protected abstract fun addJvmInlineAnnotation(valueClass: IrClass)
final override fun visitReturn(expression: IrReturn): IrExpression {
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
@@ -133,10 +137,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
if (suffix != null && target.name.asString().endsWith(suffix))
return super.visitReturn(expression)
replacements.run {
if (keepOldFunctionInsteadOfNew(target)) return@run null
getReplacementFunction(target) ?: if (target is IrConstructor) getReplacementForRegularClassConstructor(target) else null
}?.let {
replacements.getReplacementFunction(target)?.let {
return context.createIrBuilder(it.symbol, expression.startOffset, expression.endOffset).irReturn(
expression.value.transform(this, null)
)
@@ -171,65 +172,4 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
else
super.visitAnonymousInitializerNew(declaration)
protected abstract fun addBindingsFor(original: IrFunction, replacement: IrFunction)
protected enum class SpecificMangle { Inline, MultiField }
protected abstract val specificMangle: SpecificMangle
private fun createBridgeFunction(
function: IrSimpleFunction,
replacement: IrSimpleFunction
): IrSimpleFunction {
val bridgeFunction = createBridgeDeclaration(
function,
replacement,
when {
// If the original function has signature which need mangling we still need to replace it with a mangled version.
(!function.isFakeOverride || function.findInterfaceImplementation(context.state.jvmDefaultMode) != null) && when (specificMangle) {
SpecificMangle.Inline -> function.signatureRequiresMangling(includeInline = true, includeMFVC = false)
SpecificMangle.MultiField -> function.signatureRequiresMangling(includeInline = false, includeMFVC = true)
} -> replacement.name
// Since we remove the corresponding property symbol from the bridge we need to resolve getter/setter
// names at this point.
replacement.isGetter ->
Name.identifier(JvmAbi.getterName(replacement.correspondingPropertySymbol!!.owner.name.asString()))
replacement.isSetter ->
Name.identifier(JvmAbi.setterName(replacement.correspondingPropertySymbol!!.owner.name.asString()))
else ->
function.name
}
)
// Update the overridden symbols to point to their inline class replacements
bridgeFunction.overriddenSymbols = replacement.overriddenSymbols
// Replace the function body with a wrapper
if (bridgeFunction.isFakeOverride && bridgeFunction.parentAsClass.isSpecificLoweringLogicApplicable()) {
// Fake overrides redirect from the replacement to the original function, which is in turn replaced during interfacePhase.
createBridgeBody(replacement, bridgeFunction, function, true)
} else {
createBridgeBody(bridgeFunction, replacement, function, false)
}
return bridgeFunction
}
private fun IrSimpleFunction.signatureRequiresMangling(includeInline: Boolean = true, includeMFVC: Boolean = true) =
fullValueParameterList.any { it.type.getRequiresMangling(includeInline, includeMFVC) } ||
context.state.functionsWithInlineClassReturnTypesMangled &&
returnType.getRequiresMangling(includeInline = includeInline, includeMFVC = false)
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) }
// We may need to add a bridge method for inline class methods with static replacements. Ideally, we'd do this in BridgeLowering,
// but unfortunately this is a special case in the old backend. The bridge method is not marked as such and does not follow the normal
// visibility rules for bridge methods.
abstract fun createBridgeDeclaration(source: IrSimpleFunction, replacement: IrSimpleFunction, mangledName: Name): IrSimpleFunction
protected abstract fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean)
}
@@ -370,7 +370,7 @@ private class SyntheticAccessorTransformer(
val constructedClass = constructedClass
if (!DescriptorVisibilities.isPrivate(visibility) && !constructedClass.isValue && hasMangledParameters() &&
if (!DescriptorVisibilities.isPrivate(visibility) && !constructedClass.isSingleFieldValueClass && hasMangledParameters &&
!constructedClass.isAnonymousObject
) return true
@@ -60,14 +60,14 @@ object InlineClassAbi {
fun mangledNameFor(irFunction: IrFunction, mangleReturnTypes: Boolean, useOldMangleRules: Boolean): Name {
if (irFunction is IrConstructor) {
// Note that we might drop this convention and use standard mangling for constructors too, see KT-37186.
assert(irFunction.constructedClass.isValue) {
assert(irFunction.constructedClass.isSingleFieldValueClass) {
"Should not mangle names of non-inline class constructors: ${irFunction.render()}"
}
return Name.identifier("constructor-impl")
}
val suffix = hashSuffix(irFunction, mangleReturnTypes, useOldMangleRules)
if (suffix == null && ((irFunction.parent as? IrClass)?.isValue != true || irFunction.origin == IrDeclarationOrigin.IR_BUILTINS_STUB)) {
if (suffix == null && ((irFunction.parent as? IrClass)?.isSingleFieldValueClass != true || irFunction.origin == IrDeclarationOrigin.IR_BUILTINS_STUB)) {
return irFunction.name
}
@@ -101,7 +101,7 @@ object InlineClassAbi {
): String? =
collectFunctionSignatureForManglingSuffix(
useOldMangleRules,
valueParameters.any { it.getRequiresMangling() },
valueParameters.any { it.requiresMangling },
// The JVM backend computes mangled names after creating suspend function views, but before default argument
// stub insertion. It would be nice if this part of the continuation lowering happened earlier in the pipeline.
// TODO: Move suspend function view creation before JvmInlineClassLowering.
@@ -124,28 +124,19 @@ object InlineClassAbi {
get() = (this as IrSimpleFunction).correspondingPropertySymbol!!.owner.name
}
fun IrType.getRequiresMangling(includeInline: Boolean = true, includeMFVC: Boolean = true): Boolean {
val irClass = erasedUpperBound
return irClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME && when {
irClass.isSingleFieldValueClass -> includeInline
irClass.isMultiFieldValueClass -> includeMFVC
else -> false
val IrType.requiresMangling: Boolean
get() {
val irClass = erasedUpperBound
return irClass.isSingleFieldValueClass && irClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME
}
}
val IrFunction.fullValueParameterList: List<IrValueParameter>
get() = listOfNotNull(extensionReceiverParameter) + valueParameters
fun IrFunction.hasMangledParameters(includeInline: Boolean = true, includeMFVC: Boolean = true): Boolean =
(dispatchReceiverParameter != null && when {
parentAsClass.isSingleFieldValueClass -> includeInline
parentAsClass.isMultiFieldValueClass -> includeMFVC
else -> false
}) || fullValueParameterList.any { it.type.getRequiresMangling(includeInline, includeMFVC) } || (this is IrConstructor && when {
constructedClass.isSingleFieldValueClass -> includeInline
constructedClass.isMultiFieldValueClass -> includeMFVC
else -> false
})
val IrFunction.hasMangledParameters: Boolean
get() = dispatchReceiverParameter != null && parentAsClass.isSingleFieldValueClass ||
fullValueParameterList.any { it.type.requiresMangling } ||
(this is IrConstructor && constructedClass.isSingleFieldValueClass)
val IrFunction.hasMangledReturnType: Boolean
get() = returnType.isInlineClassType() && parentClassOrNull?.isFileClass != true
@@ -155,12 +146,10 @@ val IrClass.inlineClassFieldName: Name
val IrFunction.isInlineClassFieldGetter: Boolean
get() = (parent as? IrClass)?.isSingleFieldValueClass == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
contextReceiverParametersCount == 0 && !isStatic &&
correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.name == parentAsClass.inlineClassFieldName } == true
val IrFunction.isMultiFieldValueClassFieldGetter: Boolean
get() = (parent as? IrClass)?.isMultiFieldValueClass == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
contextReceiverParametersCount == 0 && !isStatic &&
correspondingPropertySymbol?.let {
val multiFieldValueClassRepresentation = parentAsClass.multiFieldValueClassRepresentation
?: error("Multi-field value class must have multiFieldValueClassRepresentation: ${parentAsClass.render()}")
@@ -143,8 +143,7 @@ class JvmBackendContext(
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
val multiFieldValueClassReplacements =
MemoizedMultiFieldValueClassReplacements(irFactory, this)
val multiFieldValueClassReplacements = MemoizedMultiFieldValueClassReplacements(irFactory, this)
val continuationClassesVarsCountByType: MutableMap<IrAttributeContainer, Map<Type, Int>> = hashMapOf()
@@ -198,20 +197,6 @@ class JvmBackendContext(
multifileFacade.setValue(newPartClasses.toMutableList())
}
for ((staticReplacement, original) in multiFieldValueClassReplacements.originalFunctionForStaticReplacement) {
if (staticReplacement !is IrSimpleFunction) continue
val newOriginal = functionSymbolMap[original.symbol]?.owner ?: continue
val newStaticReplacement = multiFieldValueClassReplacements.getReplacementFunction(newOriginal) ?: continue
functionSymbolMap[staticReplacement.symbol] = newStaticReplacement.symbol
}
for ((methodReplacement, original) in multiFieldValueClassReplacements.originalFunctionForMethodReplacement) {
if (methodReplacement !is IrSimpleFunction) continue
val newOriginal = functionSymbolMap[original.symbol]?.owner ?: continue
val newMethodReplacement = multiFieldValueClassReplacements.getReplacementFunction(newOriginal) ?: continue
functionSymbolMap[methodReplacement.symbol] = newMethodReplacement.symbol
}
for ((staticReplacement, original) in inlineClassReplacements.originalFunctionForStaticReplacement) {
if (staticReplacement !is IrSimpleFunction) continue
val newOriginal = functionSymbolMap[original.symbol]?.owner ?: continue
@@ -251,8 +251,7 @@ class JvmCachedDeclarations(
defaultImplsRedirections.getOrPut(fakeOverride) {
assert(fakeOverride.isFakeOverride)
val irClass = fakeOverride.parentAsClass
val mfvcReplacementStructure = context.multiFieldValueClassReplacements.bindingNewFunctionToParameterTemplateStructure
val redirectFunction = context.irFactory.buildFun {
context.irFactory.buildFun {
origin = JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE
name = fakeOverride.name
visibility = fakeOverride.visibility
@@ -276,8 +275,6 @@ class JvmCachedDeclarations(
annotations = fakeOverride.annotations
copyCorrespondingPropertyFrom(fakeOverride)
}
mfvcReplacementStructure[fakeOverride]?.let { mfvcReplacementStructure[redirectFunction] = it }
redirectFunction
}
fun getRepeatedAnnotationSyntheticContainer(annotationClass: IrClass): IrClass =
@@ -29,14 +29,9 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object ENUM_MAPPINGS_FOR_WHEN : IrDeclarationOriginImpl("ENUM_MAPPINGS_FOR_WHEN", isSynthetic = true)
object ENUM_MAPPINGS_FOR_ENTRIES : IrDeclarationOriginImpl("ENUM_MAPPINGS_FOR_ENTRIES", isSynthetic = true)
object SYNTHETIC_INLINE_CLASS_MEMBER : IrDeclarationOriginImpl("SYNTHETIC_INLINE_CLASS_MEMBER", isSynthetic = true)
object SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER :
IrDeclarationOriginImpl("SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER", isSynthetic = true)
object INLINE_CLASS_GENERATED_IMPL_METHOD : IrDeclarationOriginImpl("INLINE_CLASS_GENERATED_IMPL_METHOD")
object MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD : IrDeclarationOriginImpl("MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD")
object STATIC_INLINE_CLASS_REPLACEMENT : IrDeclarationOriginImpl("STATIC_INLINE_CLASS_REPLACEMENT")
object STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT : IrDeclarationOriginImpl("STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT")
object STATIC_INLINE_CLASS_CONSTRUCTOR : IrDeclarationOriginImpl("STATIC_INLINE_CLASS_CONSTRUCTOR")
object STATIC_MULTI_FIELD_VALUE_CLASS_CONSTRUCTOR : IrDeclarationOriginImpl("STATIC_MULTI_FIELD_VALUE_CLASS_CONSTRUCTOR")
object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true)
object GENERATED_EXTENDED_MAIN : IrDeclarationOriginImpl("GENERATED_EXTENDED_MAIN", isSynthetic = true)
object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true)
@@ -5,26 +5,25 @@
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.jvm.ir.classFileContainsMethod
import org.jetbrains.kotlin.backend.jvm.ir.extensionReceiverName
import org.jetbrains.kotlin.backend.jvm.ir.isStaticValueClassReplacement
import org.jetbrains.kotlin.backend.jvm.ir.parentClassId
import org.jetbrains.kotlin.backend.jvm.ir.*
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.types.isInt
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.alwaysNull
import java.util.concurrent.ConcurrentHashMap
/**
@@ -36,9 +35,10 @@ class MemoizedInlineClassReplacements(
context: JvmBackendContext
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
private val storageManager = LockBasedStorageManager("inline-class-replacements")
private val propertyMap = ConcurrentHashMap<IrPropertySymbol, IrProperty>()
val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
override val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
/**
* Get a replacement for a function or a constructor.
@@ -49,9 +49,7 @@ class MemoizedInlineClassReplacements(
// Don't mangle anonymous or synthetic functions, except for generated SAM wrapper methods
(it.isLocal && it is IrSimpleFunction && it.overriddenSymbols.isEmpty()) ||
(it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR && it.visibility == DescriptorVisibilities.LOCAL) ||
it.isStaticValueClassReplacement ||
it in context.multiFieldValueClassReplacements.bindingNewFunctionToParameterTemplateStructure ||
it.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
it.isStaticInlineClassReplacement ||
it.origin.isSynthetic && it.origin != IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION ->
null
@@ -66,7 +64,7 @@ class MemoizedInlineClassReplacements(
when {
it.isRemoveAtSpecialBuiltinStub() ->
null
it.isValueClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
it.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB ->
createMethodReplacement(it)
else ->
@@ -74,8 +72,7 @@ class MemoizedInlineClassReplacements(
}
// Otherwise, mangle functions with mangled parameters, ignoring constructors
it is IrSimpleFunction && !it.isFromJava() &&
(it.hasMangledParameters(includeMFVC = false) || mangleReturnTypes && it.hasMangledReturnType) ->
it is IrSimpleFunction && !it.isFromJava() && (it.hasMangledParameters || mangleReturnTypes && it.hasMangledReturnType) ->
createMethodReplacement(it)
else ->
@@ -83,6 +80,29 @@ class MemoizedInlineClassReplacements(
}
}
private fun IrFunction.isRemoveAtSpecialBuiltinStub() =
origin == IrDeclarationOrigin.IR_BUILTINS_STUB &&
name.asString() == "remove" &&
valueParameters.size == 1 &&
valueParameters[0].type.isInt()
private fun IrFunction.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod(): Boolean {
if (this !is IrSimpleFunction) return false
if (!this.isFakeOverride) return false
val parentClass = parentClassOrNull ?: return false
if (!parentClass.isSingleFieldValueClass) return false
val overridden = resolveFakeOverride() ?: return false
if (!overridden.parentAsClass.isJvmInterface) return false
if (overridden.modality == Modality.ABSTRACT) return false
// We have a non-abstract interface member.
// It is a JVM default interface method if one of the following conditions are true:
// - it is a Java method,
// - it is a Kotlin function compiled to JVM default interface method.
return overridden.isFromJava() || overridden.isCompiledToJvmDefault(context.state.jvmDefaultMode)
}
/**
* Get the box function for an inline class. Concretely, this is a synthetic
* static function named "box-impl" which takes an unboxed value and returns
@@ -128,7 +148,7 @@ class MemoizedInlineClassReplacements(
return specializedEqualsCache.computeIfAbsent(irClass) {
irFactory.buildFun {
name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
// TODO: Revisit this once we allow user defined equals methods in inline/multi-field value classes.
// TODO: Revisit this once we allow user defined equals methods in inline classes.
origin = JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
returnType = irBuiltIns.booleanType
}.apply {
@@ -148,7 +168,7 @@ class MemoizedInlineClassReplacements(
}
}
override fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
private fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, function.origin) {
originalFunctionForMethodReplacement[this] = function
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this, index = -1)
@@ -164,14 +184,9 @@ class MemoizedInlineClassReplacements(
it.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
}
}
context.multiFieldValueClassReplacements.run {
bindingNewFunctionToParameterTemplateStructure[function]?.also {
bindingNewFunctionToParameterTemplateStructure[this@buildReplacement] = it
}
}
}
override fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
private fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT, noFakeOverride = true) {
originalFunctionForStaticReplacement[this] = function
@@ -204,11 +219,6 @@ class MemoizedInlineClassReplacements(
}
}
valueParameters = newValueParameters
context.multiFieldValueClassReplacements.run {
bindingNewFunctionToParameterTemplateStructure[function]?.also {
bindingNewFunctionToParameterTemplateStructure[this@buildReplacement] = it
}
}
}
private fun buildReplacement(
@@ -236,7 +246,8 @@ class MemoizedInlineClassReplacements(
noFakeOverride: Boolean,
useOldManglingScheme: Boolean,
body: IrFunction.() -> Unit,
): IrSimpleFunction = commonBuildReplacementInner(function, noFakeOverride, body) {
): IrSimpleFunction = irFactory.buildFun {
updateFrom(function)
if (function is IrConstructor) {
// The [updateFrom] call will set the modality to FINAL for constructors, while the JVM backend would use OPEN here.
modality = Modality.OPEN
@@ -249,10 +260,52 @@ class MemoizedInlineClassReplacements(
else ->
replacementOrigin
}
if (noFakeOverride) {
isFakeOverride = false
}
name = InlineClassAbi.mangledNameFor(function, mangleReturnTypes, useOldManglingScheme)
}
returnType = function.returnType
}.apply {
parent = function.parent
annotations = function.annotations
copyTypeParameters(function.allTypeParameters)
if (function.metadata != null) {
metadata = function.metadata
function.metadata = null
}
copyAttributes(function as? IrAttributeContainer)
override val getReplacementForRegularClassConstructor: (IrConstructor) -> IrConstructor? = alwaysNull()
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 replaceOverriddenSymbols: (IrSimpleFunction) -> List<IrSimpleFunctionSymbol> =
storageManager.createMemoizedFunction { irSimpleFunction ->
@@ -260,4 +313,9 @@ class MemoizedInlineClassReplacements(
computeOverrideReplacement(it.owner).symbol
}
}
private fun computeOverrideReplacement(function: IrSimpleFunction): IrSimpleFunction =
getReplacementFunction(function) ?: function.also {
function.overriddenSymbols = replaceOverriddenSymbols(function)
}
}
@@ -5,24 +5,11 @@
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.MultiFieldValueClassMapping
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.ir.extensionReceiverName
import org.jetbrains.kotlin.backend.jvm.ir.findSuperDeclaration
import org.jetbrains.kotlin.backend.jvm.ir.isStaticValueClassReplacement
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.concurrent.ConcurrentHashMap
/**
@@ -34,281 +21,17 @@ class MemoizedMultiFieldValueClassReplacements(
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
private val storageManager = LockBasedStorageManager("multi-field-value-class-replacements")
val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
private val originalConstructorForConstructorReplacement: MutableMap<IrConstructor, IrConstructor> = ConcurrentHashMap()
private fun IrValueParameter.grouped(
name: String?,
substitutionMap: Map<IrTypeParameterSymbol, IrType>,
targetFunction: IrFunction,
originWhenFlattened: IrDeclarationOrigin,
): RemappedParameter {
if (!type.needsMfvcFlattening()) return RemappedParameter.RegularMapping(
targetFunction.addValueParameter {
updateFrom(this@grouped)
this.name = this@grouped.name
index = targetFunction.valueParameters.size
}.apply {
copyAnnotationsFrom(this@grouped)
}
)
val rootMfvcNode = this@MemoizedMultiFieldValueClassReplacements.getRootMfvcNode(type.erasedUpperBound)!!
require(!hasDefaultValue()) { "Default parameters values are not supported for multi-field value classes" }
val newType = type.substitute(substitutionMap) as IrSimpleType
val localSubstitutionMap = makeTypeArgumentsFromType(newType)
val valueParameters = rootMfvcNode.mapLeaves { leaf ->
targetFunction.addValueParameter {
updateFrom(this@grouped)
this.name = Name.identifier("${name ?: this@grouped.name}-${leaf.fullFieldName}")
type = leaf.type.substitute(localSubstitutionMap)
origin = originWhenFlattened
index = targetFunction.valueParameters.size
}.apply {
defaultValue = null
copyAnnotationsFrom(this@grouped)
}
}
return MultiFieldValueClassMapping(rootMfvcNode, newType, valueParameters)
}
private fun List<IrValueParameter>.grouped(
name: String?,
substitutionMap: Map<IrTypeParameterSymbol, IrType>,
targetFunction: IrFunction,
originWhenFlattened: IrDeclarationOrigin,
): List<RemappedParameter> = map { it.grouped(name, substitutionMap, targetFunction, originWhenFlattened) }
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 makeAndAddGroupedValueParametersFrom(
sourceFunction: IrFunction,
includeDispatcherReceiver: Boolean,
substitutionMap: Map<IrTypeParameterSymbol, IrType>,
targetFunction: IrFunction,
): List<RemappedParameter> {
val newFlattenedParameters = mutableListOf<RemappedParameter>()
if (sourceFunction.dispatchReceiverParameter != null && includeDispatcherReceiver) {
newFlattenedParameters.add(
sourceFunction.parentAsClass.thisReceiver!!.grouped(
"\$dispatchReceiver",
substitutionMap,
targetFunction,
IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER,
)
)
}
val contextReceivers = sourceFunction.valueParameters.take(sourceFunction.contextReceiverParametersCount)
.mapIndexed { index: Int, valueParameter: IrValueParameter ->
valueParameter.grouped(
"contextReceiver$index",
substitutionMap,
targetFunction,
IrDeclarationOrigin.MOVED_CONTEXT_RECEIVER,
)
}
newFlattenedParameters.addAll(contextReceivers)
sourceFunction.extensionReceiverParameter?.let {
val newParameters = it.grouped(
sourceFunction.extensionReceiverName(context.state),
substitutionMap,
targetFunction,
IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER,
)
newFlattenedParameters.add(newParameters)
}
newFlattenedParameters += sourceFunction.valueParameters.drop(sourceFunction.contextReceiverParametersCount)
.grouped(name = null, substitutionMap, targetFunction, IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER)
return newFlattenedParameters
}
sealed class RemappedParameter {
abstract val valueParameters: List<IrValueParameter>
data class RegularMapping(val valueParameter: IrValueParameter) : RemappedParameter() {
override val valueParameters: List<IrValueParameter> = listOf(valueParameter)
}
data class MultiFieldValueClassMapping(
val rootMfvcNode: RootMfvcNode,
val typeArguments: TypeArguments,
override val valueParameters: List<IrValueParameter>,
) : RemappedParameter() {
init {
require(valueParameters.size > 1) { "MFVC must have > 1 fields" }
}
constructor(rootMfvcNode: RootMfvcNode, type: IrSimpleType, valueParameters: List<IrValueParameter>) :
this(rootMfvcNode, makeTypeArgumentsFromType(type), valueParameters)
val boxedType: IrSimpleType = rootMfvcNode.type.substitute(typeArguments) as IrSimpleType
}
}
val bindingOldFunctionToParameterTemplateStructure: MutableMap<IrFunction, List<RemappedParameter>> = ConcurrentHashMap()
val bindingNewFunctionToParameterTemplateStructure: MutableMap<IrFunction, List<RemappedParameter>> =
object : ConcurrentHashMap<IrFunction, List<RemappedParameter>>() {
override fun put(key: IrFunction, value: List<RemappedParameter>): List<RemappedParameter>? {
require(key.explicitParametersCount == value.sumOf { it.valueParameters.size }) {
"Illegal structure $value for function ${key.dump()}"
}
return super.put(key, value)
}
}
override fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT, noFakeOverride = true) {
originalFunctionForStaticReplacement[this] = function
typeParameters = listOf()
copyTypeParametersFrom(function.parentAsClass)
val substitutionMap = function.parentAsClass.typeParameters.map { it.symbol }.zip(typeParameters.map { it.defaultType }).toMap()
copyTypeParametersFrom(function, parameterMap = (function.parentAsClass.typeParameters zip typeParameters).toMap())
val newFlattenedParameters =
makeAndAddGroupedValueParametersFrom(function, includeDispatcherReceiver = true, substitutionMap, this)
bindingOldFunctionToParameterTemplateStructure[function] = newFlattenedParameters
bindingNewFunctionToParameterTemplateStructure[this] = newFlattenedParameters
}
override fun createMethodReplacement(function: IrFunction): IrSimpleFunction = buildReplacement(function, function.origin) {
originalFunctionForMethodReplacement[this] = function
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this, index = -1)
val newFlattenedParameters = makeAndAddGroupedValueParametersFrom(function, includeDispatcherReceiver = false, mapOf(), this)
val receiver = dispatchReceiverParameter
val remappedParameters =
if (receiver != null) listOf(RemappedParameter.RegularMapping(receiver)) + newFlattenedParameters else newFlattenedParameters
bindingOldFunctionToParameterTemplateStructure[function] = remappedParameters
bindingNewFunctionToParameterTemplateStructure[this] = remappedParameters
}
private fun createConstructorReplacement(constructor: IrConstructor): IrConstructor {
val newConstructor = irFactory.buildConstructor {
updateFrom(constructor)
returnType = constructor.returnType
}.apply {
val newFlattenedParameters = makeAndAddGroupedValueParametersFrom(constructor, includeDispatcherReceiver = false, mapOf(), this)
bindingOldFunctionToParameterTemplateStructure[constructor] = newFlattenedParameters
parent = constructor.parent
copyTypeParametersFrom(constructor)
annotations = constructor.annotations
originalConstructorForConstructorReplacement[this] = constructor
bindingNewFunctionToParameterTemplateStructure[this] = newFlattenedParameters
}
return newConstructor
}
override val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
/**
* Get a function replacement for a function or a constructor.
* Get a replacement for a function or a constructor.
*/
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
storageManager.createMemoizedFunctionWithNullableValues { function ->
when {
(function.isLocal && function is IrSimpleFunction && function.overriddenSymbols.isEmpty()) ||
(function.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR && function.visibility == DescriptorVisibilities.LOCAL) ||
function.isStaticValueClassReplacement ||
function.origin == IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER && function.isAccessor ||
function.origin == JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD ||
function.origin.isSynthetic && function.origin != IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION -> null
// Do not check for overridden symbols because it makes previously overriding function not overriding would break a code.
function.isMultiFieldValueClassFieldGetter -> makeMultiFieldValueClassFieldGetterReplacement(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.needsMfvcFlattening() } &&
(!function.isFakeOverride ||
findSuperDeclaration(function, false, context.state.jvmDefaultMode)
in bindingOldFunctionToParameterTemplateStructure) ->
createMethodReplacement(function)
else -> null
}
}
private fun makeMultiFieldValueClassFieldGetterReplacement(function: IrFunction): IrSimpleFunction {
require(function is IrSimpleFunction && function.isMultiFieldValueClassFieldGetter) { "Illegal function:\n${function.dump()}" }
val replacement = getMfvcPropertyNode(function.correspondingPropertySymbol!!.owner)!!.unboxMethod
originalFunctionForMethodReplacement[replacement] = function
val templateParameters = listOf(RemappedParameter.RegularMapping(replacement.dispatchReceiverParameter!!))
bindingNewFunctionToParameterTemplateStructure[replacement] = templateParameters
bindingOldFunctionToParameterTemplateStructure[function] = templateParameters
return replacement
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? = storageManager.createMemoizedFunctionWithNullableValues {
TODO()
}
override val getReplacementForRegularClassConstructor: (IrConstructor) -> IrConstructor? =
storageManager.createMemoizedFunctionWithNullableValues { constructor ->
when {
constructor.constructedClass.isMultiFieldValueClass -> null
constructor.isFromJava() -> null
constructor.fullValueParameterList.any { it.type.needsMfvcFlattening() } ->
createConstructorReplacement(constructor)
else -> null
}
}
override val replaceOverriddenSymbols: (IrSimpleFunction) -> List<IrSimpleFunctionSymbol> =
storageManager.createMemoizedFunction { irSimpleFunction ->
irSimpleFunction.overriddenSymbols.map {
computeOverrideReplacement(it.owner).symbol
}
}
val getRootMfvcNode: (IrClass) -> RootMfvcNode? = storageManager.createMemoizedFunctionWithNullableValues {
if (it.defaultType.needsMfvcFlattening()) getRootNode(context, it) else null
override val replaceOverriddenSymbols: (IrSimpleFunction) -> List<IrSimpleFunctionSymbol> = storageManager.createMemoizedFunction {
TODO()
}
val getRegularClassMfvcPropertyNode: (IrProperty) -> IntermediateMfvcNode? =
storageManager.createMemoizedFunctionWithNullableValues { property: IrProperty ->
val parent = property.parent
when {
parent !is IrClass -> null
property.isFakeOverride -> null
property.getter.let { it != null && (it.contextReceiverParametersCount > 0 || it.extensionReceiverParameter != null) } -> null
useRootNode(parent, property) -> null
property.run { backingField?.type ?: getter?.returnType }?.needsMfvcFlattening() != true -> null
else -> createIntermediateNodeForMfvcPropertyOfRegularClass(parent, context, property)
}
}
fun getMfvcPropertyNode(property: IrProperty): NameableMfvcNode? {
val parent = property.parent
return when {
parent !is IrClass -> null
useRootNode(parent, property) -> getRootMfvcNode(parent)!![property.name]
else -> getRegularClassMfvcPropertyNode(property)
}
}
private fun useRootNode(
parent: IrClass,
property: IrProperty
) = parent.isMultiFieldValueClass && (property.getter?.isStatic ?: property.backingField?.isStatic) == false
}
@@ -5,113 +5,18 @@
package org.jetbrains.kotlin.backend.jvm
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.declarations.IrFactory
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.types.isInt
import org.jetbrains.kotlin.ir.util.*
import java.util.concurrent.ConcurrentHashMap
abstract class MemoizedValueClassAbstractReplacements(protected val irFactory: IrFactory, protected val context: JvmBackendContext) {
private val propertyMap = ConcurrentHashMap<IrPropertySymbol, IrProperty>()
/**
* Get a replacement for a function or a constructor.
*/
abstract val getReplacementFunction: (IrFunction) -> IrSimpleFunction?
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 = replaceOverriddenSymbols(function)
}
body()
}
abstract val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction>
abstract val replaceOverriddenSymbols: (IrSimpleFunction) -> List<IrSimpleFunctionSymbol>
abstract val getReplacementForRegularClassConstructor: (IrConstructor) -> IrConstructor?
protected fun computeOverrideReplacement(function: IrSimpleFunction): IrSimpleFunction =
getReplacementFunction(function) ?: function.also {
function.overriddenSymbols = replaceOverriddenSymbols(function)
}
}
@@ -1,457 +0,0 @@
/*
* 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.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.ir.isMultiFieldValueClassType
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.ir.builders.IrBlockBuilder
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetField
import org.jetbrains.kotlin.ir.expressions.IrReturn
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
typealias TypeArguments = Map<IrTypeParameterSymbol, IrType>
sealed interface MfvcNode {
val type: IrType
val leavesCount: Int
fun createInstanceFromBox(
scope: IrBlockBuilder,
typeArguments: TypeArguments,
receiver: IrExpression?,
accessType: AccessType,
saveVariable: (IrVariable) -> Unit,
): ReceiverBasedMfvcNodeInstance
}
fun MfvcNode.createInstanceFromBox(
scope: IrBlockBuilder,
receiver: IrExpression,
accessType: AccessType,
saveVariable: (IrVariable) -> Unit
) =
createInstanceFromBox(scope, makeTypeArgumentsFromType(receiver.type as IrSimpleType), receiver, accessType, saveVariable)
fun MfvcNode.createInstanceFromValueDeclarationsAndBoxType(
scope: IrBuilderWithScope, type: IrSimpleType, name: Name, saveVariable: (IrVariable) -> Unit,
): ValueDeclarationMfvcNodeInstance = createInstanceFromValueDeclarations(scope, makeTypeArgumentsFromType(type), name, saveVariable)
fun MfvcNode.createInstanceFromValueDeclarations(
scope: IrBuilderWithScope, typeArguments: TypeArguments, name: Name, saveVariable: (IrVariable) -> Unit,
): ValueDeclarationMfvcNodeInstance {
val valueDeclarations = mapLeaves {
scope.savableStandaloneVariable(
type = it.type,
name = listOf(name, it.fullFieldName).joinToString("-"),
origin = IrDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE,
saveVariable = saveVariable
)
}
return ValueDeclarationMfvcNodeInstance(this, typeArguments, valueDeclarations)
}
fun MfvcNode.createInstanceFromValueDeclarationsAndBoxType(
type: IrSimpleType, fieldValues: List<IrValueDeclaration>
): ValueDeclarationMfvcNodeInstance =
ValueDeclarationMfvcNodeInstance(this, makeTypeArgumentsFromType(type), fieldValues)
fun makeTypeArgumentsFromType(type: IrSimpleType): TypeArguments {
if (type.classifierOrNull !is IrClassSymbol) return mapOf()
val parameters = type.erasedUpperBound.typeParameters
val arguments = type.arguments
require(parameters.size == arguments.size) {
"Number of type parameters (${parameters.joinToString { it.render() }}) is not equal to number of type arguments (${arguments.joinToString { it.render() }})."
}
return parameters.zip(arguments) { parameter, argument -> parameter.symbol to (argument.typeOrNull ?: parameter.defaultType) }.toMap()
}
sealed interface NameableMfvcNode : MfvcNode {
val namedNodeImpl: NameableMfvcNodeImpl
}
val NameableMfvcNode.nameParts: List<IndexedNamePart>
get() = namedNodeImpl.nameParts
val NameableMfvcNode.name: Name
get() = nameParts.last().name
val NameableMfvcNode.index: Int
get() = nameParts.last().index
val NameableMfvcNode.unboxMethod: IrSimpleFunction
get() = namedNodeImpl.unboxMethod
val NameableMfvcNode.fullMethodName: Name
get() = namedNodeImpl.fullMethodName
val NameableMfvcNode.fullFieldName: Name
get() = namedNodeImpl.fullFieldName
val NameableMfvcNode.hasPureUnboxMethod: Boolean
get() = namedNodeImpl.hasPureUnboxMethod
data class IndexedNamePart(val index: Int, val name: Name)
class NameableMfvcNodeImpl(
rootPropertyName: String?,
val nameParts: List<IndexedNamePart>,
val unboxMethod: IrSimpleFunction,
val hasPureUnboxMethod: Boolean,
) {
val fullMethodName = makeFullMethodName(rootPropertyName, nameParts)
val fullFieldName = makeFullFieldName(rootPropertyName, nameParts)
companion object {
@JvmStatic
fun makeFullMethodName(rootPropertyName: String?, nameParts: List<IndexedNamePart>): Name = if (rootPropertyName == null) {
val restJoined = nameParts.joinToString("-") { it.index.toString() }
Name.identifier("${KotlinTypeMapper.UNBOX_JVM_METHOD_NAME}-$restJoined")
} else {
val wholeName = (listOf(JvmAbi.getterName(rootPropertyName)) + nameParts.map { it.index.toString() }).joinToString("-")
Name.identifier(wholeName)
}
@JvmStatic
fun makeFullFieldName(rootPropertyName: String?, nameParts: List<IndexedNamePart>): Name {
val joined = (listOf(rootPropertyName ?: "field") + nameParts.map { it.index.toString() }).joinToString("-")
return Name.identifier(joined)
}
}
}
fun MfvcNode.getSubnodeAndIndices(name: Name): Pair<NameableMfvcNode, IntRange>? {
val node = (this as? MfvcNodeWithSubnodes)?.get(name) ?: return null
val indices = subnodeIndices[node] ?: error("existing node without indices")
return node to indices
}
sealed interface MfvcNodeWithSubnodes : MfvcNode {
abstract override val type: IrSimpleType
val boxMethod: IrSimpleFunction
val leavesUnboxMethods: List<IrSimpleFunction>?
val subnodesImpl: MfvcNodeWithSubnodesImpl
}
fun MfvcNodeWithSubnodes.makeBoxedExpression(
scope: IrBuilderWithScope, typeArguments: TypeArguments, valueArguments: List<IrExpression>
): IrExpression = scope.irCall(boxMethod).apply {
val resultType = type.substitute(typeArguments) as IrSimpleType
require(resultType.erasedUpperBound == type.erasedUpperBound) { "Substitution of $type led to $resultType" }
for ((index, typeArgument) in resultType.arguments.withIndex()) {
putTypeArgument(index, typeArgument.typeOrNull ?: resultType.erasedUpperBound.typeParameters[index].defaultType)
}
for ((index, valueArgument) in valueArguments.withIndex()) {
putValueArgument(index, valueArgument)
}
}
operator fun MfvcNodeWithSubnodes.get(names: List<Name>): MfvcNode? {
var cur: MfvcNode = this
for (name in names) {
cur = (cur as? MfvcNodeWithSubnodes)?.get(name) ?: return null
}
return cur
}
private fun List<Any>.allEqual() = all { it == first() }
class MfvcNodeWithSubnodesImpl(val subnodes: List<NameableMfvcNode>, unboxMethod: IrSimpleFunction?) {
init {
require(subnodes.isNotEmpty())
require(subnodes.map { it.nameParts.dropLast(1) }.allEqual())
require(subnodes.map { it.index } == subnodes.indices.toList())
}
private val mapping = subnodes.associateBy { it.name }.also { mapping ->
require(mapping.size == subnodes.size) {
subnodes
.groupBy { it.name }
.filterValues { it.size > 1 }
.entries.joinToString(prefix = "Repeating node names found: ") { (name, nodes) -> "${nodes.size} nodes with name '$name'" }
}
}
operator fun get(name: Name): NameableMfvcNode? = mapping[name]
val leaves: List<LeafMfvcNode> = mapLeaves { it }
val fields: List<IrField>? = mapLeaves { it.field }.run {
@Suppress("UNCHECKED_CAST")
when {
all { it == null } -> null
all { it != null } -> this as List<IrField>
else -> error("IrFields can either exist all or none for MFVC property")
}
}
val allInnerUnboxMethods: List<IrSimpleFunction> = subnodes.flatMap { subnode ->
when (subnode) {
is MfvcNodeWithSubnodes -> subnode.allUnboxMethods
is LeafMfvcNode -> listOf(subnode.unboxMethod)
}
}
val allUnboxMethods = allInnerUnboxMethods + listOfNotNull(unboxMethod)
val indices: IntRange = leaves.indices
val subnodeIndices: Map<NameableMfvcNode, IntRange> = buildMap {
var offset = 0
for (node in subnodes) {
when (node) {
is IntermediateMfvcNode -> {
val nodeSize = node.leavesCount
put(node, offset until offset + nodeSize)
putAll(node.subnodeIndices.mapValues { (_, v) -> (v.first + offset)..(v.last + offset) })
offset += nodeSize
}
is LeafMfvcNode -> {
put(node, offset..offset)
offset++
}
}
}
}
}
val MfvcNodeWithSubnodes.subnodes: List<NameableMfvcNode>
get() = subnodesImpl.subnodes
operator fun MfvcNodeWithSubnodes.get(name: Name): NameableMfvcNode? = subnodesImpl[name]
val MfvcNodeWithSubnodes.leaves: List<LeafMfvcNode>
get() = subnodesImpl.leaves
val MfvcNodeWithSubnodes.fields: List<IrField>?
get() = subnodesImpl.fields
val RootMfvcNode.fields: List<IrField>
get() = subnodesImpl.fields!!
val MfvcNodeWithSubnodes.indices: IntRange
get() = subnodesImpl.indices
val MfvcNodeWithSubnodes.subnodeIndices: Map<NameableMfvcNode, IntRange>
get() = subnodesImpl.subnodeIndices
val MfvcNodeWithSubnodes.allUnboxMethods: List<IrSimpleFunction>
get() = subnodesImpl.allUnboxMethods
val MfvcNodeWithSubnodes.allInnerUnboxMethods: List<IrSimpleFunction>
get() = subnodesImpl.allInnerUnboxMethods
inline fun <R> MfvcNode.mapLeaves(crossinline f: (LeafMfvcNode) -> R): List<R> = flatMapLeaves { listOf(f(it)) }
fun <R> MfvcNode.flatMapLeaves(f: (LeafMfvcNode) -> List<R>): List<R> = when (this) {
is MfvcNodeWithSubnodes -> subnodes.flatMap { it.flatMapLeaves(f) }
is LeafMfvcNode -> f(this)
}
inline fun <R> MfvcNodeWithSubnodesImpl.mapLeaves(crossinline f: (LeafMfvcNode) -> R): List<R> = subnodes.flatMap { it.mapLeaves(f) }
private fun requireSameClasses(vararg classes: IrClass?) {
val notNulls = classes.filterNotNull()
require(notNulls.zipWithNext { a, b -> a == b }.all { it }) {
"Found different classes: ${notNulls.joinToString("\n") { it.render() }}"
}
}
private fun requireSameSizes(vararg sizes: Int) {
require(sizes.asList().zipWithNext { a, b -> a == b }.all { it }) {
"Found different sizes: ${sizes.joinToString()}"
}
}
private fun validateGettingAccessorParameters(function: IrSimpleFunction) {
require(function.valueParameters.isEmpty()) { "Value parameters are not expected for ${function.render()}" }
require(function.extensionReceiverParameter == null) { "Extension receiver is not expected for ${function.render()}" }
require(function.contextReceiverParametersCount == 0) { "Context receivers is not expected for ${function.render()}" }
require(function.typeParameters.isEmpty()) { "Type parameters are not expected for ${function.render()}" }
}
class LeafMfvcNode(
override val type: IrType,
rootPropertyName: String?,
nameParts: List<IndexedNamePart>,
val field: IrField?,
unboxMethod: IrSimpleFunction,
hasPureUnboxMethod: Boolean,
) : NameableMfvcNode {
override val namedNodeImpl: NameableMfvcNodeImpl = NameableMfvcNodeImpl(rootPropertyName, nameParts, unboxMethod, hasPureUnboxMethod)
override val leavesCount: Int
get() = 1
init {
requireSameClasses(
field?.parentAsClass?.takeUnless { unboxMethod.parentAsClass.isCompanion },
unboxMethod.parentAsClass,
(unboxMethod.dispatchReceiverParameter?.type as? IrSimpleType)?.erasedUpperBound,
)
validateGettingAccessorParameters(unboxMethod)
}
override fun createInstanceFromBox(
scope: IrBlockBuilder,
typeArguments: TypeArguments,
receiver: IrExpression?,
accessType: AccessType,
saveVariable: (IrVariable) -> Unit,
) = ReceiverBasedMfvcNodeInstance(
scope, this, typeArguments, receiver, field?.let(::listOf), unboxMethod, accessType, saveVariable
)
override fun toString(): String = "$fullFieldName: ${type.render()}"
}
val MfvcNode.fields
get() = when (this) {
is MfvcNodeWithSubnodes -> this.fields
is LeafMfvcNode -> field?.let(::listOf)
}
class IntermediateMfvcNode(
override val type: IrSimpleType,
rootPropertyName: String?,
nameParts: List<IndexedNamePart>,
subnodes: List<NameableMfvcNode>,
unboxMethod: IrSimpleFunction,
hasPureUnboxMethod: Boolean,
val rootNode: RootMfvcNode, // root node corresponding type of the node
) : NameableMfvcNode, MfvcNodeWithSubnodes {
override val namedNodeImpl: NameableMfvcNodeImpl = NameableMfvcNodeImpl(rootPropertyName, nameParts, unboxMethod, hasPureUnboxMethod)
override val subnodesImpl: MfvcNodeWithSubnodesImpl = MfvcNodeWithSubnodesImpl(subnodes, unboxMethod)
override val leavesCount
get() = leaves.size
init {
require(type.needsMfvcFlattening()) { "MFVC type expected but got% ${type.render()}" }
require(type.erasedUpperBound == rootNode.mfvc) {
"Root node must point at the RootNode of class ${type.erasedUpperBound.render()} but points at ${rootNode.mfvc.render()}"
}
requireSameClasses(
unboxMethod.parentAsClass,
(unboxMethod.dispatchReceiverParameter?.type as IrSimpleType?)?.erasedUpperBound,
)
validateGettingAccessorParameters(unboxMethod)
}
override val boxMethod: IrSimpleFunction
get() = rootNode.boxMethod
override val leavesUnboxMethods: List<IrSimpleFunction> = collectLeavesUnboxMethods()
override fun createInstanceFromBox(
scope: IrBlockBuilder,
typeArguments: TypeArguments,
receiver: IrExpression?,
accessType: AccessType,
saveVariable: (IrVariable) -> Unit,
) = ReceiverBasedMfvcNodeInstance(
scope, this, typeArguments, receiver, fields, unboxMethod, accessType, saveVariable
)
override fun toString(): String =
"$fullFieldName: ${type.render()}\n${subnodes.joinToString("\n").prependIndent(" ")}"
}
private fun MfvcNodeWithSubnodes.collectLeavesUnboxMethods() = mapLeaves { it.unboxMethod }
fun IrSimpleFunction.isDefaultGetter(expectedField: IrField? = null): Boolean {
if (!isGetter) return false
if (expectedField != null && correspondingPropertySymbol?.owner?.backingField != expectedField) return false
val statement = (body?.statements?.singleOrNull() as? IrReturn)?.value as? IrGetField ?: return false
val actualField = statement.symbol.owner
return expectedField == null || actualField == expectedField || parentAsClass.isCompanion && actualField.correspondingPropertySymbol == correspondingPropertySymbol
}
fun IrSimpleFunction.getGetterField(): IrField? {
if (!isGetter) return null
val statement = (body?.statements?.singleOrNull() as? IrReturn)?.value as? IrGetField ?: return null
return statement.symbol.owner
}
class RootMfvcNode internal constructor(
val mfvc: IrClass,
subnodes: List<NameableMfvcNode>,
val oldPrimaryConstructor: IrConstructor,
val newPrimaryConstructor: IrConstructor,
val primaryConstructorImpl: IrSimpleFunction,
override val boxMethod: IrSimpleFunction,
val specializedEqualsMethod: IrSimpleFunction,
) : MfvcNodeWithSubnodes {
override val subnodesImpl: MfvcNodeWithSubnodesImpl = MfvcNodeWithSubnodesImpl(subnodes, null)
override val type: IrSimpleType = mfvc.defaultType
override val leavesCount: Int
get() = leaves.size
override val leavesUnboxMethods: List<IrSimpleFunction> = collectLeavesUnboxMethods()
init {
require(type.needsMfvcFlattening()) { "MFVC type expected but got% ${type.render()}" }
for (constructor in listOf(oldPrimaryConstructor, newPrimaryConstructor)) {
require(constructor.isPrimary) { "Expected a primary constructor but got:\n${constructor.dump()}" }
}
requireSameClasses(
mfvc,
oldPrimaryConstructor.parentAsClass,
newPrimaryConstructor.parentAsClass,
primaryConstructorImpl.parentAsClass,
boxMethod.parentAsClass,
specializedEqualsMethod.parentAsClass,
oldPrimaryConstructor.constructedClass,
newPrimaryConstructor.constructedClass,
boxMethod.returnType.erasedUpperBound,
)
require(primaryConstructorImpl.returnType.isUnit()) {
"Constructor-impl must return Unit but returns ${primaryConstructorImpl.returnType.render()}"
}
require(specializedEqualsMethod.returnType.isBoolean()) {
"Specialized equals method must return Boolean but returns ${primaryConstructorImpl.returnType.render()}"
}
require(oldPrimaryConstructor.typeParameters.isEmpty() && newPrimaryConstructor.typeParameters.isEmpty()) {
"Constructors do not support type parameters yet"
}
requireSameSizes(
mfvc.typeParameters.size,
boxMethod.typeParameters.size,
primaryConstructorImpl.typeParameters.size,
)
require(specializedEqualsMethod.typeParameters.size == 2 * mfvc.typeParameters.size) {
"Specialized equals method must contain twice more type parameters than corresponding MFVC ${mfvc.typeParameters.map { it.defaultType.render() }} but has ${specializedEqualsMethod.typeParameters.map { it.defaultType.render() }}"
}
requireSameSizes(oldPrimaryConstructor.valueParameters.size, subnodes.size)
requireSameSizes(
leavesCount,
newPrimaryConstructor.valueParameters.size,
primaryConstructorImpl.valueParameters.size,
boxMethod.valueParameters.size,
)
require(specializedEqualsMethod.valueParameters.size == 2 * leavesCount) {
"Specialized equals method must contain twice more value parameters than corresponding primary constructor of the MFVC ${mfvc.typeParameters.map { it.defaultType.render() }} but has ${specializedEqualsMethod.typeParameters.map { it.defaultType.render() }}"
}
for (function in listOf(oldPrimaryConstructor, newPrimaryConstructor, primaryConstructorImpl, boxMethod, specializedEqualsMethod)) {
require(function.extensionReceiverParameter == null) { "Extension receiver is not expected for ${function.render()}" }
require(function.contextReceiverParametersCount == 0) { "Context receivers are not expected for ${function.render()}" }
}
}
override fun createInstanceFromBox(
scope: IrBlockBuilder,
typeArguments: TypeArguments,
receiver: IrExpression?,
accessType: AccessType,
saveVariable: (IrVariable) -> Unit,
) = ReceiverBasedMfvcNodeInstance(scope, this, typeArguments, receiver, fields, null, accessType, saveVariable)
override fun toString(): String =
"${type.render()}\n${subnodes.joinToString("\n").prependIndent(" ")}"
}
fun IrType.needsMfvcFlattening(): Boolean = isMultiFieldValueClassType() && !isNullable() ||
classifierOrNull.let { classifier ->
classifier is IrTypeParameterSymbol && classifier.owner.superTypes.any { it.needsMfvcFlattening() }
} // add not is annotated as @UseBox etc
@@ -1,487 +0,0 @@
/*
* 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.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.parents
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.MultiFieldValueClassRepresentation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.deepCopyWithVariables
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
fun createLeafMfvcNode(
parent: IrDeclarationContainer,
context: JvmBackendContext,
type: IrType,
rootPropertyName: String?,
nameParts: List<IndexedNamePart>,
fieldAnnotations: List<IrConstructorCall>,
static: Boolean,
overriddenNode: LeafMfvcNode?,
defaultMethodsImplementationSourceNode: Pair<IrSimpleFunction?, LeafMfvcNode>?, // used if the getter was custom and need to call it.
oldGetter: IrSimpleFunction?,
modality: Modality,
oldPropertyBackingField: IrField?,
): LeafMfvcNode {
require(!type.needsMfvcFlattening()) { "${type.render()} requires flattening" }
val fullMethodName = NameableMfvcNodeImpl.makeFullMethodName(rootPropertyName, nameParts)
val fullFieldName = NameableMfvcNodeImpl.makeFullFieldName(rootPropertyName, nameParts)
val field = oldPropertyBackingField?.let { oldBackingField ->
context.irFactory.buildField {
updateFrom(oldBackingField)
this.name = fullFieldName
this.type = type
this.visibility = DescriptorVisibilities.PRIVATE
this.metadata = null
}.apply {
this.parent = oldBackingField.parent
this.annotations = fieldAnnotations.map { it.deepCopyWithVariables() }
}
}
val unboxMethod = makeUnboxMethod(
context,
fullMethodName,
type,
parent,
overriddenNode,
static,
defaultMethodsImplementationSourceNode,
oldGetter,
modality,
) { receiver -> irGetField(if (field!!.isStatic) null else irGet(receiver!!), field) }
return LeafMfvcNode(type, rootPropertyName, nameParts, field, unboxMethod, defaultMethodsImplementationSourceNode.isPure())
}
private fun Pair<IrSimpleFunction?, NameableMfvcNode>?.isPure(): Boolean {
val (outer, inner) = this ?: return true
return outer == null && inner.hasPureUnboxMethod
}
private fun makeUnboxMethod(
context: JvmBackendContext,
fullMethodName: Name,
type: IrType,
parent: IrDeclarationParent,
overriddenNode: NameableMfvcNode?,
static: Boolean,
defaultMethodsImplementationSourceNode: Pair<IrSimpleFunction?, NameableMfvcNode>?,
oldGetter: IrSimpleFunction?,
modality: Modality,
makeOptimizedExpression: IrBuilderWithScope.(receiver: IrValueDeclaration?) -> IrExpression,
): IrSimpleFunction {
val res = oldGetter ?: context.irFactory.buildFun {
this.name = fullMethodName
this.origin = JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER
this.returnType = type
this.modality = modality
}.apply {
this.parent = parent
overriddenSymbols = overriddenNode?.let { it.unboxMethod.overriddenSymbols + it.unboxMethod.symbol } ?: listOf()
if (!static) {
createDispatchReceiverParameter()
}
}
return res.apply {
body = with(context.createJvmIrBuilder(this.symbol)) {
val receiver = dispatchReceiverParameter
if (defaultMethodsImplementationSourceNode == null) {
irExprBody(makeOptimizedExpression(receiver))
} else {
val (outer, inner) = defaultMethodsImplementationSourceNode
val receiverExpression = receiver?.let { irGet(it) }
val outerCall = if (outer == null) receiverExpression else irCall(outer).apply { dispatchReceiver = receiverExpression }
val innerCall = irCall(inner.unboxMethod).apply { dispatchReceiver = outerCall }
irExprBody(innerCall)
}
}
}
}
fun createNameableMfvcNodes(
parent: IrDeclarationContainer,
context: JvmBackendContext,
type: IrSimpleType,
typeArguments: TypeArguments,
rootPropertyName: String?,
nameParts: List<IndexedNamePart>,
fieldAnnotations: List<IrConstructorCall>,
static: Boolean,
overriddenNode: NameableMfvcNode?,
defaultMethodsImplementationSourceNode: Pair<IrSimpleFunction?, NameableMfvcNode>?, // used if the getter was custom and need to call it.
oldGetter: IrSimpleFunction?,
modality: Modality,
oldPropertyBackingField: IrField?,
): NameableMfvcNode = if (type.needsMfvcFlattening()) createIntermediateMfvcNode(
parent,
context,
type,
typeArguments,
rootPropertyName,
nameParts,
fieldAnnotations,
static,
overriddenNode as IntermediateMfvcNode?,
defaultMethodsImplementationSourceNode?.let { (outer, inner) -> outer to (inner as IntermediateMfvcNode) },
oldGetter,
modality,
oldPropertyBackingField,
) else createLeafMfvcNode(
parent,
context,
type,
rootPropertyName,
nameParts,
fieldAnnotations,
static,
overriddenNode as LeafMfvcNode?,
defaultMethodsImplementationSourceNode?.let { (outer, inner) -> outer to (inner as LeafMfvcNode) },
oldGetter,
modality,
oldPropertyBackingField
)
fun createIntermediateMfvcNode(
parent: IrDeclarationContainer,
context: JvmBackendContext,
type: IrSimpleType,
typeArguments: TypeArguments,
rootPropertyName: String?,
nameParts: List<IndexedNamePart>,
fieldAnnotations: List<IrConstructorCall>,
static: Boolean,
overriddenNode: IntermediateMfvcNode?,
defaultMethodsImplementationSourceNode: Pair<IrSimpleFunction?, IntermediateMfvcNode>?, // used if the getter was custom and need to call it.
oldGetter: IrSimpleFunction?,
modality: Modality,
oldPropertyBackingField: IrField?,
): IntermediateMfvcNode {
require(type.needsMfvcFlattening()) { "${type.render()} does not require flattening" }
val valueClass = type.erasedUpperBound
val representation = valueClass.multiFieldValueClassRepresentation!!
val replacements = context.multiFieldValueClassReplacements
val rootNode = replacements.getRootMfvcNode(valueClass)!!
val oldField = oldGetter?.correspondingPropertySymbol?.owner?.backingField
val shadowBackingFieldProperty = if (oldField == null) oldGetter?.getGetterField()?.correspondingPropertySymbol?.owner else null
val useOldGetter = oldGetter != null && (oldField == null || !oldGetter.isDefaultGetter(oldField))
val subnodes = representation.underlyingPropertyNamesToTypes.mapIndexed { index, (name, type) ->
val newType = type.substitute(typeArguments) as IrSimpleType
val newTypeArguments = typeArguments.toMutableMap().apply { putAll(makeTypeArgumentsFromType(newType)) }
val newDefaultMethodsImplementationSourceNode = when {
defaultMethodsImplementationSourceNode != null -> {
val (outer, inner) = defaultMethodsImplementationSourceNode
require(!useOldGetter) { "Multiple non-default getters:\n\n${outer?.dump()}\n\n${oldGetter?.dump()}" }
outer to inner[name]!!
}
shadowBackingFieldProperty != null -> null to replacements.getMfvcPropertyNode(shadowBackingFieldProperty)!!
useOldGetter -> oldGetter!! to rootNode[name]!!
else -> null
}
createNameableMfvcNodes(
parent,
context,
newType,
newTypeArguments,
rootPropertyName,
nameParts + IndexedNamePart(index, name),
fieldAnnotations,
static,
overriddenNode?.let { it[name]!! },
newDefaultMethodsImplementationSourceNode,
null,
modality,
oldPropertyBackingField,
)
}
val fullMethodName = NameableMfvcNodeImpl.makeFullMethodName(rootPropertyName, nameParts)
val unboxMethod = if (useOldGetter) oldGetter!! else makeUnboxMethod(
context, fullMethodName, type, parent, overriddenNode, static, defaultMethodsImplementationSourceNode, oldGetter, modality
) { receiver ->
val valueArguments = subnodes.flatMap { it.fields!! }
.map { field -> irGetField(if (field.isStatic) null else irGet(receiver!!), field) }
rootNode.makeBoxedExpression(this, typeArguments, valueArguments)
}
val hasPureUnboxMethod = defaultMethodsImplementationSourceNode.isPure() && subnodes.all { it.hasPureUnboxMethod }
return IntermediateMfvcNode(
type, rootPropertyName, nameParts, subnodes, unboxMethod, hasPureUnboxMethod, rootNode
)
}
fun collectPropertiesAfterLowering(irClass: IrClass) = LinkedHashSet<IrProperty>().apply {
for (element in irClass.declarations) {
if (element is IrField) {
element.correspondingPropertySymbol?.owner?.let { add(it) }
} else if (element is IrSimpleFunction && element.extensionReceiverParameter == null && element.contextReceiverParametersCount == 0) {
element.correspondingPropertySymbol?.owner?.let { add(it) }
}
}
}
private fun IrProperty.isStatic(currentContainer: IrDeclarationContainer) =
getterIfDeclared(currentContainer)?.isStatic ?: backingField?.isStatic ?: error("Property without both getter and backing field")
fun getRootNode(context: JvmBackendContext, mfvc: IrClass): RootMfvcNode {
require(mfvc.isMultiFieldValueClass) { "${mfvc.defaultType.render()} does not require flattening" }
val oldPrimaryConstructor = mfvc.primaryConstructor!!
val representation = mfvc.multiFieldValueClassRepresentation!!
val properties = collectPropertiesAfterLowering(mfvc).associateBy { it.isStatic(mfvc) to it.name }
val subnodes = makeRootMfvcNodeSubnodes(representation, properties, context, mfvc)
val mfvcNodeWithSubnodesImpl = MfvcNodeWithSubnodesImpl(subnodes, null)
val leaves = mfvcNodeWithSubnodesImpl.leaves
val fields = mfvcNodeWithSubnodesImpl.fields!!
val newPrimaryConstructor = makeMfvcPrimaryConstructor(context, oldPrimaryConstructor, mfvc, leaves, fields)
val primaryConstructorImpl = makePrimaryConstructorImpl(context, oldPrimaryConstructor, mfvc, leaves)
val boxMethod = makeBoxMethod(context, mfvc, leaves, newPrimaryConstructor)
val specializedEqualsMethod = makeSpecializedEqualsMethod(context, mfvc, leaves)
return RootMfvcNode(
mfvc, subnodes, oldPrimaryConstructor, newPrimaryConstructor, primaryConstructorImpl, boxMethod, specializedEqualsMethod
)
}
private fun makeSpecializedEqualsMethod(
context: JvmBackendContext,
mfvc: IrClass,
leaves: List<LeafMfvcNode>
) = context.irFactory.buildFun {
name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
// TODO: Revisit this once we allow user defined equals methods in value classes.
origin = JvmLoweredDeclarationOrigin.MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD
returnType = context.irBuiltIns.booleanType
}.apply {
parent = mfvc
copyTypeParametersFrom(mfvc)
val typeParametersHalf1 = typeParameters.apply {
for (it in this) {
it.name = Name.guessByFirstCharacter("${it.name.asString()}1")
}
}
copyTypeParametersFrom(mfvc)
val typeParametersHalf2 = typeParameters.drop(typeParametersHalf1.size).apply {
for (it in this) {
it.name = Name.guessByFirstCharacter("${it.name.asString()}2")
}
}
val valueParametersHalf1 = leaves.map { leaf ->
addValueParameter {
this.name = Name.guessByFirstCharacter(
"${InlineClassDescriptorResolver.SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME}$${leaf.fullFieldName}"
)
this.type = leaf.type.substitute(mfvc.typeParameters, typeParametersHalf1.map { it.defaultType })
}
}
val valueParametersHalf2 = leaves.map { leaf ->
addValueParameter {
this.name = Name.guessByFirstCharacter(
"${InlineClassDescriptorResolver.SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME}$${leaf.fullFieldName}"
)
this.type = leaf.type.substitute(mfvc.typeParameters, typeParametersHalf2.map { it.defaultType })
}
}
body = with(context.createJvmIrBuilder(this.symbol)) {
val conjunctions = valueParametersHalf1.zip(valueParametersHalf2) { f1, f2 -> irEquals(irGet(f1), irGet(f2)) }
irExprBody(conjunctions.reduce { acc, current ->
irCall(context.irBuiltIns.andandSymbol).apply {
putValueArgument(0, acc)
putValueArgument(1, current)
}
})
}
}
private fun makeBoxMethod(
context: JvmBackendContext,
mfvc: IrClass,
leaves: List<LeafMfvcNode>,
newPrimaryConstructor: IrConstructor
) = context.irFactory.buildFun {
name = Name.identifier(KotlinTypeMapper.BOX_JVM_METHOD_NAME)
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER
returnType = mfvc.defaultType
}.apply {
parent = mfvc
copyTypeParametersFrom(mfvc)
val mapping = mfvc.typeParameters.zip(typeParameters) { classTypeParameter, functionTypeParameter ->
classTypeParameter.symbol to functionTypeParameter.defaultType
}.toMap()
returnType = returnType.substitute(mapping)
val parameters = leaves.map { leaf -> addValueParameter(leaf.fullFieldName, leaf.type.substitute(mapping)) }
body = with(context.createJvmIrBuilder(this.symbol)) {
irExprBody(irCall(newPrimaryConstructor).apply {
for ((index, parameter) in parameters.withIndex()) {
putValueArgument(index, irGet(parameter))
}
})
}
}
private fun makePrimaryConstructorImpl(
context: JvmBackendContext,
oldPrimaryConstructor: IrConstructor,
mfvc: IrClass,
leaves: List<LeafMfvcNode>
) = context.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 {
parent = mfvc
copyTypeParametersFrom(mfvc)
for (leaf in leaves) {
addValueParameter(leaf.fullFieldName, leaf.type.substitute(mfvc.typeParameters, typeParameters.map { it.defaultType }))
}
// body is added in the Lowering file as it needs to be lowered
}
private fun makeMfvcPrimaryConstructor(
context: JvmBackendContext,
oldPrimaryConstructor: IrConstructor,
mfvc: IrClass,
leaves: List<LeafMfvcNode>,
fields: List<IrField>
) = context.irFactory.buildConstructor {
updateFrom(oldPrimaryConstructor)
visibility = DescriptorVisibilities.PRIVATE
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER
returnType = oldPrimaryConstructor.returnType
}.apply {
require(oldPrimaryConstructor.typeParameters.isEmpty()) { "Constructors do not support type parameters yet" }
this.parent = mfvc
val parameters = leaves.map { addValueParameter(it.fullFieldName, it.type) }
val irConstructor = this@apply
body = context.createIrBuilder(irConstructor.symbol).irBlockBody(irConstructor) {
+irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single())
for ((field, parameter) in fields zip parameters) {
+irSetField(irGet(mfvc.thisReceiver!!), field, irGet(parameter))
}
}
}
private fun makeRootMfvcNodeSubnodes(
representation: MultiFieldValueClassRepresentation<IrSimpleType>,
properties: Map<Pair<Boolean, Name>, IrProperty>,
context: JvmBackendContext,
mfvc: IrClass
) = representation.underlyingPropertyNamesToTypes.mapIndexed { index, (name, type) ->
val typeArguments = makeTypeArgumentsFromType(type)
val oldProperty = properties[false to name]!!
val oldBackingField = oldProperty.backingField
val oldGetter = oldProperty.getterIfDeclared(mfvc)
val overriddenNode = oldGetter?.let { getOverriddenNode(context.multiFieldValueClassReplacements, it) as IntermediateMfvcNode? }
val static = oldProperty.isStatic(mfvc)
createNameableMfvcNodes(
mfvc,
context,
type,
typeArguments,
null,
listOf(IndexedNamePart(index, name)),
oldBackingField?.annotations ?: listOf(),
static,
overriddenNode,
null,
oldGetter.takeIf { static },
Modality.FINAL,
oldBackingField,
).also {
updateAnnotationsAndPropertyFromOldProperty(oldProperty)
it.unboxMethod.overriddenSymbols = listOf() // the getter is saved so it overrides itself
}
}
private fun updateAnnotationsAndPropertyFromOldProperty(
oldProperty: IrProperty
) {
oldProperty.setter?.apply {
name = Name.identifier(JvmAbi.setterName(oldProperty.name.asString()))
correspondingPropertySymbol = null
origin = IrDeclarationOrigin.DEFINED
}
oldProperty.setter = null
oldProperty.backingField = null
}
fun createIntermediateNodeForMfvcPropertyOfRegularClass(
parent: IrDeclarationContainer,
context: JvmBackendContext,
oldProperty: IrProperty,
): IntermediateMfvcNode {
val oldGetter = oldProperty.getterIfDeclared(parent)
val oldField = oldProperty.backingField
val type = oldProperty.getter?.returnType ?: oldField?.type ?: error("Either getter or field must exist")
require(type is IrSimpleType && type.needsMfvcFlattening()) { "Expected MFVC but got ${type.render()}" }
val fieldAnnotations = oldField?.annotations ?: listOf()
val static = oldProperty.isStatic(parent)
val overriddenNode = oldGetter?.let { getOverriddenNode(context.multiFieldValueClassReplacements, it) as IntermediateMfvcNode? }
val modality = if (oldGetter == null || oldGetter.modality == Modality.FINAL) Modality.FINAL else oldGetter.modality
return createIntermediateMfvcNode(
parent, context, type, makeTypeArgumentsFromType(type), oldProperty.name.asString(), listOf(),
fieldAnnotations, static, overriddenNode, null, oldGetter, modality, oldField
).also {
updateAnnotationsAndPropertyFromOldProperty(oldProperty)
}
}
private fun getOverriddenNode(replacements: MemoizedMultiFieldValueClassReplacements, getter: IrSimpleFunction): NameableMfvcNode? =
getter.overriddenSymbols
.firstOrNull { !it.owner.isFakeOverride }
?.let { replacements.getMfvcPropertyNode(it.owner.correspondingPropertySymbol!!.owner) }
fun getOptimizedPublicAccess(currentElement: IrElement?, parent: IrClass): AccessType {
val declaration = currentElement as? IrDeclaration ?: return AccessType.AlwaysPublic
for (cur in declaration.parents.filterIsInstance<IrClass>()) {
return when {
cur == parent -> AccessType.PrivateWhenNoBox
cur.isInner -> continue
cur.isCompanion -> continue
else -> AccessType.AlwaysPublic
}
}
return AccessType.AlwaysPublic
}
private fun IrProperty.getterIfDeclared(parent: IrDeclarationContainer): IrSimpleFunction? = getter?.takeIf { it in parent.declarations }
@@ -1,279 +0,0 @@
/*
* 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.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.substitute
import org.jetbrains.kotlin.name.Name
interface MfvcNodeInstance {
val node: MfvcNode
val typeArguments: TypeArguments
val type: IrSimpleType
fun makeFlattenedGetterExpressions(scope: IrBlockBuilder): List<IrExpression>
fun makeGetterExpression(scope: IrBuilderWithScope): IrExpression
operator fun get(name: Name): MfvcNodeInstance?
fun makeStatements(scope: IrBuilderWithScope, values: List<IrExpression>): List<IrStatement>
}
private fun makeTypeFromMfvcNodeAndTypeArguments(node: MfvcNode, typeArguments: TypeArguments) =
node.type.substitute(typeArguments) as IrSimpleType
fun MfvcNodeInstance.addSetterStatements(scope: IrBlockBuilder, values: List<IrExpression>) = with(scope) {
for (statement in makeStatements(this, values)) {
+statement
}
}
fun MfvcNodeInstance.makeSetterExpressions(scope: IrBuilderWithScope, values: List<IrExpression>): IrExpression = scope.irBlock {
addSetterStatements(this, values)
}
private fun MfvcNodeInstance.checkValuesCount(values: List<IrExpression>) {
require(values.size == node.leavesCount) { "Node $node requires ${node.leavesCount} values but got ${values.map { it.render() }}" }
}
class ValueDeclarationMfvcNodeInstance(
override val node: MfvcNode,
override val typeArguments: TypeArguments,
val valueDeclarations: List<IrValueDeclaration>,
) : MfvcNodeInstance {
init {
require(valueDeclarations.size == size) { "Expected value declarations list of size $size but got of size ${valueDeclarations.size}" }
}
override val type: IrSimpleType = makeTypeFromMfvcNodeAndTypeArguments(node, typeArguments)
override fun makeFlattenedGetterExpressions(scope: IrBlockBuilder): List<IrExpression> =
makeFlattenedGetterExpressions(scope as IrBuilderWithScope)
private fun makeFlattenedGetterExpressions(scope: IrBuilderWithScope): List<IrExpression> = valueDeclarations.map { scope.irGet(it) }
override fun makeGetterExpression(scope: IrBuilderWithScope): IrExpression = when (node) {
is LeafMfvcNode -> makeFlattenedGetterExpressions(scope).single()
is MfvcNodeWithSubnodes -> node.makeBoxedExpression(scope, typeArguments, makeFlattenedGetterExpressions(scope))
}
override fun get(name: Name): ValueDeclarationMfvcNodeInstance? {
val (newNode, indices) = node.getSubnodeAndIndices(name) ?: return null
return ValueDeclarationMfvcNodeInstance(newNode, typeArguments, valueDeclarations.slice(indices))
}
override fun makeStatements(scope: IrBuilderWithScope, values: List<IrExpression>): List<IrStatement> {
checkValuesCount(values)
return valueDeclarations.zip(values) { declaration, value -> scope.irSet(declaration, value) }
}
}
internal class ExpressionCopierImpl(
expression: IrExpression?,
private val scope: IrBlockBuilder,
private val saveVariable: (IrVariable) -> Unit,
) {
private sealed interface CopyableExpression {
fun makeExpression(scope: IrBuilderWithScope): IrExpression
}
private class SavedToVariable(val variable: IrVariable) : CopyableExpression {
override fun makeExpression(scope: IrBuilderWithScope): IrExpression = scope.irGet(variable)
}
private class PureExpression(val expression: IrExpression) : CopyableExpression {
override fun makeExpression(scope: IrBuilderWithScope): IrExpression = expression.deepCopyWithSymbols()
}
private fun IrExpression.orSavedToVariable(): CopyableExpression =
if (isRepeatableGetter()) {
PureExpression(this)
} else SavedToVariable(
scope.savableStandaloneVariableWithSetter(
this@orSavedToVariable,
origin = IrDeclarationOrigin.TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE,
saveVariable = saveVariable,
isTemporary = true,
)
)
private val copyableExpression = expression?.orSavedToVariable()
fun makeCopy() = copyableExpression?.makeExpression(scope)
}
fun IrExpression?.isRepeatableGetter(): Boolean = when (this) {
null -> true
is IrConst<*> -> true
is IrGetValue -> true
is IrGetField -> receiver.isRepeatableGetter()
is IrTypeOperatorCallImpl -> this.argument.isRepeatableGetter()
is IrContainerExpression -> statements.all { it is IrExpression && it.isRepeatableGetter() || it is IrVariable }
else -> false
}
fun IrExpression?.isRepeatableSetter(): Boolean = when (this) {
null -> true
is IrConst<*> -> true
is IrSetValue -> value.isRepeatableGetter()
is IrSetField -> receiver.isRepeatableGetter() && value.isRepeatableGetter()
is IrTypeOperatorCallImpl -> this.argument.isRepeatableSetter()
is IrContainerExpression -> statements.dropLast(1).all { it is IrExpression && it.isRepeatableGetter() || it is IrVariable } &&
statements.lastOrNull().let { it is IrExpression? && it.isRepeatableSetter() }
else -> false
}
fun IrExpression?.isRepeatableAccessor(): Boolean = isRepeatableGetter() || isRepeatableSetter()
enum class AccessType { AlwaysPublic, PrivateWhenNoBox, AlwaysPrivate }
class ReceiverBasedMfvcNodeInstance(
private val scope: IrBlockBuilder,
override val node: MfvcNode,
override val typeArguments: TypeArguments,
receiver: IrExpression?,
val fields: List<IrField>?,
val unboxMethod: IrSimpleFunction?,
val accessType: AccessType,
private val saveVariable: (IrVariable) -> Unit,
) : MfvcNodeInstance {
override val type: IrSimpleType = makeTypeFromMfvcNodeAndTypeArguments(node, typeArguments)
private val receiverCopier = ExpressionCopierImpl(receiver, scope, saveVariable)
private fun makeReceiverCopy() = receiverCopier.makeCopy()
init {
require(fields == null || fields.isNotEmpty()) { "Empty list of fields" }
require(node is RootMfvcNode == (unboxMethod == null)) { "Only root node has node getter" }
}
override fun makeFlattenedGetterExpressions(scope: IrBlockBuilder): List<IrExpression> = when (node) {
is LeafMfvcNode -> listOf(makeGetterExpression(scope))
is MfvcNodeWithSubnodes -> when {
node is IntermediateMfvcNode && canUsePrivateAccessFor(node) && fields != null ->
fields.map { scope.irGetField(makeReceiverCopy(), it) }
node is IntermediateMfvcNode && !node.hasPureUnboxMethod -> {
val value = makeGetterExpression(scope)
val asVariable = scope.savableStandaloneVariableWithSetter(
value,
origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER,
saveVariable = saveVariable,
isTemporary = true,
)
val root = node.rootNode
val variableInstance =
root.createInstanceFromBox(scope, typeArguments, scope.irGet(asVariable), accessType, saveVariable)
variableInstance.makeFlattenedGetterExpressions(scope)
}
else -> node.subnodes.flatMap { get(it.name)!!.makeFlattenedGetterExpressions(scope) }
}
}
override fun makeGetterExpression(scope: IrBuilderWithScope): IrExpression = with(scope) {
when {
node is LeafMfvcNode && canUsePrivateAccessFor(node) && fields != null -> irGetField(makeReceiverCopy(), fields.single())
node is IntermediateMfvcNode && accessType == AccessType.AlwaysPrivate && fields != null ->
node.makeBoxedExpression(this, typeArguments, fields.map { irGetField(makeReceiverCopy(), it) })
unboxMethod != null -> irCall(unboxMethod).apply {
val dispatchReceiverParameter = unboxMethod.dispatchReceiverParameter
if (dispatchReceiverParameter != null) {
dispatchReceiver = makeReceiverCopy() ?: run {
val erasedUpperBound = dispatchReceiverParameter.type.erasedUpperBound
require(erasedUpperBound.isCompanion) { "Expected a dispatch receiver for:\n${unboxMethod.dump()}" }
irGetObject(erasedUpperBound.symbol)
}
}
}
node is RootMfvcNode -> makeReceiverCopy()!!
else -> error("Unbox method must exist for $node")
}
}
private fun canUsePrivateAccessFor(node: NameableMfvcNode) =
node.hasPureUnboxMethod && accessType == AccessType.PrivateWhenNoBox || accessType == AccessType.AlwaysPrivate
override fun get(name: Name): ReceiverBasedMfvcNodeInstance? {
val (newNode, _) = node.getSubnodeAndIndices(name) ?: return null
return newNode.createInstanceFromBox(scope, typeArguments, makeReceiverCopy(), accessType, saveVariable)
}
override fun makeStatements(scope: IrBuilderWithScope, values: List<IrExpression>): List<IrStatement> {
checkValuesCount(values)
require(fields != null) { "$node is immutable as it has custom getter and so no backing fields" }
return fields.zip(values) { field, expr -> scope.irSetField(makeReceiverCopy(), field, expr) }
}
}
val MfvcNodeInstance.size: Int
get() = node.leavesCount
fun IrContainerExpression.unwrapBlock(): IrExpression = statements.singleOrNull() as? IrExpression ?: this
/**
* Creates a variable and doesn't add it to a container. It saves the variable with given saveVariable.
*
* It may be used when the variable will be used outside the current container so the declaration is added later when all usages are known.
*/
fun IrBuilderWithScope.savableStandaloneVariable(
type: IrType,
name: String? = null,
isMutable: Boolean = false,
origin: IrDeclarationOrigin,
isTemporary: Boolean = origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
saveVariable: (IrVariable) -> Unit,
): IrVariable {
val variable = if (isTemporary || name == null) scope.createTemporaryVariableDeclaration(
type, name, isMutable,
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
) else IrVariableImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
symbol = IrVariableSymbolImpl(),
name = Name.identifier(name),
type = type,
isVar = isMutable,
isConst = false,
isLateinit = false
).apply {
parent = this@savableStandaloneVariable.scope.getLocalDeclarationParent()
}
saveVariable(variable)
return variable
}
/**
* Creates a variable and doesn't add it to a container. It saves the variable with given saveVariable. It adds irSet-based initialization.
*
* It may be used when the variable will be used outside the current container so the declaration is added later when all usages are known.
*/
fun <T : IrElement> IrStatementsBuilder<T>.savableStandaloneVariableWithSetter(
expression: IrExpression,
name: String? = null,
isMutable: Boolean = false,
origin: IrDeclarationOrigin,
isTemporary: Boolean = origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
saveVariable: (IrVariable) -> Unit,
) = savableStandaloneVariable(expression.type, name, isMutable, origin, isTemporary, saveVariable).also {
+irSet(it, expression)
}
@@ -117,11 +117,8 @@ fun IrType.defaultValue(startOffset: Int, endOffset: Int, context: JvmBackendCon
}
}
fun IrType.isInlineClassType(): Boolean = erasedUpperBound.isSingleFieldValueClass
fun IrType.isMultiFieldValueClassType(): Boolean = erasedUpperBound.isMultiFieldValueClass
fun IrType.isValueClassType(): Boolean = erasedUpperBound.isValue
fun IrType.isInlineClassType(): Boolean =
erasedUpperBound.isSingleFieldValueClass
val IrType.upperBound: IrType
get() = erasedUpperBound.symbol.starProjectedType
@@ -8,7 +8,10 @@ package org.jetbrains.kotlin.backend.jvm.ir
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.common.lower.irNot
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.CachedFieldsForObjectInstances
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.JvmSymbols
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.codegen.ASSERTIONS_DISABLED_FIELD_NAME
import org.jetbrains.kotlin.codegen.AsmUtil
@@ -243,9 +246,6 @@ fun IrSimpleFunction.copyCorrespondingPropertyFrom(source: IrSimpleFunction) {
fun IrProperty.needsAccessor(accessor: IrSimpleFunction): Boolean = when {
// Properties in annotation classes become abstract methods named after the property.
(parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS -> true
// Multi-field value class getters must always be added. Getters for properties of MFVC itself follow general rules.
accessor.isGetter && accessor.contextReceiverParametersCount == 0 && accessor.extensionReceiverParameter == null &&
!accessor.parent.let { it is IrClass && it.isMultiFieldValueClass } && accessor.returnType.needsMfvcFlattening() -> true
// @JvmField properties have no getters/setters
resolveFakeOverride()?.backingField?.hasAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME) == true -> false
// We do not produce default accessors for private fields
@@ -256,13 +256,6 @@ val IrDeclaration.isStaticInlineClassReplacement: Boolean
get() = origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT
|| origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR
val IrDeclaration.isStaticMultiFieldValueClassReplacement: Boolean
get() = origin == JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT
|| origin == JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_CONSTRUCTOR
val IrDeclaration.isStaticValueClassReplacement: Boolean
get() = isStaticMultiFieldValueClassReplacement || isStaticInlineClassReplacement
// On the IR backend we represent raw types as star projected types with a special synthetic annotation.
// See `TypeTranslator.translateTypeAnnotations`.
private fun JvmBackendContext.makeRawTypeAnnotation() =
@@ -312,7 +305,7 @@ val IrClass.isSyntheticSingleton: Boolean
fun IrSimpleFunction.suspendFunctionOriginal(): IrSimpleFunction =
if (isSuspend &&
!isStaticValueClassReplacement &&
!isStaticInlineClassReplacement &&
!isOrOverridesDefaultParameterStub() &&
parentAsClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS
)
@@ -422,7 +422,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext, private val
// TODO: get rid of this (probably via some special lowering)
private fun mapOverriddenSpecialBuiltinIfNeeded(callee: IrFunction, superCall: Boolean): JvmMethodSignature? {
// Do not remap calls to static replacements of inline class methods, since they have completely different signatures.
if (callee.isStaticValueClassReplacement) return null
if (callee.isStaticInlineClassReplacement) return null
val overriddenSpecialBuiltinFunction =
(callee.toIrBasedDescriptor().getOverriddenBuiltinReflectingJvmDescriptor() as IrBasedSimpleFunctionDescriptor?)?.owner
if (overriddenSpecialBuiltinFunction != null && !superCall) {
@@ -53,13 +53,11 @@ fun <T : IrElement> IrStatementsBuilder<T>.irTemporary(
nameHint: String? = null,
irType: IrType = value?.type!!, // either value or irType should be supplied at callsite
isMutable: Boolean = false,
origin: IrDeclarationOrigin = IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
): IrVariable {
val temporary = scope.createTemporaryVariableDeclaration(
irType, nameHint, isMutable,
startOffset = startOffset,
endOffset = endOffset,
origin = origin,
endOffset = endOffset
)
value?.let { temporary.initializer = it }
+temporary
@@ -43,9 +43,6 @@ interface IrDeclarationOrigin {
object GENERATED_DATA_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_DATA_CLASS_MEMBER")
object GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER")
object GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER")
object GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER : IrDeclarationOriginImpl("GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER")
object TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE : IrDeclarationOriginImpl("TEMPORARY_MULTI_FIELD_VALUE_CLASS_VARIABLE")
object MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE : IrDeclarationOriginImpl("MULTI_FIELD_VALUE_CLASS_REPRESENTATION_VARIABLE")
object LOCAL_FUNCTION : IrDeclarationOriginImpl("LOCAL_FUNCTION")
object LOCAL_FUNCTION_FOR_LAMBDA : IrDeclarationOriginImpl("LOCAL_FUNCTION_FOR_LAMBDA")
object CATCH_PARAMETER : IrDeclarationOriginImpl("CATCH_PARAMETER")
@@ -128,7 +128,7 @@ abstract class DataClassMembersGenerator(
fun generateEqualsMethodBody(properties: List<IrProperty>) {
val irType = irClass.defaultType
if (!irClass.isValue) {
if (!irClass.isSingleFieldValueClass) {
+irIfThenReturnTrue(irEqeqeq(irThis(), irOther()))
}
+irIfThenReturnFalse(irNotIs(irOther(), irType))