diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 4644f9a722f..5a72e6ccdd6 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -50189,6 +50189,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/valueClasses/classFlattening.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } + @Test + @TestMetadata("complex.kt") + public void testComplex() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/complex.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("equality.kt") public void testEquality() throws Exception { @@ -50196,9 +50202,21 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT } @Test - @TestMetadata("simple.kt") - public void testSimple() throws Exception { - runTest("compiler/testData/codegen/box/valueClasses/simple.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + @TestMetadata("MFVCFieldInitializationOrder.kt") + public void testMFVCFieldInitializationOrder() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/MFVCFieldInitializationOrder.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("overrides.kt") + public void testOverrides() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/overrides.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("throwingMFVCReassignments.kt") + public void testThrowingMFVCReassignments() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/throwingMFVCReassignments.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } } diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java index ce3303b8985..0f99dc2174e 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.test.runners.codegen; import com.intellij.testFramework.TestDataPath; import org.jetbrains.kotlin.test.util.KtTestUtil; import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.utils.TransformersFunctions; import org.jetbrains.kotlin.test.TestMetadata; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -5779,6 +5780,46 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { } } + @Nested + @TestMetadata("compiler/testData/codegen/bytecodeText/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("equalsBoxTest.kt") + public void testEqualsBoxTest() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/valueClasses/equalsBoxTest.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("MFVCDeclaration.kt") + public void testMFVCDeclaration() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/valueClasses/MFVCDeclaration.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("MFVCReassignments.kt") + public void testMFVCReassignments() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/valueClasses/MFVCReassignments.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("passingMFVC2Functions.kt") + public void testPassingMFVC2Functions() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/valueClasses/passingMFVC2Functions.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("regularClassWithMFVC.kt") + public void testRegularClassWithMFVC() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/valueClasses/regularClassWithMFVC.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + } + @Nested @TestMetadata("compiler/testData/codegen/bytecodeText/varargs") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index 4aab44fbd66..e6ab5a6d4a0 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt @@ -113,7 +113,7 @@ internal val bridgePhase = makeIrFilePhase( ::BridgeLowering, name = "Bridge", description = "Generate bridges", - prerequisite = setOf(jvmInlineClassPhase, jvmMultiFieldValueClassPhase) + prerequisite = setOf(jvmInlineClassPhase, jvmMultiFieldValueClassPhase, inheritedDefaultMethodsOnClassesPhase) ) internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() { @@ -446,6 +446,11 @@ 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. @@ -501,6 +506,11 @@ 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 -> @@ -627,18 +637,36 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass ) = irCastIfNeeded( irCall(target, origin = IrStatementOrigin.BRIDGE_DELEGATION, superQualifierSymbol = superQualifierSymbol).apply { - val mfvcOrOriginal = - this@BridgeLowering.context.inlineClassReplacements.originalFunctionForMethodReplacement[target] ?: target - val structure = with(this@BridgeLowering.context.multiFieldValueClassReplacements) { - originalFunctionForMethodReplacement[mfvcOrOriginal] - ?.let { bindingParameterTemplateStructure[it] } + + val targetStructure = run { + val mfvcOrOriginal = + this@BridgeLowering.context.inlineClassReplacements.originalFunctionForMethodReplacement[target] ?: target + this@BridgeLowering.context.multiFieldValueClassReplacements + .bindingNewFunctionToParameterTemplateStructure[mfvcOrOriginal] ?.also { structure -> val errorMessage = { "Bad parameters structure: $structure" } - require(structure.size == bridge.explicitParametersCount) { errorMessage() } require(structure.sumOf { it.valueParameters.size } == target.explicitParametersCount) { errorMessage() } } } + val bridgeStructure = run { + val mfvcOrOriginal = + this@BridgeLowering.context.inlineClassReplacements.originalFunctionForMethodReplacement[bridge] ?: bridge + this@BridgeLowering.context.multiFieldValueClassReplacements + .bindingNewFunctionToParameterTemplateStructure[mfvcOrOriginal] + ?.also { structure -> + val errorMessage = { "Bad parameters structure: $structure" } + require(structure.sumOf { it.valueParameters.size } == bridge.explicitParametersCount) { errorMessage() } + } + } + + require(targetStructure == null || bridgeStructure == null || + bridgeStructure.size == targetStructure.size && + (targetStructure zip bridgeStructure).none { (targetParameter, bridgeParameter) -> + targetParameter is MultiFieldValueClassMapping && bridgeParameter is MultiFieldValueClassMapping && + targetParameter.declarations != bridgeParameter.declarations + }) { "Incompatible structures: $bridgeStructure and $targetStructure" } + fun irGetOrCast(bridgeParameter: IrValueParameter, targetParameter: IrValueParameter) = irGet(bridgeParameter).let { argument -> if (bridgeParameter == bridge.dispatchReceiverParameter) @@ -647,29 +675,69 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass irCastIfNeeded(argument, targetParameter.type.upperBound) } - val targetParameters = target.explicitParameters - val flattenedBridgeArguments = when (structure) { - null -> bridge.explicitParameters.zip(target.explicitParameters, ::irGetOrCast) - else -> { - var index = 0 - (structure zip bridge.explicitParameters).flatMap { (remappedParameter, bridgeParameter) -> - when (remappedParameter) { - is MultiFieldValueClassMapping -> remappedParameter.declarations.unboxMethods.map { unboxFunction -> + 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.declarations == targetRemappedParameter.declarations) { + "Incompatible parameters: $bridgeRemappedParameter, $targetRemappedParameter" + } + repeat(bridgeRemappedParameter.valueParameters.size) { + val bridgeParameter = bridgeExplicitParameters[bridgeIndex++] + val targetParameter = targetExplicitParameters[targetIndex++] + putArgument(targetParameter, irGetOrCast(bridgeParameter, targetParameter)) + } + } + is RegularMapping, null -> { + val bridgeParameter = bridgeExplicitParameters[bridgeIndex++] + val newArguments = targetRemappedParameter.declarations.unboxMethods.map { unboxFunction -> irCall(unboxFunction).apply { - dispatchReceiver = - irCastIfNeeded(irGet(bridgeParameter), remappedParameter.declarations.valueClass.defaultType) + val targetParameterType = targetRemappedParameter.declarations.valueClass.defaultType + dispatchReceiver = irCastIfNeeded(irGet(bridgeParameter), targetParameterType) } - }.also { index += it.size } - is RegularMapping -> listOf(irGetOrCast(bridgeParameter, targetParameters[index++])) + } + for (newArgument in newArguments) { + putArgument(targetExplicitParameters[targetIndex++], newArgument) + } } } + is RegularMapping, null -> { + val targetParameter = targetExplicitParameters[targetIndex] + when (bridgeRemappedParameter) { + is MultiFieldValueClassMapping -> { + val count = bridgeRemappedParameter.declarations.fields.size + val boxCall = irCall(bridgeRemappedParameter.declarations.boxMethod).apply { + for (i in 0 until count) { + putValueArgument(i, irGet(bridgeExplicitParameters[bridgeIndex++])) + } + } + putArgument(targetParameter, irCastIfNeeded(boxCall, targetParameter.type.upperBound)) + } + is RegularMapping, null -> { + val bridgeParameter = bridgeExplicitParameters[bridgeIndex++] + putArgument(targetParameter, irGetOrCast(bridgeParameter, targetParameter)) + } + } + targetIndex++ + } } + structureIndex++ } - require(targetParameters.size == flattenedBridgeArguments.size) { - "Incorrect number of arguments: ${flattenedBridgeArguments.size} instead of ${targetParameters.size}" + require(targetIndex == targetExplicitParameters.size && bridgeIndex == bridgeExplicitParameters.size) { + "Incorrect bridge:\n${bridge.dump()}\n\nfor target\n${target.dump()}" } - for ((targetParam, argument) in target.explicitParameters zip flattenedBridgeArguments) { - putArgument(targetParam, argument) + require((targetStructure == null || structureIndex == targetStructure.size)) { + "Invalid structure index $structureIndex for $targetStructure" + } + require((bridgeStructure == null || structureIndex == bridgeStructure.size)) { + "Invalid structure index $structureIndex for $bridgeStructure" } }, bridge.returnType.upperBound diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt index 120cae230f1..d166824b715 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt @@ -12,6 +12,9 @@ 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.builtins.StandardNames import org.jetbrains.kotlin.config.JvmDefaultMode @@ -100,15 +103,47 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo } passTypeArgumentsFrom(irFunction, offset = superMethod.parentAsClass.typeParameters.size) - var offset = 0 irFunction.dispatchReceiverParameter?.let { - putValueArgument( - offset++, - irGet(it).reinterpretAsDispatchReceiverOfType(superClassType) - ) + putValueArgument(0, irGet(it).reinterpretAsDispatchReceiverOfType(superClassType)) + } + val lowering = this@InheritedDefaultMethodsOnClassesLowering + val mfvcOrOriginal = lowering.context.inlineClassReplacements.originalFunctionForMethodReplacement[classOverride] + ?: classOverride + val bindingNewFunctionToParameterTemplateStructure = lowering.context.multiFieldValueClassReplacements + .bindingNewFunctionToParameterTemplateStructure + val structure = bindingNewFunctionToParameterTemplateStructure[mfvcOrOriginal]?.let { structure -> + val errorMessage = { "Bad parameters structure: $structure" } + require(structure.sumOf { it.valueParameters.size } == classOverride.explicitParametersCount) { errorMessage() } + if (defaultImplFun.explicitParametersCount == irFunction.explicitParametersCount) { + null + } else { + require(structure.size == defaultImplFun.explicitParametersCount) { errorMessage() } + structure + } + } + require(structure == null || structure.first() is RegularMapping) { + "Dispatch receiver for method replacement cannot be flattened" + } + val sourceFullValueParameterList = irFunction.fullValueParameterList + if (structure == null) { + sourceFullValueParameterList.forEachIndexed { index, parameter -> + putValueArgument(1 + index, irGet(parameter)) + } + } else { + var flattenedIndex = 0 + for (i in 1 until structure.size) { + when (val remappedParameter = structure[i]) { + is MultiFieldValueClassMapping -> + putValueArgument(i, irCall(remappedParameter.declarations.boxMethod).apply { + val boxArgumentsCount = remappedParameter.valueParameters.size + for (boxArgumentIndex in 0 until boxArgumentsCount) { + putValueArgument(boxArgumentIndex, irGet(sourceFullValueParameterList[flattenedIndex++])) + } + }) + is RegularMapping -> putValueArgument(i, irGet(sourceFullValueParameterList[flattenedIndex++])) + } + } } - irFunction.extensionReceiverParameter?.let { putValueArgument(offset++, irGet(it)) } - irFunction.valueParameters.mapIndexed { i, parameter -> putValueArgument(i + offset, irGet(parameter)) } } ) } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt index e0b8790b8fe..dfc239d8e5f 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.addConstructor +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.* @@ -29,6 +30,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.name.Name import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME val jvmInlineClassPhase = makeIrFilePhase( @@ -64,6 +66,21 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass } } + override fun createBridgeDeclaration(source: IrSimpleFunction, replacement: IrSimpleFunction, mangledName: Name): IrSimpleFunction = + context.irFactory.buildFun { + updateFrom(source) + name = mangledName + returnType = source.returnType + }.apply { + copyParameterDeclarationsFrom(source) + annotations = source.annotations + parent = source.parent + // We need to ensure that this bridge has the same attribute owner as its static inline class replacement, since this + // is used in [CoroutineCodegen.isStaticInlineClassReplacementDelegatingCall] to identify the bridge and avoid generating + // a continuation class. + copyAttributes(source) + } + override fun IrClass.isSpecificLoweringLogicApplicable(): Boolean = isSingleFieldValueClass override fun IrFunction.isFieldGetterToRemove(): Boolean = isInlineClassFieldGetter diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt index 98ff37a6efd..8cd2a01f6e9 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.backend.jvm.lower +import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom +import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom import org.jetbrains.kotlin.backend.common.lower.createIrBuilder @@ -21,22 +23,26 @@ import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassSpecificDeclarations import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassTree.InternalNode import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.backend.jvm.ir.isMultiFieldValueClassType +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.addGetter import org.jetbrains.kotlin.ir.builders.declarations.buildField +import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.declarations.buildProperty import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.symbols.IrValueSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl import org.jetbrains.kotlin.ir.transformStatement -import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.isNullable import org.jetbrains.kotlin.ir.types.makeNotNull import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.addToStdlib.popLast @@ -103,6 +109,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV private val regularClassMFVCPropertyNextGetters: MutableMap> = mutableMapOf() private val regularClassMFVCPropertyFieldsMapping: MutableMap> = mutableMapOf() private val regularClassMFVCPropertyNodes: MutableMap> = mutableMapOf() + private val regularClassMFVCPropertyAllPrimitiveGetters: MutableSet = mutableSetOf() override val replacements get() = context.multiFieldValueClassReplacements @@ -152,7 +159,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV visibility = sourceField.visibility }.apply { parent = declaration - initializer + initializer = null } } regularClassMFVCPropertyFieldsMapping[oldField] = newFields @@ -161,6 +168,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV type = newField.type, makeGetter = { receiver: IrValueDeclaration -> irGetField(irGet(receiver), newField) }, assigner = { receiver: IrValueDeclaration, value: IrExpression -> irSetField(irGet(receiver), newField, value) }, + symbol = null, ) } val implementationAgnostic = declarations.ImplementationAgnostic(virtualFields) @@ -202,6 +210,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV regularClassMFVCPropertyNextGetters[getter] = node.fields.associate { it.name to nodesToGetters[it.node]!! } } } + regularClassMFVCPropertyAllPrimitiveGetters.addAll(nodesToGetters.values) } } for (accessor in listOfNotNull(mainGetter, mainSetter)) @@ -214,7 +223,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV val gettersAndSetters = newFields.toGettersAndSetters(accessor.dispatchReceiverParameter!!, transformReceiver = true) val representation = newFields.zip(gettersAndSetters) { newField, (getter, setter) -> - VirtualProperty(newField.type, getter, setter) + VirtualProperty(newField.type, getter, setter, null) } val fieldRepresentation = declarations.ImplementationAgnostic(representation) val boxed = fieldRepresentation.boxedExpression( @@ -235,10 +244,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV parent = declaration body = context.createIrBuilder(this.symbol).irBlockBody { +irBlock { - flattenExpressionTo( - initializer.expression.transform(this@JvmMultiFieldValueClassLowering, null), - newFields.toGettersAndSetters(declaration.thisReceiver!!) - ) + flattenExpressionTo(initializer.expression, newFields.toGettersAndSetters(declaration.thisReceiver!!)) } } oldFieldToInitializers[oldField] = this @@ -317,40 +323,104 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } private fun MultiFieldValueClassSpecificDeclarations.replaceProperties() { - valueClass.declarations.removeIf { it is IrFunction && it.isFieldGetterToRemove() } + valueClass.declarations.removeAll(oldProperties.values.mapNotNull { it.getter }) properties.values.forEach { it.parent = valueClass } valueClass.declarations += properties.values.map { it.getter!!.apply { parent = valueClass } } } + override fun createBridgeDeclaration(source: IrSimpleFunction, replacement: IrSimpleFunction, mangledName: Name): IrSimpleFunction = + context.irFactory.buildFun { + updateFrom(source) + name = mangledName + returnType = source.returnType + }.apply { + val overriddenReplaced = replacement.overriddenSymbols.firstOrNull { + replacements.bindingNewFunctionToParameterTemplateStructure[it.owner] != null + }?.owner + if (source.isFakeOverride && source.parentAsClass.isMultiFieldValueClass && overriddenReplaced != null) { + copyParameterDeclarationsFrom(overriddenReplaced) + dispatchReceiverParameter = source.dispatchReceiverParameter!!.copyTo(this) + val replacementStructure = replacement.overriddenSymbols.firstNotNullOf { + replacements.bindingNewFunctionToParameterTemplateStructure[it.owner] + }.toMutableList().apply { + set(0, RegularMapping(ValueParameterTemplate(dispatchReceiverParameter!!, dispatchReceiverParameter!!.origin))) + } + replacements.bindingNewFunctionToParameterTemplateStructure[this] = replacementStructure + } else { + copyParameterDeclarationsFrom(source) + } + annotations = source.annotations + parent = source.parent + // We need to ensure that this bridge has the same attribute owner as its static inline class replacement, since this + // is used in [CoroutineCodegen.isStaticInlineClassReplacementDelegatingCall] to identify the bridge and avoid generating + // a continuation class. + copyAttributes(source) + } + override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) { allScopes.push(createScope(source)) source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run { + val sourceExplicitParameters = source.explicitParameters if (inverted) { irExprBody(irCall(target).apply { passTypeArgumentsFrom(source) - val structure: List = replacements.bindingParameterTemplateStructure[original]!! - var flattenedIndex = 0 - for ((remappedParameter, targetParameter) in structure zip target.explicitParameters) { - when (remappedParameter) { - is MultiFieldValueClassMapping -> putArgument( - targetParameter, - irCall(remappedParameter.declarations.boxMethod).apply { - source.explicitParameters - .slice(flattenedIndex until flattenedIndex + remappedParameter.valueParameters.size) - .forEachIndexed { index, boxParameter -> putValueArgument(index, irGet(boxParameter)) } - .also { flattenedIndex += remappedParameter.valueParameters.size } - }) - is RegularMapping -> putArgument(targetParameter, irGet(source.explicitParameters[flattenedIndex++])) + val targetExplicitParameters = target.explicitParameters + val originalStructure: List = replacements.bindingOldFunctionToParameterTemplateStructure[original]!! + val targetStructure = replacements.bindingNewFunctionToParameterTemplateStructure[target] + require( + when (targetStructure) { + null -> originalStructure.size == targetExplicitParameters.size + else -> originalStructure.size == targetStructure.size && + targetStructure.sumOf { it.valueParameters.size } == targetExplicitParameters.size + } + ) { + "Incompatible structures: $originalStructure, $targetStructure" + } + val structuresSizes = originalStructure.size + var flattenedSourceIndex = 0 + var flattenedTargetIndex = 0 + for (i in 0 until structuresSizes) { + val remappedOriginalParameter = originalStructure[i] + val remappedTargetParameter = targetStructure?.get(i) + when (remappedOriginalParameter) { + is MultiFieldValueClassMapping -> { + when (remappedTargetParameter) { + is MultiFieldValueClassMapping -> { + require(remappedTargetParameter.valueParameters.size == remappedOriginalParameter.valueParameters.size) { + "Incompatible structures: $remappedTargetParameter, $remappedOriginalParameter" + } + repeat(remappedTargetParameter.valueParameters.size) { + putArgument( + targetExplicitParameters[flattenedTargetIndex++], + irGet(sourceExplicitParameters[flattenedSourceIndex++]) + ) + } + } + is RegularMapping, null -> + putArgument( + targetExplicitParameters[flattenedTargetIndex++], + irCall(remappedOriginalParameter.declarations.boxMethod).apply { + sourceExplicitParameters + .slice(flattenedSourceIndex until flattenedSourceIndex + remappedOriginalParameter.valueParameters.size) + .forEachIndexed { index, boxParameter -> putValueArgument(index, irGet(boxParameter)) } + .also { flattenedSourceIndex += remappedOriginalParameter.valueParameters.size } + }) + } + } + is RegularMapping -> putArgument( + targetExplicitParameters[flattenedTargetIndex++], + irGet(sourceExplicitParameters[flattenedSourceIndex++]) + ) } } }) } else { irExprBody(irCall(original).apply { // not target as it will be replaced during lowering passTypeArgumentsFrom(source) - for ((parameter, newParameter) in source.explicitParameters.zip(original.explicitParameters)) { + for ((parameter, newParameter) in sourceExplicitParameters.zip(original.explicitParameters)) { putArgument(newParameter, irGet(parameter)) } }).transform(this@JvmMultiFieldValueClassLowering, null) @@ -360,7 +430,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } override fun addBindingsFor(original: IrFunction, replacement: IrFunction) { - val parametersStructure = replacements.bindingParameterTemplateStructure[original]!! + val parametersStructure = replacements.bindingOldFunctionToParameterTemplateStructure[original]!! require(parametersStructure.size == original.explicitParameters.size) { "Wrong value parameters structure: $parametersStructure" } @@ -462,7 +532,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } else -> when (val newConstructor = (function as? IrConstructor)?.let { replacements.getReplacementRegularClassConstructor(it) }) { - null -> super.visitFunctionAccess(expression) + null -> return super.visitFunctionAccess(expression) else -> context.createIrBuilder(currentScope.symbol).irBlock { buildReplacement(function, expression, newConstructor) } @@ -472,21 +542,27 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV override fun visitCall(expression: IrCall): IrExpression { val callee = expression.symbol.owner - val receiver = expression.dispatchReceiver?.transform(this, null) + val oldReceiver = expression.dispatchReceiver val name = callee.correspondingPropertySymbol?.owner?.name - if (callee.isMultiFieldValueClassOriginalFieldGetter && receiver != null) { + if (callee.isMultiFieldValueClassOriginalFieldGetter && oldReceiver != null) { with(valueDeclarationsRemapper) { with(context.createIrBuilder(expression.symbol)) { - subfield(receiver, name!!)?.let { return it } - } - } - } - if (callee.isGetter && name != null && receiver is IrCall) { - val nextFunction = regularClassMFVCPropertyNextGetters[receiver.symbol.owner]?.get(name) - if (nextFunction != null) { - return with(context.createIrBuilder(expression.symbol)) { - irCall(nextFunction).apply { - this.dispatchReceiver = receiver.dispatchReceiver + val newReceiver = oldReceiver.transform(this@JvmMultiFieldValueClassLowering, null) + if (newReceiver is IrCall) { + val nextFunction = regularClassMFVCPropertyNextGetters[newReceiver.symbol.owner]?.get(name) + if (nextFunction != null) { + return irCall(nextFunction).apply { + this.dispatchReceiver = newReceiver.dispatchReceiver + } + } + } + subfield(newReceiver, name!!)?.let { return it } + return expression.apply { + dispatchReceiver = newReceiver + extensionReceiver = extensionReceiver?.transform(this@JvmMultiFieldValueClassLowering, null) + for (i in 0 until valueArgumentsCount) { + putValueArgument(i, getValueArgument(i)?.transform(this@JvmMultiFieldValueClassLowering, null)) + } } } } @@ -511,7 +587,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } else { val equals = leftClass.functions.single { it.name.asString() == "equals" && it.overriddenSymbols.isNotEmpty() } return super.visitCall(context.createIrBuilder(expression.symbol).run { - irCall(equals).apply { + irCall(equals).apply { copyTypeArgumentsFrom(expression) dispatchReceiver = leftArgument putValueArgument(0, rightArgument) @@ -520,16 +596,16 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } } else if (rightImplementation != null) { if (leftArgument.isNullConst()) { - return context.createIrBuilder(expression.symbol).irBlock { + return context.createIrBuilder(expression.symbol).irBlock { +rightArgument +irFalse() }.transform(this, null) } - if (leftArgument.type.classOrNull == rightArgument.type.classOrNull && leftArgument.type.isNullable()) { + if (leftArgument.type.erasedUpperBound == rightArgument.type.erasedUpperBound && leftArgument.type.isNullable()) { return context.createIrBuilder(expression.symbol).irBlock { val leftValue = irTemporary(leftArgument) +irIfNull(context.irBuiltIns.booleanType, irGet(leftValue), irFalse(), irBlock { - val nonNullLeftArgumentVariable = + val nonNullLeftArgumentVariable = irTemporary(irImplicitCast(irGet(leftValue), leftArgument.type.makeNotNull())) +irCall(context.irBuiltIns.eqeqSymbol).apply { putValueArgument(0, irGet(nonNullLeftArgumentVariable)) @@ -556,7 +632,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV replacement: IrFunction ) { val parameter2expression = typedArgumentList(originalFunction, original) - val structure = replacements.bindingParameterTemplateStructure[originalFunction]!! + val structure = replacements.bindingOldFunctionToParameterTemplateStructure[originalFunction]!! require(parameter2expression.size == structure.size) require(structure.sumOf { it.valueParameters.size } == replacement.explicitParametersCount) val newArguments: List = @@ -570,22 +646,72 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } } + override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression { + for (i in expression.arguments.indices) { + val argument = expression.arguments[i] + if (!argument.type.isNullable() && argument.type.isMultiFieldValueClassType()) { + expression.arguments[i] = context.createIrBuilder((currentScope!!.irElement as IrSymbolOwner).symbol).run { + val toString = argument.type.erasedUpperBound.functions.single { + it.name.asString() == "toString" && it.valueParameters.isEmpty() + } + irCall(toString).apply { + dispatchReceiver = argument + } + } + } + } + return super.visitStringConcatenation(expression) + } + private fun IrBlockBuilder.makeNewArguments( oldArguments: List, structure: List> ): List { - val variables = structure.flatMap { argTemplate -> argTemplate.map { irTemporary(irType = it.type) } } - val subVariables = structure.scan(0) { acc: Int, templates: List -> acc + templates.size } - .zipWithNext().map { (start, finish) -> variables.slice(start until finish) } - return (oldArguments zip subVariables).flatMap { (oldArgument, curSubVariables) -> - val oldArgumentTransformed = oldArgument?.transform(this@JvmMultiFieldValueClassLowering, null) - when { - oldArgumentTransformed == null -> List(curSubVariables.size) { null } - curSubVariables.size == 1 -> listOf(oldArgumentTransformed) - else -> flattenExpressionTo(oldArgumentTransformed, curSubVariables.toGettersAndSetters()) - .let { curSubVariables.map { irGet(it) } } + + var variables: List = listOf() + var subVariables: List> = listOf() + val declareVariables = irComposite { + variables = structure.flatMap { argTemplate -> argTemplate.map { irTemporary(irType = it.type) } } + subVariables = structure.scan(0) { acc: Int, templates: List -> acc + templates.size } + .zipWithNext().map { (start, finish) -> variables.slice(start until finish) } + } + val initializeVariables = irBlock { + (oldArguments zip subVariables).forEach { (oldArgument, curSubVariables) -> + when { + oldArgument == null -> Unit + curSubVariables.size == 1 -> { + val curSubVariable = curSubVariables.single() + +irSet(curSubVariable, oldArgument.transform(this@JvmMultiFieldValueClassLowering, null)) + } + else -> flattenExpressionTo(oldArgument, curSubVariables.toGettersAndSetters()) + } } } + val variablesSymbols2Index = variables.mapIndexed { index, variable -> variable.symbol to index }.toMap() + val inlinedArguments = run { + val result = mutableMapOf() + var lastIndex = -1 // we need to assign them in the same order + for (statement in initializeVariables.statements) { + if (statement !is IrSetValue) return@run null + val index = variablesSymbols2Index[statement.symbol] ?: return@run null + if (lastIndex >= index) return@run null + lastIndex = index + result[statement.symbol] = statement.value + } + result + } + if (inlinedArguments != null) { + return variables.map { inlinedArguments[it.symbol] } + } + +declareVariables + +initializeVariables + val newArguments: List = (oldArguments zip subVariables).flatMap { (oldArgument, curSubVariables) -> + when (oldArgument) { + null -> List(curSubVariables.size) { null } + else -> curSubVariables.map { irGet(it) } + } + } + return newArguments } // Note that reference equality (x === y) is not allowed on values of MFVC class type, @@ -593,7 +719,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV private val IrCall.isSpecializedMFVCEqEq: Boolean get() = symbol == context.irBuiltIns.eqeqSymbol && listOf(getValueArgument(0)!!, getValueArgument(1)!!) - .any { it.type.classOrNull?.owner?.takeIf { it.isMultiFieldValueClass } != null } + .any { it.type.erasedUpperBound.takeIf { it.isMultiFieldValueClass } != null } override fun visitGetField(expression: IrGetField): IrExpression { val field = expression.symbol.owner @@ -630,11 +756,14 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV val replacementFields = regularClassMFVCPropertyFieldsMapping[field] ?: return super.visitSetField(expression) return context.createIrBuilder(expression.symbol).irBlock { val thisVar = irTemporary(expression.receiver!!.transform(this@JvmMultiFieldValueClassLowering, null)) - val variables = replacementFields.map { irTemporary(irType = it.type, nameHint = it.name.asString()) } // We flatten to temp variables because code can throw an exception otherwise and partially update variables - flattenExpressionTo(expression.value.transform(this@JvmMultiFieldValueClassLowering, null), variables.toGettersAndSetters()) - for ((replacementField, variable) in replacementFields zip variables) { - +irSetField(irGet(thisVar), replacementField, irGet(variable)) + val subValues = flattenExpressionToGetters( + expression = expression.value, // not modified + types = replacementFields.map { it.type }, + additionalConditionForValue = { (_, value) -> value is IrGetValue } + ) + for ((replacementField, subValue) in replacementFields zip subValues) { + +irSetField(irGet(thisVar), replacementField, subValue) } } } @@ -649,11 +778,19 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV val setters = valueDeclarationsRemapper.setter(expression.symbol) ?: return super.visitSetValue(expression) return context.createIrBuilder(expression.symbol).irBlock { val declarations = replacements.getDeclarations(expression.symbol.owner.type.erasedUpperBound)!! - val variables = declarations.leaves.map { irTemporary(irType = it.type) } // We flatten to temp variables because code can throw an exception otherwise and partially update variables - flattenExpressionTo(expression.value.transform(this@JvmMultiFieldValueClassLowering, null), variables.toGettersAndSetters()) - for ((setter, variable) in setters zip variables) { - setter?.invoke(this, Unit, irGet(variable))?.let { +it } + val modifiedExpression = expression.value.transform(this@JvmMultiFieldValueClassLowering, null) + val currentDeclarationReplacementSymbols = valueDeclarationsRemapper.implementationAgnostic(modifiedExpression)?.symbols + ?.mapIndexedNotNull { index, symbol -> symbol?.to(index) }?.toMap() ?: emptyMap() + val subValues = flattenExpressionToGetters( + expression = expression.value, // not modified + types = declarations.leaves.map { it.type }, + additionalConditionForValue = { (index, value) -> + value is IrGetValue && currentDeclarationReplacementSymbols[value.symbol].let { it == null || it <= index } + } + ) + for ((setter, subValue) in setters zip subValues) { + setter?.invoke(this, Unit, subValue)?.let { +it } } } } @@ -706,22 +843,40 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV ) } + // expression takes not transformed first argument + private fun IrBlockBuilder.flattenExpressionToGetters( + expression: IrExpression, + types: List, + additionalConditionForValue: (IndexedValue) -> Boolean, + ): List { + val variables = types.map { irTemporary(irType = it) } + val temporaryBlock = irBlock { + flattenExpressionTo(expression, variables.toGettersAndSetters()) + } + val satisfyingSetVariableSymbols = temporaryBlock.statements.mapIndexed { index, blockStatement -> + (blockStatement as? IrSetValue)?.takeIf { additionalConditionForValue(IndexedValue(index, it.value)) }?.symbol + } + return if (variables.map { it.symbol } == satisfyingSetVariableSymbols) { + temporaryBlock.statements.map { (it as IrSetValue).value } + } else { + +temporaryBlock + variables.map { irGet(it) } + } + } + fun IrBlockBuilder.flattenExpressionTo( expression: IrExpression, variables: List, ExpressionSupplier>> ) { - valueDeclarationsRemapper.implementationAgnostic(expression)?.virtualFields?.map { it.makeGetter(this, Unit) }?.let { - require(variables.size == it.size) - for ((variable, subExpression) in variables zip it) { - +variable.second(this, Unit, subExpression) - } - return + val declarations = replacements.getDeclarations(expression.type.erasedUpperBound) ?: run { + val constructor = (expression as? IrConstructorCall)?.symbol?.owner ?: return@run null + replacements.getDeclarations(constructor.constructedClass) } - if (expression.type.isNullable() || !expression.type.isMultiFieldValueClassType()) { + + if (expression.type.isNullable() || declarations == null) { require(variables.size == 1) +variables.single().second(this, Unit, expression.transform(this@JvmMultiFieldValueClassLowering, null)) return } - val declarations = replacements.getDeclarations(expression.type.erasedUpperBound)!! require(variables.size == declarations.leaves.size) if (expression is IrConstructorCall) { val constructor = expression.symbol.owner @@ -734,11 +889,11 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV var curOffset = 0 for ((treeField, argument) in root.fields zip oldArguments) { val size = when (treeField.node) { - is InternalNode -> replacements.getDeclarations(treeField.node.irClass!!)!!.leaves.size + is InternalNode -> replacements.getDeclarations(treeField.node.irClass)!!.leaves.size is MultiFieldValueClassTree.Leaf -> 1 } val subVariables = variables.slice(curOffset until (curOffset + size)).also { curOffset += size } - argument?.let { flattenExpressionTo(it, subVariables) } ?: List(size) { null } + argument?.let { flattenExpressionTo(it, subVariables) } } +irCall(declarations.primaryConstructorImpl).apply { variables.forEachIndexed { index, variable -> putValueArgument(index, variable.first(this@flattenExpressionTo, Unit)) } @@ -747,6 +902,13 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } } val transformedExpression = expression.transform(this@JvmMultiFieldValueClassLowering, null) + valueDeclarationsRemapper.implementationAgnostic(transformedExpression)?.virtualFields?.map { it.makeGetter(this, Unit) }?.let { + require(variables.size == it.size) + for ((variable, subExpression) in variables zip it) { + +variable.second(this, Unit, subExpression) + } + return + } if (transformedExpression is IrCall) { val callee = transformedExpression.symbol.owner if (callee == declarations.boxMethod) { @@ -763,10 +925,10 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV return } } - (transformedExpression as? IrCall)?.let { makeLeavesGetters(it.symbol.owner) }?.let { geters -> + (transformedExpression as? IrCall)?.let { makeLeavesGetters(it.symbol.owner) }?.let { getters -> val receiver = irTemporary(transformedExpression.dispatchReceiver) - require(geters.size == variables.size) { "Number of getters must be equal to number of variables" } - for ((variable, getter) in variables zip geters) { + require(getters.size == variables.size) { "Number of getters must be equal to number of variables" } + for ((variable, getter) in variables zip getters) { +variable.second(this, Unit, irCall(getter).apply { dispatchReceiver = irGet(receiver) }) } return @@ -785,43 +947,72 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } } - override fun visitStatementContainer(container: IrStatementContainer) { - super.visitStatementContainer(container) - deleteUselessBoxes(container) + override fun visitContainerExpression(expression: IrContainerExpression): IrExpression { + return super.visitContainerExpression(expression).apply { expression.accept(ExtraBoxesRemover(), false) } } - private fun deleteUselessBoxes(container: IrStatementContainer) { - val statements = container.statements + override fun visitBlockBody(body: IrBlockBody): IrBody { + return super.visitBlockBody(body).apply { body.accept(ExtraBoxesRemover(), true) } + } + + private val visitedContainersLastStatement = mutableMapOf() + + private inner class ExtraBoxesRemover : IrElementVisitor { // Removing box-impl's but not getters because they are auto-generated - fun IrExpression.isBoxCallStatement() = this is IrCall && valueDeclarationsRemapper.implementationAgnostic(this) != null && + private fun IrExpression.isBoxCallStatement() = this is IrCall && valueDeclarationsRemapper.implementationAgnostic(this) != null && List(valueArgumentsCount) { getValueArgument(it) }.all { it == null || it is IrGetField || it is IrGetValue } - fun IrStatement.coercionToUnitArgument() = + private fun IrStatement.coercionToUnitArgument() = (this as? IrTypeOperatorCall)?.takeIf { it.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT }?.argument - fun IrStatement.isBoxCallStatement(): Boolean { + private fun IrStatement.isBoxCallStatement(): Boolean { val coercionToUnitArgument = coercionToUnitArgument() if (coercionToUnitArgument?.isBoxCallStatement() == true || this is IrExpression && this.isBoxCallStatement()) { return true } - if (coercionToUnitArgument is IrStatementContainer && (coercionToUnitArgument.statements.lastOrNull() as? IrExpression)?.isBoxCallStatement() == true) { - coercionToUnitArgument.statements.popLast() + if (coercionToUnitArgument is IrStatementContainer) { + coercionToUnitArgument.accept(this@ExtraBoxesRemover, true) } return false } - if (statements.isEmpty()) return + override fun visitElement(element: IrElement, data: Boolean) = Unit - val last = statements.popLast() - - statements.removeIf { it.isBoxCallStatement() } - for (statement in statements) { - if (statement is IrStatementContainer && statement.statements.lastOrNull()?.isBoxCallStatement() == true) { - statement.statements.removeLast() - } + override fun visitContainerExpression(expression: IrContainerExpression, data: Boolean) { + visitStatementContainer(expression, data) + } + + override fun visitBlockBody(body: IrBlockBody, data: Boolean) { + visitStatementContainer(body, true) // we last body statement is always not used. + } + + private fun visitStatementContainer(expression: IrStatementContainer, isInnerContainer: Boolean) { + visitedContainersLastStatement[expression]?.let { visitedLastStatement -> + if (!visitedLastStatement && isInnerContainer) { + visitedContainersLastStatement[expression] = true + expression.statements.lastOrNull()?.let { + if (it.isBoxCallStatement()) { + expression.statements.popLast() + } else { + it.accept(this, true) + } + } + } + return + } + visitedContainersLastStatement[expression] = isInnerContainer + val statementsToRemove = mutableListOf() + for (i in 0 until expression.statements.size) { + val isInnerStatement = isInnerContainer || i < expression.statements.lastIndex + if (isInnerStatement && expression.statements[i].isBoxCallStatement()) { + statementsToRemove.add(expression.statements[i]) + } else { + expression.statements[i].accept(this, isInnerStatement) + } + } + expression.statements.removeAll(statementsToRemove) } - statements.add(last) // we don't check delete it anyway } private fun IrSimpleFunction.isDefaultGetter(field: IrField): Boolean = diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt index 08d7305cd30..8b896cf7e84 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt @@ -7,14 +7,11 @@ 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.ir.copyParameterDeclarationsFrom 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.ir.isInlineClassType import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* @@ -139,7 +136,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon return super.visitReturn(expression) } - protected open fun visitStatementContainer(container: IrStatementContainer) { + private fun visitStatementContainer(container: IrStatementContainer) { container.statements.transformFlat { statement -> if (statement is IrFunction) transformFunctionFlat(statement) @@ -148,12 +145,12 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon } } - final override fun visitContainerExpression(expression: IrContainerExpression): IrExpression { + override fun visitContainerExpression(expression: IrContainerExpression): IrExpression { visitStatementContainer(expression) return expression } - final override fun visitBlockBody(body: IrBlockBody): IrBody { + override fun visitBlockBody(body: IrBlockBody): IrBody { visitStatementContainer(body) return body } @@ -173,6 +170,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon ): 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) && @@ -202,9 +200,10 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon return bridgeFunction } - private fun IrSimpleFunction.signatureRequiresMangling() = - fullValueParameterList.any { it.type.getRequiresMangling() } || - context.state.functionsWithInlineClassReturnTypesMangled && returnType.getRequiresMangling() && returnType.isInlineClassType() + 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 }, @@ -215,20 +214,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon // 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 - returnType = source.returnType - }.apply { - copyParameterDeclarationsFrom(source) - annotations = source.annotations - parent = source.parent - // We need to ensure that this bridge has the same attribute owner as its static inline class replacement, since this - // is used in [CoroutineCodegen.isStaticInlineClassReplacementDelegatingCall] to identify the bridge and avoid generating - // a continuation class. - copyAttributes(source) - } + abstract fun createBridgeDeclaration(source: IrSimpleFunction, replacement: IrSimpleFunction, mangledName: Name): IrSimpleFunction protected abstract fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt index 0ee749810f4..b3f88dae703 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmCachedDeclarations.kt @@ -251,7 +251,8 @@ class JvmCachedDeclarations( defaultImplsRedirections.getOrPut(fakeOverride) { assert(fakeOverride.isFakeOverride) val irClass = fakeOverride.parentAsClass - context.irFactory.buildFun { + val mfvcReplacementStructure = context.multiFieldValueClassReplacements.bindingNewFunctionToParameterTemplateStructure + val redirectFunction = context.irFactory.buildFun { origin = JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE name = fakeOverride.name visibility = fakeOverride.visibility @@ -275,6 +276,8 @@ class JvmCachedDeclarations( annotations = fakeOverride.annotations copyCorrespondingPropertyFrom(fakeOverride) } + mfvcReplacementStructure[fakeOverride]?.let { mfvcReplacementStructure[redirectFunction] = it } + redirectFunction } fun getRepeatedAnnotationSyntheticContainer(annotationClass: IrClass): IrClass = diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt index 3ad32b01c06..d2606fe77e6 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt @@ -7,9 +7,7 @@ package org.jetbrains.kotlin.backend.jvm import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom -import org.jetbrains.kotlin.backend.jvm.ir.extensionReceiverName -import org.jetbrains.kotlin.backend.jvm.ir.isMultiFieldValueClassType -import org.jetbrains.kotlin.backend.jvm.ir.isStaticValueClassReplacement +import org.jetbrains.kotlin.backend.jvm.ir.* import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor import org.jetbrains.kotlin.ir.declarations.* @@ -17,7 +15,6 @@ import org.jetbrains.kotlin.ir.expressions.IrExpressionBody import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrTypeSystemContext -import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.isNullable import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name @@ -61,8 +58,17 @@ class MemoizedMultiFieldValueClassReplacements( val type: IrType, val origin: IrDeclarationOrigin?, val defaultValue: IrExpressionBody?, - val original: IrValueParameter, + val original: IrValueParameter, // todo don't use it as it is not always correct ) { + + constructor(valueParameter: IrValueParameter, origin: IrDeclarationOrigin?) : this( + name = valueParameter.name.asString(), + type = valueParameter.type, + origin = origin, + defaultValue = valueParameter.defaultValue, + original = valueParameter, + ) + fun toParameter(irFunction: IrFunction, index: Int): IrValueParameter = original.copyTo( irFunction = irFunction, index = index, @@ -83,7 +89,7 @@ class MemoizedMultiFieldValueClassReplacements( private fun List.grouped( originWhenFlattenedAndNotSpecified: IrDeclarationOrigin? = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER ): List = map { parameter -> - val declarations = parameter.type.takeIf { !it.isNullable() }?.getClass()?.let { getDeclarations(it) } + val declarations = parameter.type.takeIf { !it.isNullable() }?.erasedUpperBound?.let { getDeclarations(it) } ?: return@map RemappedParameter.RegularMapping(parameter) require(!parameter.original.hasDefaultValue()) { "Default parameters values are not supported for multi-field value classes" } RemappedParameter.MultiFieldValueClassMapping(declarations, declarations.leaves.map { leaf -> @@ -150,13 +156,7 @@ class MemoizedMultiFieldValueClassReplacements( newFlattenedParameters.addAll(listOf(template).grouped()) } newFlattenedParameters += function.valueParameters.drop(function.contextReceiverParametersCount).map { - ValueParameterTemplate( - name = it.name.asString(), - type = it.type, - defaultValue = it.defaultValue, - original = it, - origin = null, - ) + ValueParameterTemplate(it, origin = null) }.grouped() return newFlattenedParameters } @@ -174,17 +174,19 @@ class MemoizedMultiFieldValueClassReplacements( require(valueParameters.size > 1) { "MFVC must have > 1 fields" } } } - + abstract val valueParameters: List } - val bindingParameterTemplateStructure: MutableMap> = ConcurrentHashMap() + val bindingOldFunctionToParameterTemplateStructure: MutableMap> = ConcurrentHashMap() + val bindingNewFunctionToParameterTemplateStructure: MutableMap> = ConcurrentHashMap() override fun createStaticReplacement(function: IrFunction): IrSimpleFunction = buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT, noFakeOverride = true) { originalFunctionForStaticReplacement[this] = function val newFlattenedParameters = makeGroupedValueParametersFrom(function, includeDispatcherReceiver = true) - bindingParameterTemplateStructure[function] = newFlattenedParameters + bindingOldFunctionToParameterTemplateStructure[function] = newFlattenedParameters + bindingNewFunctionToParameterTemplateStructure[this] = newFlattenedParameters valueParameters = makeValueParametersFromTemplate(newFlattenedParameters) } @@ -193,22 +195,18 @@ class MemoizedMultiFieldValueClassReplacements( dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this, index = -1) val newFlattenedParameters = makeGroupedValueParametersFrom(function, includeDispatcherReceiver = false) val receiver = dispatchReceiverParameter - val receiverTemplate = if (receiver != null) ValueParameterTemplate( - name = receiver.name.asString(), - type = receiver.type, - origin = receiver.origin, - defaultValue = receiver.defaultValue, - original = receiver, - ) else null - bindingParameterTemplateStructure[function] = + val receiverTemplate = if (receiver != null) ValueParameterTemplate(receiver, origin = receiver.origin) else null + val remappedParameters = if (receiverTemplate != null) listOf(RemappedParameter.RegularMapping(receiverTemplate)) + newFlattenedParameters else newFlattenedParameters + bindingOldFunctionToParameterTemplateStructure[function] = remappedParameters + bindingNewFunctionToParameterTemplateStructure[this] = remappedParameters valueParameters = makeValueParametersFromTemplate(newFlattenedParameters) } private fun createConstructorReplacement(@Suppress("UNUSED_PARAMETER") constructor: IrConstructor): IrConstructor { val newFlattenedParameters = makeGroupedValueParametersFrom(constructor, includeDispatcherReceiver = false) - bindingParameterTemplateStructure[constructor] = newFlattenedParameters - return irFactory.buildConstructor { + bindingOldFunctionToParameterTemplateStructure[constructor] = newFlattenedParameters + val newConstructor = irFactory.buildConstructor { updateFrom(constructor) returnType = constructor.returnType }.apply { @@ -218,6 +216,8 @@ class MemoizedMultiFieldValueClassReplacements( parent = constructor.parent originalConstructorForConstructorReplacement[this] = constructor } + bindingNewFunctionToParameterTemplateStructure[newConstructor] = newFlattenedParameters + return newConstructor } private fun createGetterReplacement(function: IrFunction): IrSimpleFunction { @@ -227,14 +227,10 @@ class MemoizedMultiFieldValueClassReplacements( val newGetter = declarations.properties[node]!!.getter!! originalFunctionForMethodReplacement[newGetter] = function val receiver = function.dispatchReceiverParameter!! - val receiverTemplate = ValueParameterTemplate( - name = receiver.name.asString(), - type = receiver.type, - origin = receiver.origin, - defaultValue = receiver.defaultValue, - original = receiver - ) - bindingParameterTemplateStructure[function] = listOf(RemappedParameter.RegularMapping(receiverTemplate)) + val receiverTemplate = ValueParameterTemplate(receiver, origin = receiver.origin) + val parameterRemapping = listOf(RemappedParameter.RegularMapping(receiverTemplate)) + bindingOldFunctionToParameterTemplateStructure[function] = parameterRemapping + bindingNewFunctionToParameterTemplateStructure[newGetter] = parameterRemapping return newGetter } @@ -261,7 +257,9 @@ class MemoizedMultiFieldValueClassReplacements( createStaticReplacement(function) } function is IrSimpleFunction && !function.isFromJava() && - function.fullValueParameterList.any { it.type.isMultiFieldValueClassType() && !it.type.isNullable() } -> + function.fullValueParameterList.any { it.type.isMultiFieldValueClassType() && !it.type.isNullable() } && + !(function.isFakeOverride && + bindingOldFunctionToParameterTemplateStructure[findSuperDeclaration(function, false, context.state.jvmDefaultMode)] == null)-> createMethodReplacement(function) else -> null } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MultiFieldValueClassSpecificDeclarations.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MultiFieldValueClassSpecificDeclarations.kt index de691750445..245f00ee699 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MultiFieldValueClassSpecificDeclarations.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MultiFieldValueClassSpecificDeclarations.kt @@ -26,7 +26,11 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrGetField -import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.symbols.IrValueSymbol +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.IrTypeSystemContext +import org.jetbrains.kotlin.ir.types.isNullable +import org.jetbrains.kotlin.ir.types.isSubtypeOf import org.jetbrains.kotlin.ir.util.constructedClass import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.defaultType @@ -36,8 +40,8 @@ import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver sealed class MultiFieldValueClassTree { abstract val type: IrType - val irClass: IrClass? - get() = type.getClass() + val irClass: IrClass + get() = type.erasedUpperBound companion object { @JvmStatic @@ -56,7 +60,7 @@ sealed class MultiFieldValueClassTree { fun create(type: IrType, replacements: MemoizedMultiFieldValueClassReplacements) = create(type, Unit, Unit, replacements) } - data class Leaf(override val type: IrType, val leafValue: TL) : MultiFieldValueClassTree() { + class Leaf(override val type: IrType, val leafValue: TL) : MultiFieldValueClassTree() { init { require(type.isNullable() || !type.isMultiFieldValueClassType()) } @@ -64,7 +68,7 @@ sealed class MultiFieldValueClassTree { fun map(transformLeaf: Leaf.(TL) -> RL) = Leaf(type, transformLeaf(leafValue)) } - data class InternalNode internal constructor( + class InternalNode internal constructor( override val type: IrType, val internalNodeValue: TI, val fields: List> ) : MultiFieldValueClassTree() { constructor( @@ -84,7 +88,7 @@ sealed class MultiFieldValueClassTree { } }) - data class TreeField( + class TreeField( val name: Name, val type: IrType, val visibility: DescriptorVisibility, val annotations: List, val node: MultiFieldValueClassTree ) { @@ -192,7 +196,7 @@ class MultiFieldValueClassSpecificDeclarations( private fun makeName() = Name.guessByFirstCharacter(parts.joinToString("$")) }) } - + init { require(nodeFullNames.size == nodeFullNames.values.distinct().size) { "Ambiguous names found: ${nodeFullNames.values}" } } @@ -219,6 +223,9 @@ class MultiFieldValueClassSpecificDeclarations( } val oldPrimaryConstructor = valueClass.primaryConstructor ?: error("Value classes have primary constructors") + private val oldFields = replacements.getOldFields(valueClass) + + val oldProperties = oldFields.map { it.correspondingPropertySymbol!!.owner }.associateBy { it.name } val fields = leaves.map { leaf -> irFactory.buildField { @@ -279,6 +286,7 @@ class MultiFieldValueClassSpecificDeclarations( type = it.type, makeGetter = { receiver: IrValueParameter -> irGetField(irGet(receiver), it) }, assigner = { receiver: IrValueParameter, value: IrExpression -> irSetField(irGet(receiver), it, value) }, + symbol = null, ) } ) @@ -377,17 +385,21 @@ class MultiFieldValueClassSpecificDeclarations( val properties: Map, IrProperty> = selfImplementationAgnosticDeclarations.nodeToExpressionGetters.filterKeys { it in nodeFullNames }.mapValues { (node, getter) -> + val propertyName = nodeFullNames[node]!! + val overrideable = oldProperties[propertyName] irFactory.buildProperty { - name = nodeFullNames[node]!! + name = propertyName origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER visibility = gettersVisibilities[node]!! }.apply { annotations = gettersAnnotations[node]!! + overrideable?.overriddenSymbols?.let { overriddenSymbols = it } parent = valueClass addGetter { returnType = node.type origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER }.apply { + overrideable?.getter?.overriddenSymbols?.let { overriddenSymbols = it } createDispatchReceiverParameter() body = with(context.createIrBuilder(this.symbol)) { irExprBody(getter(this, dispatchReceiverParameter!!)) @@ -396,11 +408,17 @@ class MultiFieldValueClassSpecificDeclarations( } } - data class VirtualProperty(val type: IrType, val makeGetter: ExpressionGenerator, val assigner: ExpressionSupplier?) { + data class VirtualProperty( + val type: IrType, + val makeGetter: ExpressionGenerator, + val assigner: ExpressionSupplier?, + val symbol: IrValueSymbol? + ) { constructor(declaration: IrValueDeclaration) : this( declaration.type, { irGet(declaration) }, if (declaration.isAssignable) { _, value -> irSet(declaration, value) } else null, + declaration.symbol, ) val isAssignable: Boolean @@ -410,6 +428,7 @@ class MultiFieldValueClassSpecificDeclarations( type = type, makeGetter = { makeGetter(f(it)) }, assigner = assigner?.let { assigner -> { additionalParam, value -> assigner(f(additionalParam), value) } }, + symbol = symbol, ) } @@ -427,7 +446,8 @@ class MultiFieldValueClassSpecificDeclarations( */ inner class ImplementationAgnostic(val virtualFields: List>) { val regularDeclarations = this@MultiFieldValueClassSpecificDeclarations - + val symbols = virtualFields.map { it.symbol } + fun map(f: (R) -> T) = ImplementationAgnostic(virtualFields.map { it.map(f) }) init { @@ -444,7 +464,7 @@ class MultiFieldValueClassSpecificDeclarations( indexesByInternalNode.mapValues { (node, indexes) -> { additionalParameter -> val arguments = virtualFields.slice(indexes).map { it.makeGetter(this, additionalParameter) } - val innerDeclarations = replacements.getDeclarations(node.irClass!!)!! + val innerDeclarations = replacements.getDeclarations(node.irClass)!! irCall(innerDeclarations.boxMethod).apply { for (i in arguments.indices) { putValueArgument(i, arguments[i]) @@ -465,7 +485,7 @@ class MultiFieldValueClassSpecificDeclarations( else -> nodeToExpressionGetters[field.node]!! to when (field.node) { is Leaf -> null is InternalNode -> { - val irClass = field.node.irClass!! + val irClass = field.node.irClass val declarations = replacements.getDeclarations(irClass)!! declarations.ImplementationAgnostic(nodeToSymbols[field.node]!!) } diff --git a/compiler/testData/codegen/box/valueClasses/MFVCFieldInitializationOrder.kt b/compiler/testData/codegen/box/valueClasses/MFVCFieldInitializationOrder.kt new file mode 100644 index 00000000000..986f68ba1aa --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/MFVCFieldInitializationOrder.kt @@ -0,0 +1,26 @@ +// WITH_STDLIB +// TARGET_BACKEND: JVM_IR +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses + +@JvmInline +value class DPoint(val x: Double, val y: Double) + +class A(var x: DPoint) { + init { x = DPoint(2.0, 3.0) } +} + +class B() { + var a = DPoint(4.0, 5.0) + val b = a + init { a = DPoint(6.0, 7.0) } + val c = a +} + +fun box(): String { + require(A(DPoint(0.0, 1.0)).x == DPoint(2.0, 3.0)) { "No init used" } + require(B().a == DPoint(6.0, 7.0)) { "No init used" } + require(B().b == DPoint(4.0, 5.0)) { "Wrong init order used" } + require(B().c == DPoint(6.0, 7.0)) { "Wrong init order used" } + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/valueClasses/simple.kt b/compiler/testData/codegen/box/valueClasses/complex.kt similarity index 96% rename from compiler/testData/codegen/box/valueClasses/simple.kt rename to compiler/testData/codegen/box/valueClasses/complex.kt index ea2c0c4edb6..e2eca95ab7e 100644 --- a/compiler/testData/codegen/box/valueClasses/simple.kt +++ b/compiler/testData/codegen/box/valueClasses/complex.kt @@ -66,7 +66,7 @@ fun > h(r: R) { g(r.z) f(r) r - C(2, B(3U), "") // todo fix box + C(2, B(3U), "") D(C(2, B(3U), "")) val x = D(C(2, B(3U), "")) var y = D(C(4, B(5U), "1")) @@ -172,4 +172,4 @@ fun equalsChecks(left: R>, right: R>) { // todo add default parameters -fun box() = "OK"//.also { h(R(1, 2U, E(D(C(3, B(4U), "5"))), A(listOf(listOf(6))))) } +fun box() = "OK" diff --git a/compiler/testData/codegen/box/valueClasses/simple.txt b/compiler/testData/codegen/box/valueClasses/complex.txt similarity index 96% rename from compiler/testData/codegen/box/valueClasses/simple.txt rename to compiler/testData/codegen/box/valueClasses/complex.txt index 760160e0fce..55379fd0870 100644 --- a/compiler/testData/codegen/box/valueClasses/simple.txt +++ b/compiler/testData/codegen/box/valueClasses/complex.txt @@ -1,7 +1,7 @@ @kotlin.jvm.JvmInline @kotlin.Metadata public final class A { - // source: 'simple.kt' + // source: 'complex.kt' private final @org.jetbrains.annotations.NotNull field x: java.util.List private synthetic method (p0: java.util.List): void public synthetic final static method box-impl(p0: java.util.List): A @@ -20,7 +20,7 @@ public final class A { @kotlin.jvm.JvmInline @kotlin.Metadata public final class B { - // source: 'simple.kt' + // source: 'complex.kt' private final field x: int private synthetic method (p0: int): void public synthetic final static method box-impl(p0: int): B @@ -38,7 +38,7 @@ public final class B { @kotlin.Metadata public final class Base1$DefaultImpls { - // source: 'simple.kt' + // source: 'complex.kt' public static @org.jetbrains.annotations.NotNull method getFakeOverrideMFVC(@org.jetbrains.annotations.NotNull p0: Base1): R public static method getFakeOverrideRegular(@org.jetbrains.annotations.NotNull p0: Base1): int public final inner class Base1$DefaultImpls @@ -46,7 +46,7 @@ public final class Base1$DefaultImpls { @kotlin.Metadata public interface Base1 { - // source: 'simple.kt' + // source: 'complex.kt' public abstract @org.jetbrains.annotations.NotNull method getFakeOverrideMFVC(): R public abstract method getFakeOverrideRegular(): int public final inner class Base1$DefaultImpls @@ -54,20 +54,20 @@ public interface Base1 { @kotlin.Metadata public interface Base2 { - // source: 'simple.kt' + // source: 'complex.kt' public abstract @org.jetbrains.annotations.NotNull method getL(): R public abstract method setL-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void } @kotlin.Metadata public interface Base3 { - // source: 'simple.kt' + // source: 'complex.kt' public abstract @org.jetbrains.annotations.NotNull method getZ(): E } @kotlin.Metadata public interface Base4 { - // source: 'simple.kt' + // source: 'complex.kt' public abstract method getL(): java.lang.Object public abstract method setL(p0: java.lang.Object): void } @@ -75,7 +75,7 @@ public interface Base4 { @kotlin.jvm.JvmInline @kotlin.Metadata public final class C { - // source: 'simple.kt' + // source: 'complex.kt' private field x: int private field y: int private @org.jetbrains.annotations.NotNull field z: java.lang.String @@ -97,10 +97,26 @@ public final class C { public synthetic final method unbox-impl2(): java.lang.String } +@kotlin.Metadata +public final class ComplexKt { + // source: 'complex.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method equalsChecks-GPBa7dw-yJnZWq8(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List, p6: int, p7: int, p8: int, p9: int, @org.jetbrains.annotations.NotNull p10: java.lang.String, @org.jetbrains.annotations.NotNull p11: java.util.List): void + public final static method f-sUp7gFk-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void + public final static method g-sUp7gFk-ZTiL54U(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void + public final static method h-sUp7gFk-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void + public final static method h1(): void + public final static @org.jetbrains.annotations.NotNull method inlined-OsBMiQA(p0: int, p1: int, p2: int): D + public final static @org.jetbrains.annotations.NotNull method notInlined-OsBMiQA(p0: int, p1: int, p2: int): D + public final static method reuseBoxed(@org.jetbrains.annotations.NotNull p0: java.util.List): void + public final static method supply(p0: boolean): void + public final static method testVars(@org.jetbrains.annotations.NotNull p0: NotInlined): void +} + @kotlin.jvm.JvmInline @kotlin.Metadata public final class D { - // source: 'simple.kt' + // source: 'complex.kt' private field x$x: int private field x$y: int private @org.jetbrains.annotations.NotNull field x$z: java.lang.String @@ -127,7 +143,7 @@ public final class D { @kotlin.jvm.JvmInline @kotlin.Metadata public final class E { - // source: 'simple.kt' + // source: 'complex.kt' private field x$x$x: int private field x$x$y: int private @org.jetbrains.annotations.NotNull field x$x$z: java.lang.String @@ -155,22 +171,22 @@ public final class E { @kotlin.Metadata public final class NotInlined { - // source: 'simple.kt' + // source: 'complex.kt' private @org.jetbrains.annotations.NotNull field l$t: java.util.List + private field l$x: int private field l$y: int - private field l$z$x$x$x$1: int private field l$z$x$x$x: int private field l$z$x$x$y: int private @org.jetbrains.annotations.NotNull field l$z$x$x$z: java.lang.String private @org.jetbrains.annotations.NotNull field withNonTrivialGettersWithBF$t: java.util.List + private field withNonTrivialGettersWithBF$x: int private field withNonTrivialGettersWithBF$y: int - private field withNonTrivialGettersWithBF$z$x$x$x$1: int private field withNonTrivialGettersWithBF$z$x$x$x: int private field withNonTrivialGettersWithBF$z$x$x$y: int private @org.jetbrains.annotations.NotNull field withNonTrivialGettersWithBF$z$x$x$z: java.lang.String private @org.jetbrains.annotations.NotNull field withNonTrivialSettersWithBF$t: java.util.List + private field withNonTrivialSettersWithBF$x: int private field withNonTrivialSettersWithBF$y: int - private field withNonTrivialSettersWithBF$z$x$x$x$1: int private field withNonTrivialSettersWithBF$z$x$x$x: int private field withNonTrivialSettersWithBF$z$x$x$y: int private @org.jetbrains.annotations.NotNull field withNonTrivialSettersWithBF$z$x$x$z: java.lang.String @@ -180,6 +196,7 @@ public final class NotInlined { public @org.jetbrains.annotations.NotNull method getFakeOverrideMFVC(): R public method getFakeOverrideRegular(): int public final @org.jetbrains.annotations.NotNull method getL$t(): java.util.List + public final method getL$x(): int public final method getL$y(): int public final method getL$z$x$x$x(): int public final method getL$z$x$x$y(): int @@ -205,10 +222,10 @@ public final class NotInlined { @kotlin.jvm.JvmInline @kotlin.Metadata public final class R { - // source: 'simple.kt' + // source: 'complex.kt' private @org.jetbrains.annotations.NotNull field t: java.util.List + private field x: int private field y: int - private field z$x$x$x$1: int private field z$x$x$x: int private field z$x$x$y: int private @org.jetbrains.annotations.NotNull field z$x$x$z: java.lang.String @@ -223,6 +240,7 @@ public final class R { public method getFakeOverrideRegular(): int public static method getFakeOverrideRegular-impl(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): int public final @org.jetbrains.annotations.NotNull method getT(): java.util.List + public final method getX(): int public final method getY(): int public final method getZ$x$x$x(): int public final method getZ$x$x$y(): int @@ -241,19 +259,3 @@ public final class R { public synthetic final method unbox-impl4(): java.lang.String public synthetic final method unbox-impl5(): java.util.List } - -@kotlin.Metadata -public final class SimpleKt { - // source: 'simple.kt' - public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String - public final static method equalsChecks-GPBa7dw-yJnZWq8(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List, p6: int, p7: int, p8: int, p9: int, @org.jetbrains.annotations.NotNull p10: java.lang.String, @org.jetbrains.annotations.NotNull p11: java.util.List): void - public final static method f-sUp7gFk-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void - public final static method g-sUp7gFk-ZTiL54U(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void - public final static method h-sUp7gFk-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void - public final static method h1(): void - public final static @org.jetbrains.annotations.NotNull method inlined-OsBMiQA(p0: int, p1: int, p2: int): D - public final static @org.jetbrains.annotations.NotNull method notInlined-OsBMiQA(p0: int, p1: int, p2: int): D - public final static method reuseBoxed(@org.jetbrains.annotations.NotNull p0: java.util.List): void - public final static method supply(p0: boolean): void - public final static method testVars(@org.jetbrains.annotations.NotNull p0: NotInlined): void -} diff --git a/compiler/testData/codegen/box/valueClasses/overrides.kt b/compiler/testData/codegen/box/valueClasses/overrides.kt new file mode 100644 index 00000000000..4a19e324a05 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/overrides.kt @@ -0,0 +1,277 @@ +// WITH_STDLIB +// TARGET_BACKEND: JVM_IR +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses + +interface AbstractPoint { + val x: T + val y: T +} + +@JvmInline +value class DPoint(override val x: Double, override val y: Double): AbstractPoint + +interface GenericMFVCHolder { + var p: T + var p1: T +} + +interface GenericMFVCHolderWithMFVCUpperBound { + var p: T + var p1: T +} + +interface ReifiedMFVCHolder { + var p: DPoint + var p1: DPoint +} + +data class DataClassException(val value: Any?): Exception() + +interface GenericMFVCHolderWithImpls { + var p: T + get() = throw DataClassException(1) + set(value) = throw DataClassException(2 to value) + + var p1: T + get() = throw DataClassException(3) + set(value) = throw DataClassException(4 to value) +} + +interface GenericMFVCHolderWithMFVCUpperBoundWithImpls { + var p: T + get() = throw DataClassException(5) + set(value) = throw DataClassException(6 to value) + + var p1: T + get() = throw DataClassException(7) + set(value) = throw DataClassException(8 to value) +} + +interface ReifiedMFVCHolderWithImpls { + var p: DPoint + get() = throw DataClassException(9) + set(value) = throw DataClassException(10 to value) + + var p1: DPoint + get() = throw DataClassException(11) + set(value) = throw DataClassException(12 to value) +} + +class RealOverride(override var p: DPoint) : GenericMFVCHolder, ReifiedMFVCHolder, GenericMFVCHolderWithMFVCUpperBound { + override var p1: DPoint + get() = throw DataClassException(13) + set(value) = throw DataClassException(14 to value) +} + +class GenericFakeOverride : GenericMFVCHolderWithImpls +class ReifiedFakeOverride : ReifiedMFVCHolderWithImpls +class GenericFakeOverrideWithMFVCUpperBound : GenericMFVCHolderWithMFVCUpperBoundWithImpls + + +@JvmInline +value class GenericFakeOverrideMFVC(val field1: Double, val field2: Double) : GenericMFVCHolderWithImpls +@JvmInline +value class ReifiedFakeOverrideMFVC(val field1: Double, val field2: Double) : ReifiedMFVCHolderWithImpls +@JvmInline +value class GenericFakeOverrideMFVCWithMFVCUpperBound(val field1: Double, val field2: Double) : GenericMFVCHolderWithMFVCUpperBoundWithImpls + + +interface SomePointInterface { + var somethingRegular: Int + + var somethingGeneric: T + + var somethingMFVC: DPoint +} + +interface SomePointInterfaceWithMFVCBound { + var somethingRegular: Int + + var somethingGeneric: T + + var somethingMFVC: DPoint +} + +@JvmInline +value class DPointWithInterface(val x: Double, val y: Double) : SomePointInterface, SomePointInterfaceWithMFVCBound { + override var somethingGeneric: DPoint + get() = throw DataClassException(15) + set(value) = throw DataClassException(16 to value) + + override var somethingMFVC: DPoint + get() = throw DataClassException(17) + set(value) = throw DataClassException(18 to value) + + override var somethingRegular: Int + get() = throw DataClassException(19) + set(value) = throw DataClassException(20 to value) +} + + +interface AbstractSegment { + val p1: T + val p2: T +} + +@JvmInline +value class DSegment(override val p1: DPoint, override val p2: DPoint): AbstractSegment + +fun equal(expected: () -> T, actual: () -> T) { + val expectedResult = runCatching { expected() } + val actualResult = runCatching { actual() } + require(expectedResult == actualResult) { "Expected: $expectedResult\nActual: $actualResult" } +} + +fun box(): String { + val dPoint = DPoint(1.0, 2.0) + equal({ dPoint.toString() }, { (dPoint as Any).toString() }) + + equal({ dPoint.equals(dPoint) }, { dPoint.equals(dPoint as Any) }) + equal({ dPoint.equals(dPoint) }, { (dPoint as Any).equals(dPoint) }) + equal({ dPoint.equals(dPoint) }, { dPoint.equals(dPoint as Any) }) + equal({ dPoint.equals(dPoint) }, { (dPoint as Any).equals(dPoint as Any) }) + + val otherDPoint = DPoint(3.0, 4.0) + equal({ dPoint.equals(otherDPoint) }, { dPoint.equals(otherDPoint as Any) }) + equal({ dPoint.equals(otherDPoint) }, { (dPoint as Any).equals(otherDPoint) }) + equal({ dPoint.equals(otherDPoint) }, { dPoint.equals(otherDPoint as Any) }) + equal({ dPoint.equals(otherDPoint) }, { (dPoint as Any).equals(otherDPoint as Any) }) + + equal({ dPoint.hashCode() }, { (dPoint as Any).hashCode() }) + + equal({ dPoint.x }, { (dPoint as AbstractPoint).x }) + equal({ dPoint.y }, { (dPoint as AbstractPoint).y }) + + + val realOverride = RealOverride(dPoint) + + equal({ realOverride.p }, { (realOverride as GenericMFVCHolder).p }) + equal({ realOverride.p1 }, { (realOverride as GenericMFVCHolder).p1 }) + equal({ realOverride.p }, { (realOverride as ReifiedMFVCHolder).p }) + equal({ realOverride.p1 }, { (realOverride as ReifiedMFVCHolder).p1 }) + equal({ realOverride.p }, { (realOverride as GenericMFVCHolderWithMFVCUpperBound).p }) + equal({ realOverride.p1 }, { (realOverride as GenericMFVCHolderWithMFVCUpperBound).p1 }) + + equal({ realOverride.p = dPoint }, { (realOverride as GenericMFVCHolder).p = dPoint }) + equal({ realOverride.p1 = dPoint }, { (realOverride as GenericMFVCHolder).p1 = dPoint }) + equal({ realOverride.p = dPoint }, { (realOverride as ReifiedMFVCHolder).p = dPoint }) + equal({ realOverride.p1 = dPoint }, { (realOverride as ReifiedMFVCHolder).p1 = dPoint }) + equal({ realOverride.p = dPoint }, { (realOverride as GenericMFVCHolderWithMFVCUpperBound).p = dPoint }) + equal({ realOverride.p1 = dPoint }, { (realOverride as GenericMFVCHolderWithMFVCUpperBound).p1 = dPoint }) + + + val genericFakeOverride = GenericFakeOverride() + + equal({ genericFakeOverride.p }, { (genericFakeOverride as GenericMFVCHolderWithImpls).p }) + equal({ genericFakeOverride.p1 }, { (genericFakeOverride as GenericMFVCHolderWithImpls).p1 }) + val reifiedFakeOverride = ReifiedFakeOverride() + equal({ reifiedFakeOverride.p }, { (reifiedFakeOverride as ReifiedMFVCHolderWithImpls).p }) + equal({ reifiedFakeOverride.p1 }, { (reifiedFakeOverride as ReifiedMFVCHolderWithImpls).p1 }) + val genericFakeOverrideWithMFVCUpperBound = GenericFakeOverrideWithMFVCUpperBound() + equal( + { genericFakeOverrideWithMFVCUpperBound.p }, + { (genericFakeOverrideWithMFVCUpperBound as GenericMFVCHolderWithMFVCUpperBoundWithImpls).p } + ) + equal( + { genericFakeOverrideWithMFVCUpperBound.p1 }, + { (genericFakeOverrideWithMFVCUpperBound as GenericMFVCHolderWithMFVCUpperBoundWithImpls).p1 } + ) + + equal({ genericFakeOverride.p = dPoint }, { (genericFakeOverride as GenericMFVCHolderWithImpls).p = dPoint }) + equal({ genericFakeOverride.p1 = dPoint }, { (genericFakeOverride as GenericMFVCHolderWithImpls).p1 = dPoint }) + equal({ reifiedFakeOverride.p = dPoint }, { (reifiedFakeOverride as ReifiedMFVCHolderWithImpls).p = dPoint }) + equal({ reifiedFakeOverride.p1 = dPoint }, { (reifiedFakeOverride as ReifiedMFVCHolderWithImpls).p1 = dPoint }) + equal( + { genericFakeOverrideWithMFVCUpperBound.p = dPoint }, + { (genericFakeOverrideWithMFVCUpperBound as GenericMFVCHolderWithMFVCUpperBoundWithImpls).p = dPoint } + ) + equal( + { genericFakeOverrideWithMFVCUpperBound.p1 = dPoint }, + { (genericFakeOverrideWithMFVCUpperBound as GenericMFVCHolderWithMFVCUpperBoundWithImpls).p1 = dPoint } + ) + + + val genericFakeOverrideMFVC = GenericFakeOverrideMFVC(1.0, 2.0) + + equal({ genericFakeOverrideMFVC.p }, { (genericFakeOverrideMFVC as GenericMFVCHolderWithImpls).p }) + equal({ genericFakeOverrideMFVC.p1 }, { (genericFakeOverrideMFVC as GenericMFVCHolderWithImpls).p1 }) + + val reifiedFakeOverrideMFVC = ReifiedFakeOverrideMFVC(1.0, 2.0) + equal({ reifiedFakeOverrideMFVC.p }, { (reifiedFakeOverrideMFVC as ReifiedMFVCHolderWithImpls).p }) + equal({ reifiedFakeOverrideMFVC.p1 }, { (reifiedFakeOverrideMFVC as ReifiedMFVCHolderWithImpls).p1 }) + + val genericFakeOverrideMFVCWithMFVCUpperBound = GenericFakeOverrideMFVCWithMFVCUpperBound(1.0, 2.0) + equal( + { genericFakeOverrideMFVCWithMFVCUpperBound.p }, + { (genericFakeOverrideMFVCWithMFVCUpperBound as GenericMFVCHolderWithMFVCUpperBoundWithImpls).p }, + ) + equal( + { genericFakeOverrideMFVCWithMFVCUpperBound.p1 }, + { (genericFakeOverrideMFVCWithMFVCUpperBound as GenericMFVCHolderWithMFVCUpperBoundWithImpls).p1 }, + ) + + equal({ genericFakeOverrideMFVC.p = dPoint }, { (genericFakeOverrideMFVC as GenericMFVCHolderWithImpls).p = dPoint }) + equal({ genericFakeOverrideMFVC.p1 = dPoint }, { (genericFakeOverrideMFVC as GenericMFVCHolderWithImpls).p1 = dPoint }) + + equal({ reifiedFakeOverrideMFVC.p = dPoint }, { (reifiedFakeOverrideMFVC as ReifiedMFVCHolderWithImpls).p = dPoint }) + equal({ reifiedFakeOverrideMFVC.p1 = dPoint }, { (reifiedFakeOverrideMFVC as ReifiedMFVCHolderWithImpls).p1 = dPoint }) + + equal( + { genericFakeOverrideMFVCWithMFVCUpperBound.p = dPoint }, + { (genericFakeOverrideMFVCWithMFVCUpperBound as GenericMFVCHolderWithMFVCUpperBoundWithImpls).p = dPoint }, + ) + equal( + { genericFakeOverrideMFVCWithMFVCUpperBound.p1 = dPoint }, + { (genericFakeOverrideMFVCWithMFVCUpperBound as GenericMFVCHolderWithMFVCUpperBoundWithImpls).p1 = dPoint }, + ) + + + val dPointWithInterface = DPointWithInterface(1.0, 2.0) + + equal({ dPointWithInterface.somethingGeneric }, { (dPointWithInterface as SomePointInterface).somethingGeneric }) + equal({ dPointWithInterface.somethingRegular }, { (dPointWithInterface as SomePointInterface).somethingRegular }) + equal({ dPointWithInterface.somethingMFVC }, { (dPointWithInterface as SomePointInterface).somethingMFVC }) + + equal({ dPointWithInterface.somethingGeneric }, { (dPointWithInterface as SomePointInterfaceWithMFVCBound).somethingGeneric }) + equal({ dPointWithInterface.somethingRegular }, { (dPointWithInterface as SomePointInterfaceWithMFVCBound).somethingRegular }) + equal({ dPointWithInterface.somethingMFVC }, { (dPointWithInterface as SomePointInterfaceWithMFVCBound).somethingMFVC }) + + equal( + { dPointWithInterface.somethingGeneric = dPoint }, + { (dPointWithInterface as SomePointInterface).somethingGeneric = dPoint } + ) + equal({ dPointWithInterface.somethingRegular = 1 }, { (dPointWithInterface as SomePointInterface).somethingRegular = 1 }) + equal({ dPointWithInterface.somethingMFVC = dPoint }, { (dPointWithInterface as SomePointInterface).somethingMFVC = dPoint }) + + equal( + { dPointWithInterface.somethingGeneric = dPoint }, + { (dPointWithInterface as SomePointInterfaceWithMFVCBound).somethingGeneric = dPoint } + ) + equal( + { dPointWithInterface.somethingRegular = 2 }, + { (dPointWithInterface as SomePointInterfaceWithMFVCBound).somethingRegular = 2 } + ) + equal( + { dPointWithInterface.somethingMFVC = dPoint}, + { (dPointWithInterface as SomePointInterfaceWithMFVCBound).somethingMFVC = dPoint } + ) + + + val dSegment = DSegment(dPoint, otherDPoint) + + equal({ dPoint }, { dSegment.p1 }) + equal({ otherDPoint }, { dSegment.p2 }) + equal({ dPoint.x }, { dSegment.p1.x }) + equal({ otherDPoint.x }, { dSegment.p2.x }) + equal({ dPoint.y }, { dSegment.p1.y }) + equal({ otherDPoint.y }, { dSegment.p2.y }) + equal({ dSegment.p1 }, { (dSegment as AbstractSegment).p1 }) + equal({ dSegment.p2 }, { (dSegment as AbstractSegment).p2 }) + equal({ dSegment.p1.x }, { (dSegment as AbstractSegment).p1.x }) + equal({ dSegment.p2.x }, { (dSegment as AbstractSegment).p2.x }) + equal({ dSegment.p1.y }, { (dSegment as AbstractSegment).p1.y }) + equal({ dSegment.p2.y }, { (dSegment as AbstractSegment).p2.y }) + + return "OK" +} diff --git a/compiler/testData/codegen/box/valueClasses/throwingMFVCReassignments.kt b/compiler/testData/codegen/box/valueClasses/throwingMFVCReassignments.kt new file mode 100644 index 00000000000..d6e8749d41f --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/throwingMFVCReassignments.kt @@ -0,0 +1,33 @@ +// WITH_STDLIB +// TARGET_BACKEND: JVM_IR +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses + +@JvmInline +value class DPoint(val x: Double, val y: Double) + +class PointBox(var value: DPoint) + +fun box(): String { + var p = DPoint(1.0, 2.0) + try { + p = DPoint(3.0, error("Failure")) + } catch (_: Exception) { + } + if (p != DPoint(1.0, 2.0)) { + return "Partially reassigned variable" + } + + val box = PointBox(p) + + try { + box.value = DPoint(3.0, error("Failure")) + } catch (_: Exception) { + } + + if (box.value != DPoint(1.0, 2.0)) { + return "Partially reassigned field" + } + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/valueClasses/MFVCDeclaration.kt b/compiler/testData/codegen/bytecodeText/valueClasses/MFVCDeclaration.kt new file mode 100644 index 00000000000..1ac84437da8 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/valueClasses/MFVCDeclaration.kt @@ -0,0 +1,68 @@ +// CHECK_BYTECODE_LISTING +// WITH_STDLIB +// TARGET_BACKEND: JVM_IR +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses + +@JvmInline +value class A(val x: List) + +@JvmInline +value class B(val x: UInt) + +@JvmInline +value class C(val x: Int, val y: B, val z: String = "3") + +@JvmInline +value class D(val x: C) { + constructor(x: Int, y: UInt, z: Int) : this(C(x, B(y), z.toString())) + + init { + println(x.x) + } +} + +fun functionWithoutBoxes(x: D, y: D) { + var z: D = x + val t: D = D(C(1, B(3U), "4")) + z = t + require(z == y) +} + +// 2 public static toString-impl\(IILjava/lang/String;\)Ljava/lang/String; +// 2 INVOKESTATIC C.toString-impl \(IILjava/lang/String;\)Ljava/lang/String; +// 1 INVOKESTATIC D.toString-impl \(IILjava/lang/String;\)Ljava/lang/String; +// 2 public static hashCode-impl\(IILjava/lang/String;\)I +// 2 INVOKESTATIC C.hashCode-impl \(IILjava/lang/String;\)I +// 1 INVOKESTATIC D.hashCode-impl \(IILjava/lang/String;\)I +// 2 public static equals-impl\(IILjava/lang/String;Ljava/lang/Object;\)Z +// 2 public final static equals-impl0\(IILjava/lang/String;IILjava/lang/String;\)Z +// 1 public final static constructor-impl\(III\)LD; +// 2 public final static constructor-impl\(IILjava/lang/String;\)V +// 2 INVOKESTATIC D.constructor-impl \(IILjava/lang/String;\)V +// 2 INVOKESTATIC C.constructor-impl \(IILjava/lang/String;\)V +// 1 public final getX\(\)LC; +// 1 public final getX\$x\(\)I +// 1 public final getX\$y\(\)I +// 1 public final getX\$z\(\)Ljava/lang/String; +// 2 private synthetic \(IILjava/lang/String;\)V +// 1 public final static synthetic box-impl\(IILjava/lang/String;\)LD; +// 1 public final static synthetic box-impl\(IILjava/lang/String;\)LC; +// 2 public final synthetic unbox-impl0\(\)I +// 2 public final synthetic unbox-impl1\(\)I +// 2 public final synthetic unbox-impl2\(\)Ljava/lang/String; +// 1 private I x\$x +// 1 private I x\$y +// 1 private Ljava/lang/String; x\$z +// 0 private LC; x +// 3 private I x +// 1 private I y +// 1 private Ljava/lang/String; z +// 1 INVOKESPECIAL C. \(IILjava/lang/String;\)V +// 1 INVOKESPECIAL D. \(IILjava/lang/String;\)V +// 1 INVOKESTATIC D.box-impl \(IILjava/lang/String;\)LD; +// 1 INVOKESTATIC D.box-impl \(IILjava/lang/String;\)LD;\n ARETURN +// 1 INVOKESTATIC C.box-impl \(IILjava/lang/String;\)LC; +// 1 INVOKESTATIC C.box-impl \(IILjava/lang/String;\)LC;\n ARETURN +// 1 public final static functionWithoutBoxes-GPBa7dw-AYZo7_8\(IILjava/lang/String;IILjava/lang/String;\)V +// 0 functionWithoutBoxes.*(\n {3}.*)*(\n {4}(NEW [ABCD]|.*(box|[ABCD]\.|LA;|LB;|LC;|LD;))) diff --git a/compiler/testData/codegen/bytecodeText/valueClasses/MFVCReassignments.kt b/compiler/testData/codegen/bytecodeText/valueClasses/MFVCReassignments.kt new file mode 100644 index 00000000000..09cb8313ae9 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/valueClasses/MFVCReassignments.kt @@ -0,0 +1,73 @@ +// CHECK_BYTECODE_LISTING +// WITH_STDLIB +// TARGET_BACKEND: JVM_IR +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses + +@JvmInline +value class DPoint(val x: Double, val y: Double) + +class Box(var value: DPoint) + +fun supplier(index: Int) {} // to make usage of the argument +fun supplier(index: Int, x: DPoint) {} // to make usage of the argument + +fun reassignVariable(x: DPoint, box: Box) { + supplier(100) + var p = DPoint(1.0, 2.0) // should not use temporary variables + supplier(101, p) + p = p // should not use temporary variables + supplier(102, p) + p = DPoint(3.0, 4.0) // should use tempVars + supplier(103, p) + p = x // should not use temporary variables + supplier(104, p) + p = box.value // should use temporary variables + supplier(105, p) + p = listOf(p)[0] // should use temporary variables + supplier(106, p) +} + +fun reassignField(x: DPoint, box: Box) { + supplier(107) + val p = DPoint(5.0, 6.0) + supplier(108, p) + var b = Box(p) // should not use temporary variables + supplier(109) + b.value = b.value // should not use temporary variables + supplier(110) + b.value = DPoint(7.0, 8.0) // should use tempVars + supplier(111) + b.value = x // should not use temporary variables + supplier(112) + b.value = box.value // should not use temporary variables + supplier(113) + b.value = listOf(p)[0] // should use temporary variables + supplier(114) +} + +// 1 100(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){2}101 +// 0 100(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){3}101 +// 1 101(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){2}102 +// 0 101(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){3}102 +// 1 102(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){4}103 +// 0 102(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){5}103 +// 1 103(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){2}104 +// 0 103(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){3}104 +// 1 104(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){4}105 +// 0 104(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){5}105 +// 1 105(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){4}106 +// 0 105(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){5}106 + +// 1 107(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){2}108 +// 0 107(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){3}108 +// 0 108(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){1}109 +// 1 109(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){2}110 +// 0 109(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){3}110 +// 1 110(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){2}111 +// 0 110(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){3}111 +// 0 111(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){1}112 +// 1 112(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){2}113 +// 0 112(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){3}113 +// 1 113(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){2}114 +// 0 113(\D|\d\D|\d\d\D)*(DSTORE(\D|\d\D|\d\d\D)*){3}114 diff --git a/compiler/testData/codegen/bytecodeText/valueClasses/equalsBoxTest.kt b/compiler/testData/codegen/bytecodeText/valueClasses/equalsBoxTest.kt new file mode 100644 index 00000000000..cd648dfeb75 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/valueClasses/equalsBoxTest.kt @@ -0,0 +1,42 @@ +// CHECK_BYTECODE_LISTING +// WITH_STDLIB +// TARGET_BACKEND: JVM_IR +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses + +@JvmInline +value class DPoint(val x: Double, val y: Double) + +fun require(index: Int, condition: Boolean) = require(condition) { "$index" } + +fun DPoint.toObject() = this as Any +fun DPoint.toDPointOrNull() = this as DPoint? +fun Nothing?.toDPointOrNull() = this as DPoint? + +fun equalsChecks(left: DPoint, right: DPoint) { + require(100, left == right) + require(101, left.toObject() == right) + require(102, left == right.toObject()) + require(103, left.toObject() == right.toObject()) + require(104, null == right) + require(105, left == null) + require(106, null as Any? == right) + require(107, left == null as Any?) + require(108, null.toDPointOrNull() == right) + require(109, left == null.toDPointOrNull()) + require(110, left.toDPointOrNull() == right) + require(111, left == right.toDPointOrNull()) +} + +// 1 BIPUSH 100\n {4}DLOAD 0\n {4}DLOAD 2\n {4}DLOAD 4\n {4}DLOAD 6\n {4}INVOKESTATIC .*equals-impl0.*\n {4}INVOKESTATIC .*require +// 1 BIPUSH 101\n {4}DLOAD 0\n {4}DLOAD 2\n {4}.*toObject.*\n {4}DLOAD 4\n {4}DLOAD 6\n {4}.*box-impl.*\n {4}INVOKESTATIC .*Intrinsics.areEqual.*\n {4}INVOKESTATIC .*require +// 1 BIPUSH 102\n {4}DLOAD 0\n {4}DLOAD 2\n {4}DLOAD 4\n {4}DLOAD 6\n {4}.*toObject.*\n {4}INVOKESTATIC .*equals-impl .*\n {4}INVOKESTATIC .*require +// 1 BIPUSH 103\n {4}DLOAD 0\n {4}DLOAD 2\n {4}.*toObject.*\n {4}DLOAD 4\n {4}DLOAD 6\n {4}.*toObject.*\n {4}INVOKESTATIC .*Intrinsics.areEqual.*\n {4}INVOKESTATIC .*require +// 1 BIPUSH 104\n {4}ICONST_0\n {4}INVOKESTATIC .*require +// 1 BIPUSH 105\n {4}DLOAD 0\n {4}DLOAD 2\n {4}ACONST_NULL\n {4}INVOKESTATIC .*equals-impl .*\n {4}INVOKESTATIC .*require +// 1 BIPUSH 106\n {4}ACONST_NULL\n {4}DLOAD 4\n {4}DLOAD 6\n {4}.*box.*\n {4}INVOKESTATIC .*Intrinsics.areEqual.*\n {4}INVOKESTATIC .*require +// 1 BIPUSH 107\n {4}DLOAD 0\n {4}DLOAD 2\n {4}ACONST_NULL\n {4}INVOKESTATIC .*equals-impl .*\n {4}INVOKESTATIC .*require +// 1 BIPUSH 108\n {4}ACONST_NULL\n {4}.*toDPointOrNull.*\n {4}DUP\n {4}IFNONNULL.*(\n {3}([^b\n]|b[^o\n]|bo[^x\n]|box-impl\d)*)*\n {4}INVOKESTATIC .*require +// 1 BIPUSH 109\n {4}DLOAD 0\n {4}DLOAD 2\n {4}ACONST_NULL\n {4}.*toDPointOrNull.*\n {4}INVOKESTATIC .*equals-impl .*\n {4}INVOKESTATIC .*require +// 1 BIPUSH 110\n {4}DLOAD 0\n {4}DLOAD 2\n {4}.*toDPointOrNull.*\n {4}DUP\n {4}IFNONNULL.*(\n {3}([^b\n]|b[^o\n]|bo[^x\n]|box-impl\d)*)*\n {4}INVOKESTATIC .*require +// 1 BIPUSH 111\n {4}DLOAD 0\n {4}DLOAD 2\n {4}DLOAD 4\n {4}DLOAD 6\n {4}.*toDPointOrNull.*\n {4}INVOKESTATIC .*equals-impl .*\n {4}INVOKESTATIC .*require diff --git a/compiler/testData/codegen/bytecodeText/valueClasses/passingMFVC2Functions.kt b/compiler/testData/codegen/bytecodeText/valueClasses/passingMFVC2Functions.kt new file mode 100644 index 00000000000..1bb0077f41b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/valueClasses/passingMFVC2Functions.kt @@ -0,0 +1,49 @@ +// CHECK_BYTECODE_LISTING +// WITH_STDLIB +// TARGET_BACKEND: JVM_IR +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses + +@JvmInline +value class DPoint(val x: Double, val y: Double) + +fun acceptBoxed(x: Any?) {} +fun acceptFlattened(x: DPoint) {} +fun returnBoxed() = DPoint(3.0, 4.0) + +fun testFlattened2Boxed() { + acceptBoxed(DPoint(1.0, 2.0)) +} + +fun testBoxed2Boxed() { + acceptBoxed(returnBoxed()) +} + +fun testFlattened2Flattened() { + acceptFlattened(DPoint(1.0, 2.0)) +} + +fun testBoxed2Flattened() { + acceptFlattened(returnBoxed()) +} + +fun testIgnoredFlattened() { + DPoint(1.0, 2.0) + DPoint(1.0, 2.0) +} + +fun testIgnoredBoxed() { + returnBoxed() +} + +// 1 testFlattened2Boxed\(\)V(\n {3}.*)*((\n {3}.*box-impl .*)(\n {3}.*)*){1} +// 0 testFlattened2Boxed\(\)V(\n {3}.*)*((\n {3}.*box-impl.*)(\n {3}.*)*){2} +// 0 testBoxed2Boxed\(\)V(\n {3}.*)*((\n {3}.*(box-impl|DSTORE|DLOAD).*)(\n {3}.*)*){1} +// 0 testFlattened2Flattened\(\)V(\n {3}.*)*((\n {3}.*box-impl.*)(\n {3}.*)*){1} +// 1 testFlattened2Flattened\(\)V(\n {3}.*)*((\n {3}.*DSTORE.*)(\n {3}.*)*){2} +// 0 testFlattened2Flattened\(\)V(\n {3}.*)*((\n {3}.*DSTORE.*)(\n {3}.*)*){3} +// 0 testBoxed2Flattened\(\)V(\n {3}.*)*((\n {3}.*box-impl .*)(\n {3}.*)*){1} +// 1 testBoxed2Flattened\(\)V(\n {3}.*)*((\n {3}.*unbox-impl.*)(\n {3}.*)*){2} +// 0 testBoxed2Flattened\(\)V(\n {3}.*)*((\n {3}.*unbox-impl.*)(\n {3}.*)*){3} +// 0 testIgnoredFlattened\(\)V(\n {3}.*)*((\n {3}.*box-impl.*)(\n {3}.*)*){1} +// 0 testIgnoredBoxed\(\)V(\n {3}.*)*((\n {3}.*box-impl.*)(\n {3}.*)*){1} diff --git a/compiler/testData/codegen/bytecodeText/valueClasses/regularClassWithMFVC.kt b/compiler/testData/codegen/bytecodeText/valueClasses/regularClassWithMFVC.kt new file mode 100644 index 00000000000..72acb825bf3 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/valueClasses/regularClassWithMFVC.kt @@ -0,0 +1,149 @@ +// CHECK_BYTECODE_LISTING +// WITH_STDLIB +// TARGET_BACKEND: JVM_IR +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses + +@JvmInline +value class DPoint(val x: Double, val y: Double) { + val opposite: DPoint + get() = DPoint(-x, -y) +} + +class DSegment(var p1: DPoint, var p2: DPoint) { + + val center: DPoint + get() = DPoint(p1.x / 2 + p2.x / 2, p1.y / 2 + p2.y / 2) + + var notImplemented: DPoint + get() = TODO() + set(_) = TODO() + + var point1WithBackingFieldAndDefaultGetter: DPoint = p1 + var point2WithBackingFieldAndDefaultGetter: DPoint = p1 + get() = field + set(value) { + field = value + require("${2 + 2}" == "4") + } + + var pointWithBackingFieldAndCustomGetter: DPoint = p1 + get() = field.also { require("${2 + 2}" == "4") } + set(value) { + field = value + require("${2 + 2}" == "4") + } + + init { + p1 = p1 + } +} + +fun tryGetSegment(segment: DSegment) { + segment.p1 + segment.p1.x + segment.p1.y + segment.p2 + segment.p2.x + segment.p2.y + segment.center + segment.center.x + segment.center.y + segment.notImplemented + segment.notImplemented.x + segment.notImplemented.y + segment.point1WithBackingFieldAndDefaultGetter + segment.point1WithBackingFieldAndDefaultGetter.x + segment.point1WithBackingFieldAndDefaultGetter.y + segment.point2WithBackingFieldAndDefaultGetter + segment.point2WithBackingFieldAndDefaultGetter.x + segment.point2WithBackingFieldAndDefaultGetter.y + segment.pointWithBackingFieldAndCustomGetter + segment.pointWithBackingFieldAndCustomGetter.x + segment.pointWithBackingFieldAndCustomGetter.y +} + +fun trySetSegment(segment: DSegment) { + segment.notImplemented = segment.p1 + segment.point1WithBackingFieldAndDefaultGetter = segment.p1 + segment.point2WithBackingFieldAndDefaultGetter = segment.p1 + segment.pointWithBackingFieldAndCustomGetter = segment.p1 + segment.p1 = segment.p1 + segment.p2 = segment.p2 +} + +// 0 public final getOpposite\(\)LDPoint; +// 1 public final static getOpposite-impl\(DD\)LDPoint; +// 1 private D p1\$x +// 1 private D p1\$y +// 0 private DPoint; p1 +// 1 private D p2\$x +// 1 private D p2\$y +// 0 private DPoint; p2 +// 1 private D point1WithBackingFieldAndDefaultGetter\$x +// 1 private D point1WithBackingFieldAndDefaultGetter\$y +// 0 private DPoint; point1WithBackingFieldAndDefaultGetter +// 1 private D point2WithBackingFieldAndDefaultGetter\$x +// 1 private D point2WithBackingFieldAndDefaultGetter\$y +// 0 private DPoint; point2WithBackingFieldAndDefaultGetter +// 1 private D pointWithBackingFieldAndCustomGetter\$x +// 1 private D pointWithBackingFieldAndCustomGetter\$y +// 0 private DPoint; pointWithBackingFieldAndCustomGetter +// 0 private DPoint; notImplemented +// 0 private D notImplemented +// 0 private DPoint; center +// 0 private D center +// 1 public \(DDDD\)V +// 0 public \(DDDD\)V.*(\n {3}.*)*(\n {4}.*box) +// 1 public final getP1\(\)LDPoint; +// 1 public final setP1-sUp7gFk\(DD\)V +// 1 public final getP2\(\)LDPoint; +// 1 public final setP2-sUp7gFk\(DD\)V +// 1 public final getCenter\(\)LDPoint; +// 1 public final getNotImplemented\(\)LDPoint; +// 1 public final setNotImplemented-sUp7gFk\(DD\)V +// 1 public final getPoint1WithBackingFieldAndDefaultGetter\(\)LDPoint; +// 1 public final setPoint1WithBackingFieldAndDefaultGetter-sUp7gFk\(DD\)V +// 1 public final getPoint2WithBackingFieldAndDefaultGetter\(\)LDPoint; +// 1 public final setPoint2WithBackingFieldAndDefaultGetter-sUp7gFk\(DD\)V +// 1 public final getPointWithBackingFieldAndCustomGetter\(\)LDPoint; +// 1 public final setPointWithBackingFieldAndCustomGetter-sUp7gFk\(DD\)V +// 1 public final getP1\$x\(\)D +// 1 public final getP1\$y\(\)D +// 1 public final getP2\$x\(\)D +// 1 public final getP2\$y\(\)D +// 1 public final getPoint1WithBackingFieldAndDefaultGetter\$x\(\)D +// 1 public final getPoint1WithBackingFieldAndDefaultGetter\$y\(\)D +// 1 public final getPoint2WithBackingFieldAndDefaultGetter\$x\(\)D +// 1 public final getPoint2WithBackingFieldAndDefaultGetter\$y\(\)D +// 0 public final getCenter\$ +// 0 public final getNotImplemented\$ +// 0 public final getPointWithBackingFieldAndCustomGetter\$ +// 0 ^ {2}\b.*get.*\$.*(\n {3}.*)*(\n {4}.*\.box) +// 1 tryGetSegment\(LDSegment;\)V +// 0 try[GS]etSegment\(LDSegment;\)V.*(\n {3}.*)*(\n {4}.*\.box) +// 1 tryGetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}.*LDPoint;)){13} +// 0 tryGetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}.*LDPoint;)){14} +// 0 trySetSegment\(LDSegment;\)V.*(\n {3}.*)*(\n {4}.*LDPoint;) +// 1 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setNotImplemented-sUp7gFk \(DD\)V)){1} +// 0 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setNotImplemented-sUp7gFk \(DD\)V)){2} +// 1 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setPoint1WithBackingFieldAndDefaultGetter-sUp7gFk \(DD\)V)){1} +// 0 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setPoint1WithBackingFieldAndDefaultGetter-sUp7gFk \(DD\)V)){2} +// 1 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setPoint2WithBackingFieldAndDefaultGetter-sUp7gFk \(DD\)V)){1} +// 0 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setPoint2WithBackingFieldAndDefaultGetter-sUp7gFk \(DD\)V)){2} +// 1 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setPointWithBackingFieldAndCustomGetter-sUp7gFk \(DD\)V)){1} +// 0 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setPointWithBackingFieldAndCustomGetter-sUp7gFk \(DD\)V)){2} +// 1 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setP1-sUp7gFk \(DD\)V)){1} +// 0 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setP1-sUp7gFk \(DD\)V)){2} +// 1 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setP2-sUp7gFk \(DD\)V)){1} +// 0 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DSegment\.setP2-sUp7gFk \(DD\)V)){2} +// 1 tryGetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DPoint\.getX \(\)D)){3} +// 0 tryGetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DPoint\.getX \(\)D)){4} +// 0 trySetSegment\(LDSegment;\)V.*(\n {3}.*)*(\n {4}INVOKEVIRTUAL DPoint\.getX \(\)D) +// 1 tryGetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DPoint\.getY \(\)D)){3} +// 0 tryGetSegment\(LDSegment;\)V.*((\n {3}.*)*(\n {4}INVOKEVIRTUAL DPoint\.getY \(\)D)){4} +// 0 trySetSegment\(LDSegment;\)V.*(\n {3}.*)*(\n {4}INVOKEVIRTUAL DPoint\.getY \(\)D) +// 1 tryGetSegment\(LDSegment;\)V.*((\n {3}.*)*?(\n {4}INVOKEVIRTUAL DSegment\.get.*\$[xy] \(\)D)){8} +// 0 tryGetSegment\(LDSegment;\)V.*((\n {3}.*)*?(\n {4}INVOKEVIRTUAL DSegment\.get.*\$[xy] \(\)D)){9} +// 1 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*?(\n {4}INVOKEVIRTUAL DSegment\.get.*\$[xy] \(\)D)){12} +// 0 trySetSegment\(LDSegment;\)V.*((\n {3}.*)*?(\n {4}INVOKEVIRTUAL DSegment\.get.*\$[xy] \(\)D)){13} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java index 20c86bb9889..74cd55233c3 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.test.runners.codegen; import com.intellij.testFramework.TestDataPath; import org.jetbrains.kotlin.test.util.KtTestUtil; import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.utils.TransformersFunctions; import org.jetbrains.kotlin.test.TestMetadata; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -5455,6 +5456,16 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } } + @Nested + @TestMetadata("compiler/testData/codegen/bytecodeText/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + } + @Nested @TestMetadata("compiler/testData/codegen/bytecodeText/varargs") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index fd557d143a8..f7de6e10b6f 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -50189,6 +50189,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/valueClasses/classFlattening.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } + @Test + @TestMetadata("complex.kt") + public void testComplex() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/complex.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("equality.kt") public void testEquality() throws Exception { @@ -50196,9 +50202,21 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } @Test - @TestMetadata("simple.kt") - public void testSimple() throws Exception { - runTest("compiler/testData/codegen/box/valueClasses/simple.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + @TestMetadata("MFVCFieldInitializationOrder.kt") + public void testMFVCFieldInitializationOrder() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/MFVCFieldInitializationOrder.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("overrides.kt") + public void testOverrides() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/overrides.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("throwingMFVCReassignments.kt") + public void testThrowingMFVCReassignments() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/throwingMFVCReassignments.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } } diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java index b4079bee42e..fd1e1aa3e92 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.test.runners.codegen; import com.intellij.testFramework.TestDataPath; import org.jetbrains.kotlin.test.util.KtTestUtil; import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.utils.TransformersFunctions; import org.jetbrains.kotlin.test.TestMetadata; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -5779,6 +5780,46 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { } } + @Nested + @TestMetadata("compiler/testData/codegen/bytecodeText/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("equalsBoxTest.kt") + public void testEqualsBoxTest() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/valueClasses/equalsBoxTest.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("MFVCDeclaration.kt") + public void testMFVCDeclaration() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/valueClasses/MFVCDeclaration.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("MFVCReassignments.kt") + public void testMFVCReassignments() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/valueClasses/MFVCReassignments.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("passingMFVC2Functions.kt") + public void testPassingMFVC2Functions() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/valueClasses/passingMFVC2Functions.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("regularClassWithMFVC.kt") + public void testRegularClassWithMFVC() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/valueClasses/regularClassWithMFVC.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + } + @Nested @TestMetadata("compiler/testData/codegen/bytecodeText/varargs") @TestDataPath("$PROJECT_ROOT")