From bf5fa61ffbd1ac1a642fe882e31e6dc809f8e433 Mon Sep 17 00:00:00 2001 From: "Evgeniy.Zhelenskiy" Date: Sat, 22 Apr 2023 08:44:09 +0200 Subject: [PATCH] [IR] Unify MFVC bridge redirection, fix related bugs, support MFVC overriding functions with default parameters #KT-1179 --- ...LightTreeBlackBoxCodegenTestGenerated.java | 6 + .../FirPsiBlackBoxCodegenTestGenerated.java | 6 + .../lower/DefaultArgumentStubGenerator.kt | 149 ++++++++------- .../lower/InlineClassDeclarationLowering.kt | 3 +- .../js/lower/JsDefaultParameterInjector.kt | 33 ++-- .../jvm/lower/AddContinuationLowering.kt | 3 +- .../backend/jvm/lower/BridgeLowering.kt | 138 +++----------- ...nheritedDefaultMethodsOnClassesLowering.kt | 80 +++----- .../jvm/lower/JvmDefaultParameterInjector.kt | 60 ++++++ .../lower/JvmMultiFieldValueClassLowering.kt | 173 ++++++------------ .../lower/JvmValueClassAbstractLowering.kt | 2 +- .../lower/StaticDefaultFunctionLowering.kt | 7 +- .../backend/jvm/JvmCachedDeclarations.kt | 6 +- ...emoizedMultiFieldValueClassReplacements.kt | 116 +++++++++++- .../jetbrains/kotlin/backend/jvm/MfvcNode.kt | 63 ++++++- .../kotlin/backend/jvm/MfvcNodeInstance.kt | 39 +++- .../wasm/lower/JsInteropFunctionsLowering.kt | 6 +- .../org/jetbrains/kotlin/ir/util/IrUtils.kt | 12 +- .../valueClasses/nothingAsParameterType.kt | 50 +++++ .../overrideFunctionWithDefaultParameter.kt | 4 +- ...rideFunctionWithDefaultParameterGeneric.kt | 4 +- .../codegen/box/valueClasses/overrides.txt | 4 +- .../valueClasses/overrides_inlineClass.txt | 4 +- .../valueClasses/overrides_typeParameters.txt | 4 +- .../codegen/BlackBoxCodegenTestGenerated.java | 12 -- .../IrBlackBoxCodegenTestGenerated.java | 6 + ...kBoxCodegenWithIrInlinerTestGenerated.java | 6 + .../LightAnalysisModeTestGenerated.java | 14 -- 28 files changed, 577 insertions(+), 433 deletions(-) create mode 100644 compiler/testData/codegen/box/valueClasses/nothingAsParameterType.kt diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java index b2bc0eb5bf4..374c79d6175 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java @@ -52095,6 +52095,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/valueClasses/mutableSharedMfvcVar.kt"); } + @Test + @TestMetadata("nothingAsParameterType.kt") + public void testNothingAsParameterType() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/nothingAsParameterType.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("overrideFunctionWithDefaultParameter.kt") public void testOverrideFunctionWithDefaultParameter() throws Exception { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java index f16e1600e89..ddb55f14834 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java @@ -52095,6 +52095,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/valueClasses/mutableSharedMfvcVar.kt"); } + @Test + @TestMetadata("nothingAsParameterType.kt") + public void testNothingAsParameterType() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/nothingAsParameterType.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("overrideFunctionWithDefaultParameter.kt") public void testOverrideFunctionWithDefaultParameter() throws Exception { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt index 85109f6a2cc..4c2a1fbc646 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt @@ -5,11 +5,10 @@ package org.jetbrains.kotlin.backend.common.lower -import org.jetbrains.kotlin.backend.common.BodyLoweringPass -import org.jetbrains.kotlin.backend.common.CommonBackendContext -import org.jetbrains.kotlin.backend.common.DeclarationTransformer +import org.jetbrains.kotlin.backend.common.* import org.jetbrains.kotlin.backend.common.descriptors.synthesizedString import org.jetbrains.kotlin.backend.common.ir.ValueRemapper +import org.jetbrains.kotlin.backend.common.lower.isMovedReceiver import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* @@ -30,6 +29,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.memoryOptimizedPlus +import org.jetbrains.kotlin.backend.common.lower.isMovedReceiver as isMovedReceiverImpl // TODO: fix expect/actual default parameters @@ -263,8 +263,11 @@ open class DefaultParameterInjector( protected val forceSetOverrideSymbols: Boolean = true, ) : IrElementTransformerVoid(), BodyLoweringPass { + private val declarationStack = mutableListOf() override fun lower(irBody: IrBody, container: IrDeclaration) { + declarationStack.push(container) irBody.transformChildrenVoid(this) + declarationStack.pop() } protected open fun shouldReplaceWithSyntheticFunction(functionAccess: IrFunctionAccessExpression): Boolean { @@ -275,50 +278,44 @@ open class DefaultParameterInjector( if (!shouldReplaceWithSyntheticFunction(expression)) return expression - val (symbol, params) = parametersForCall(expression) ?: return expression + val symbol = createStubFunction(expression) ?: return expression for (i in 0 until expression.typeArgumentsCount) { log { "$symbol[$i]: ${expression.getTypeArgument(i)}" } } - symbol.owner.typeParameters.forEach { log { "${symbol.owner}[${it.index}] : $it" } } + val stubFunction = symbol.owner + stubFunction.typeParameters.forEach { log { "$stubFunction[${it.index}] : $it" } } - val isStatic = isStatic(expression.symbol.owner) - - val newCall = builder(symbol).apply { - copyTypeArgumentsFrom(expression) - - var receivers = 0 - - if (isStatic) { - if (symbol.owner.dispatchReceiverParameter != null) { - dispatchReceiver = params[receivers++] + val declaration = expression.symbol.owner + val currentDeclaration = declarationStack.last() + return context.createIrBuilder( + currentDeclaration.symbol, startOffset = expression.startOffset, endOffset = expression.endOffset + ).irBlock { + val newCall = builder(symbol).apply { + val offset = if (needsTypeArgumentOffset(declaration)) declaration.parentAsClass.typeParameters.size else 0 + for (i in 0 until typeArgumentsCount) { + putTypeArgument(i, expression.getTypeArgument(i + offset)) } - if (symbol.owner.extensionReceiverParameter != null) { - extensionReceiver = params[receivers++] + val parameter2arguments = argumentsForCall(expression, stubFunction) + + for ((parameter, argument) in parameter2arguments) { + when (parameter) { + stubFunction.dispatchReceiverParameter -> log { "call::dispatch@: ${ir2string(argument)}" } + stubFunction.extensionReceiverParameter -> log { "call::extension@: ${ir2string(argument)}" } + else -> log { "call::params@$${parameter.index}/${parameter.name}: ${ir2string(argument)}" } + } + if (argument != null) { + putArgument(parameter, argument) + } } - } else { - dispatchReceiver = expression.dispatchReceiver - extensionReceiver = expression.extensionReceiver } - params.drop(receivers).forEachIndexed { i, value -> - log { "call::params@$i/${symbol.owner.valueParameters[i].name}: ${ir2string(value)}" } - putValueArgument(i, value) - } - - log { "call::extension@: ${ir2string(expression.extensionReceiver)}" } - log { "call::dispatch@: ${ir2string(expression.dispatchReceiver)}" } - } - - return newCall.takeIf { it.type == expression.type } ?: IrTypeOperatorCallImpl( - startOffset = newCall.startOffset, - endOffset = newCall.endOffset, - type = expression.type, - operator = IrTypeOperator.IMPLICIT_CAST, - typeOperand = expression.type, - argument = newCall - ) + +irCastIfNeeded(newCall, expression.type) + }.unwrapBlock() } + private fun needsTypeArgumentOffset(declaration: IrFunction) = + isStatic(declaration) && declaration.parentAsClass.isMultiFieldValueClass && declaration is IrSimpleFunction + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { expression.transformChildrenVoid() return visitFunctionAccessExpression(expression) { @@ -362,11 +359,13 @@ open class DefaultParameterInjector( override fun visitCall(expression: IrCall): IrExpression { expression.transformChildrenVoid() + val declaration = expression.symbol.owner + val typeParametersToRemove = if (needsTypeArgumentOffset(declaration)) declaration.parentAsClass.typeParameters.size else 0 with(expression) { return visitFunctionAccessExpression(expression) { IrCallImpl( startOffset, endOffset, (it as IrSimpleFunctionSymbol).owner.returnType, it, - typeArgumentsCount = typeArgumentsCount, + typeArgumentsCount = typeArgumentsCount - typeParametersToRemove, valueArgumentsCount = it.owner.valueParameters.size, origin = LoweredStatementOrigins.DEFAULT_DISPATCH_CALL, superQualifierSymbol = superQualifierSymbol @@ -375,9 +374,7 @@ open class DefaultParameterInjector( } } - open protected fun parametersForCall(expression: IrFunctionAccessExpression): Pair>? { - val startOffset = expression.startOffset - val endOffset = expression.endOffset + private fun createStubFunction(expression: IrFunctionAccessExpression): IrFunctionSymbol? { val declaration = expression.symbol.owner // We *have* to find the actual function here since on the JVM, a default stub for a function implemented @@ -396,8 +393,14 @@ open class DefaultParameterInjector( baseFunction.copyAnnotations(), ) } ?: return null - log { "$declaration -> $stubFunction" } + return stubFunction.symbol + } + + protected open fun IrBlockBuilder.argumentsForCall(expression: IrFunctionAccessExpression, stubFunction: IrFunction): Map { + val startOffset = expression.startOffset + val endOffset = expression.endOffset + val declaration = expression.symbol.owner val realArgumentsNumber = declaration.valueParameters.filterNot { it.isMovedReceiver() }.size val maskValues = IntArray((realArgumentsNumber + 31) / 32) @@ -408,32 +411,48 @@ open class DefaultParameterInjector( } var sourceParameterIndex = -1 - val valueParametersPrefix = - if (isStatic(declaration)) + return buildMap { + val valueParametersPrefix: List = if (isStatic(declaration)) { listOfNotNull(stubFunction.dispatchReceiverParameter, stubFunction.extensionReceiverParameter) - else emptyList() - return stubFunction.symbol to (valueParametersPrefix + stubFunction.valueParameters).mapIndexed { i, parameter -> - if (!parameter.isMovedReceiver() && parameter != stubFunction.dispatchReceiverParameter && parameter != stubFunction.extensionReceiverParameter) { - ++sourceParameterIndex + } else { + stubFunction.dispatchReceiverParameter?.let { put(it, expression.dispatchReceiver) } + stubFunction.extensionReceiverParameter?.let { put(it, expression.extensionReceiver) } + listOf() } - when { - sourceParameterIndex >= realArgumentsNumber + maskValues.size -> IrConstImpl.constNull(startOffset, endOffset, parameter.type) - sourceParameterIndex >= realArgumentsNumber -> IrConstImpl.int(startOffset, endOffset, parameter.type, maskValues[sourceParameterIndex - realArgumentsNumber]) - else -> { - val valueArgument = expression.getValueArgument(i) - if (valueArgument == null) { - maskValues[sourceParameterIndex / 32] = maskValues[sourceParameterIndex / 32] or (1 shl (sourceParameterIndex % 32)) - } - valueArgument ?: nullConst(startOffset, endOffset, parameter)?.let { - IrCompositeImpl( - expression.startOffset, - expression.endOffset, - parameter.type, - IrStatementOrigin.DEFAULT_VALUE, - listOf(it) - ) + for ((i, parameter) in (valueParametersPrefix + stubFunction.valueParameters).withIndex()) { + if (!parameter.isMovedReceiver() && parameter != stubFunction.dispatchReceiverParameter && parameter != stubFunction.extensionReceiverParameter) { + ++sourceParameterIndex + } + val newArgument = when { + sourceParameterIndex >= realArgumentsNumber + maskValues.size -> IrConstImpl.constNull( + startOffset, + endOffset, + parameter.type + ) + sourceParameterIndex >= realArgumentsNumber -> IrConstImpl.int( + startOffset, + endOffset, + parameter.type, + maskValues[sourceParameterIndex - realArgumentsNumber] + ) + else -> { + val valueArgument = expression.getValueArgument(i) + if (valueArgument == null) { + maskValues[sourceParameterIndex / 32] = + maskValues[sourceParameterIndex / 32] or (1 shl (sourceParameterIndex % 32)) + } + valueArgument ?: nullConst(startOffset, endOffset, parameter)?.let { + IrCompositeImpl( + expression.startOffset, + expression.endOffset, + parameter.type, + IrStatementOrigin.DEFAULT_VALUE, + listOf(it) + ) + } } } + put(parameter, newArgument) } } } @@ -455,6 +474,8 @@ open class DefaultParameterInjector( protected open fun isStatic(function: IrFunction): Boolean = false private fun log(msg: () -> String) = context.log { "DEFAULT-INJECTOR: ${msg()}" } + + protected fun IrValueParameter.isMovedReceiver() = isMovedReceiverImpl() } // Remove default argument initializers. diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineClassDeclarationLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineClassDeclarationLowering.kt index b6733c4d505..19c1040b5b5 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineClassDeclarationLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineClassDeclarationLowering.kt @@ -374,6 +374,7 @@ class InlineClassLowering(val context: CommonBackendContext) { function.parent, function.toInlineClassImplementationName(), function, - typeParametersFromContext = extractTypeParameters(function.parentAsClass) + typeParametersFromContext = extractTypeParameters(function.parentAsClass), + remapMultiFieldValueClassStructure = context::remapMultiFieldValueClassStructure ) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultParameterInjector.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultParameterInjector.kt index 0d644075bf3..5e905774315 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultParameterInjector.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultParameterInjector.kt @@ -5,14 +5,15 @@ package org.jetbrains.kotlin.ir.backend.js.lower -import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.backend.common.lower.DefaultParameterInjector import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.utils.getVoid import org.jetbrains.kotlin.ir.backend.js.utils.jsConstructorReference +import org.jetbrains.kotlin.ir.builders.IrBlockBuilder import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrValueParameter import org.jetbrains.kotlin.ir.expressions.IrCall @@ -20,8 +21,6 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.util.copyAnnotations import org.jetbrains.kotlin.ir.util.isTopLevel import org.jetbrains.kotlin.ir.util.isVararg @@ -48,32 +47,24 @@ class JsDefaultParameterInjector(context: JsIrBackendContext) : } } - override fun parametersForCall(expression: IrFunctionAccessExpression): Pair>? { + override fun IrBlockBuilder.argumentsForCall(expression: IrFunctionAccessExpression, stubFunction: IrFunction): Map { val startOffset = expression.startOffset val endOffset = expression.endOffset - val declaration = expression.symbol.owner - val stubFunction = factory.findBaseFunctionWithDefaultArgumentsFor(declaration, skipInline, skipExternalMethods)?.let { - factory.generateDefaultsFunction( - it, - skipInline, - skipExternalMethods, - forceSetOverrideSymbols, - defaultArgumentStubVisibility(declaration), - useConstructorMarker(declaration), - it.copyAnnotations(), - ) - } ?: return null - - return stubFunction.symbol to buildList { + return buildMap { + stubFunction.dispatchReceiverParameter?.let { put(it, expression.dispatchReceiver) } + stubFunction.extensionReceiverParameter?.let { put(it, expression.extensionReceiver) } for (i in 0 until expression.valueArgumentsCount) { val declaredParameter = stubFunction.valueParameters[i] - val actualParameter = expression.getValueArgument(i) - add(actualParameter ?: nullConst(startOffset, endOffset, declaredParameter)) + val actualArgument = expression.getValueArgument(i) + put(declaredParameter, actualArgument ?: nullConst(startOffset, endOffset, declaredParameter)) } if (expression is IrCall && stubFunction.hasSuperContextParameter()) { - add(expression.superQualifierSymbol?.prototypeOf() ?: context.getVoid()) + put( + stubFunction.valueParameters[expression.valueArgumentsCount], + expression.superQualifierSymbol?.prototypeOf() ?: this@JsDefaultParameterInjector.context.getVoid() + ) } } } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt index 3fc4cd63ad4..b067439dbae 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt @@ -226,7 +226,8 @@ private class AddContinuationLowering(context: JvmBackendContext) : SuspendLower JavaDescriptorVisibilities.PACKAGE_VISIBILITY, isFakeOverride = false, copyMetadata = false, - typeParametersFromContext = extractTypeParameters(irFunction.parentAsClass) + typeParametersFromContext = extractTypeParameters(irFunction.parentAsClass), + remapMultiFieldValueClassStructure = context::remapMultiFieldValueClassStructure ) static.body = irFunction.moveBodyTo(static) // Fixup dispatch parameter to outer class 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 6d146904769..1151aee5360 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 @@ -10,9 +10,10 @@ import org.jetbrains.kotlin.backend.common.lower.SpecialMethodWithDefaultInfo import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irNot import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase -import org.jetbrains.kotlin.backend.jvm.* -import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.MultiFieldValueClassMapping -import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.RegularMapping +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements +import org.jetbrains.kotlin.backend.jvm.SpecialBridge import org.jetbrains.kotlin.backend.jvm.ir.* import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.descriptors.DescriptorVisibilities @@ -24,7 +25,10 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.isNullable +import org.jetbrains.kotlin.ir.types.isPrimitiveType +import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.name.Name @@ -598,22 +602,20 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass bridge: IrSimpleFunction, target: IrSimpleFunction, superQualifierSymbol: IrClassSymbol? = null - ) = - irCastIfNeeded(irBlock { - +irReturn(irCall(target, origin = IrStatementOrigin.BRIDGE_DELEGATION, superQualifierSymbol = superQualifierSymbol).apply { - - val targetStructure = getStructure(target) - val bridgeStructure = getStructure(bridge) - - if (targetStructure == null && bridgeStructure == null) { - for ((param, targetParam) in bridge.explicitParameters.zip(target.explicitParameters)) { - putArgument(targetParam, irGetOrCast(bridge, param, targetParam)) + ) = irCastIfNeeded(irBlock { + +irReturn(irCall(target, origin = IrStatementOrigin.BRIDGE_DELEGATION, superQualifierSymbol = superQualifierSymbol).apply { + if (getStructure(target) == null && getStructure(bridge) == null) { + for ((param, targetParam) in bridge.explicitParameters.zip(target.explicitParameters)) { + val argument = irGet(param).let { argument -> + if (param == bridge.dispatchReceiverParameter) argument else irCastIfNeeded(argument, targetParam.type.upperBound) } - } else { - this@irBlock.addBoxedAndUnboxedMfvcArguments(targetStructure, bridgeStructure, target, bridge, this) + putArgument(targetParam, argument) } - }) - }.unwrapBlock(), bridge.returnType.upperBound) + } else { + this@irBlock.addBoxedAndUnboxedMfvcArguments(target, bridge, this) + } + }) + }.unwrapBlock(), bridge.returnType.upperBound) private fun getStructure(function: IrSimpleFunction): List? { val structure = context.multiFieldValueClassReplacements.bindingNewFunctionToParameterTemplateStructure[function] ?: return null @@ -625,106 +627,22 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass } private fun IrBlockBuilder.addBoxedAndUnboxedMfvcArguments( - targetStructure: List?, - bridgeStructure: List?, target: IrSimpleFunction, bridge: IrSimpleFunction, irCall: IrCall ) { - require( - targetStructure == null || bridgeStructure == null || - bridgeStructure.size == targetStructure.size && - (targetStructure zip bridgeStructure).none { (targetParameter, bridgeParameter) -> - targetParameter is MultiFieldValueClassMapping && bridgeParameter is MultiFieldValueClassMapping && - targetParameter.rootMfvcNode != bridgeParameter.rootMfvcNode - } - ) { "Incompatible structures: $bridgeStructure and $targetStructure" } - - val targetExplicitParameters = target.explicitParameters - val bridgeExplicitParameters = bridge.explicitParameters - var targetIndex = 0 - var bridgeIndex = 0 - var structureIndex = 0 - while (targetIndex < targetExplicitParameters.size && bridgeIndex < bridgeExplicitParameters.size) { - val targetRemappedParameter = targetStructure?.get(structureIndex) - val bridgeRemappedParameter = bridgeStructure?.get(structureIndex) - when (targetRemappedParameter) { - is MultiFieldValueClassMapping -> when (bridgeRemappedParameter) { - is MultiFieldValueClassMapping -> { - require(bridgeRemappedParameter.rootMfvcNode == targetRemappedParameter.rootMfvcNode) { - "Incompatible parameters: $bridgeRemappedParameter, $targetRemappedParameter" - } - repeat(bridgeRemappedParameter.valueParameters.size) { - val bridgeParameter = bridgeExplicitParameters[bridgeIndex++] - val targetParameter = targetExplicitParameters[targetIndex++] - irCall.putArgument(targetParameter, irGetOrCast(bridge, bridgeParameter, targetParameter)) - } - } - - is RegularMapping, null -> { - val bridgeParameter = bridgeExplicitParameters[bridgeIndex++] - val targetParameterType = targetRemappedParameter.rootMfvcNode.mfvc.defaultType - val instance = targetRemappedParameter.rootMfvcNode.createInstanceFromBox( - this, - irCastIfNeeded(irGet(bridgeParameter), targetParameterType), - AccessType.ChooseEffective - ) { error("Not applicable") } - val newArguments = instance.makeFlattenedGetterExpressions(this, bridge.parentAsClass, registerPossibleExtraBoxCreation = {}) - for (newArgument in newArguments) { - irCall.putArgument(targetExplicitParameters[targetIndex++], newArgument) - } - } - } - - is RegularMapping, null -> { - val targetParameter = targetExplicitParameters[targetIndex] - when (bridgeRemappedParameter) { - is MultiFieldValueClassMapping -> { - val valueArguments = List(bridgeRemappedParameter.rootMfvcNode.leavesCount) { - irGet(bridgeExplicitParameters[bridgeIndex++]) - } - val boxCall = bridgeRemappedParameter.rootMfvcNode.makeBoxedExpression( - this, bridgeRemappedParameter.typeArguments, valueArguments, registerPossibleExtraBoxCreation = {} - ) - irCall.putArgument(targetParameter, irCastIfNeeded(boxCall, targetParameter.type.upperBound)) - } - - is RegularMapping, null -> { - val bridgeParameter = bridgeExplicitParameters[bridgeIndex++] - irCall.putArgument(targetParameter, irGetOrCast(bridge, bridgeParameter, targetParameter)) - } - } - targetIndex++ - } + val parameters2arguments = this@BridgeLowering.context.multiFieldValueClassReplacements + .mapFunctionMfvcStructures(this, target, bridge) { sourceParameter, targetParameterType -> + if (sourceParameter == bridge.dispatchReceiverParameter) irGet(sourceParameter) + else irCastIfNeeded(irGet(sourceParameter), targetParameterType) + } + for ((parameter, argument) in parameters2arguments) { + if (argument != null) { + irCall.putArgument(parameter, argument) } - structureIndex++ - } - require(targetIndex == targetExplicitParameters.size && bridgeIndex == bridgeExplicitParameters.size) { - "Incorrect bridge:\n${bridge.dump()}\n\nfor target\n${target.dump()}" - } - require((targetStructure == null || structureIndex == targetStructure.size)) { - "Invalid structure index $structureIndex for $targetStructure" - } - require((bridgeStructure == null || structureIndex == bridgeStructure.size)) { - "Invalid structure index $structureIndex for $bridgeStructure" } } - private fun IrBuilderWithScope.irGetOrCast( - bridge: IrSimpleFunction, - bridgeParameter: IrValueParameter, - targetParameter: IrValueParameter - ) = - irGet(bridgeParameter).let { argument -> - if (bridgeParameter == bridge.dispatchReceiverParameter) - argument - else - irCastIfNeeded(argument, targetParameter.type.upperBound) - } - - private fun IrBuilderWithScope.irCastIfNeeded(expression: IrExpression, to: IrType): IrExpression = - if (expression.type == to || to.isAny() || to.isNullableAny()) expression else irImplicitCast(expression, to) - private val IrFunction.jvmMethod: Method get() = context.bridgeLoweringCache.computeJvmMethod(this) } 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 eedd9b87e0c..a052b0e8e19 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,26 +12,20 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin -import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.MultiFieldValueClassMapping -import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.RegularMapping -import org.jetbrains.kotlin.backend.jvm.fullValueParameterList import org.jetbrains.kotlin.backend.jvm.ir.* -import org.jetbrains.kotlin.backend.jvm.makeBoxedExpression import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.config.JvmDefaultMode import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.builders.irBlockBody -import org.jetbrains.kotlin.ir.builders.irCall -import org.jetbrains.kotlin.ir.builders.irGet -import org.jetbrains.kotlin.ir.builders.irReturn +import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl +import org.jetbrains.kotlin.ir.expressions.putArgument import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.* @@ -96,59 +90,27 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo val classStartOffset = classOverride.parentAsClass.startOffset val backendContext = context context.createIrBuilder(irFunction.symbol, classStartOffset, classStartOffset).apply { - irFunction.body = irBlockBody { - +irReturn( - irCall(defaultImplFun.symbol, irFunction.returnType).apply { - superMethod.parentAsClass.typeParameters.forEachIndexed { index, _ -> - putTypeArgument(index, createPlaceholderAnyNType(context.irBuiltIns)) - } - passTypeArgumentsFrom(irFunction, offset = superMethod.parentAsClass.typeParameters.size) - - irFunction.dispatchReceiverParameter?.let { - putValueArgument(0, irGet(it).reinterpretAsDispatchReceiverOfType(superClassType)) - } - val bindingNewFunctionToParameterTemplateStructure = backendContext.multiFieldValueClassReplacements - .bindingNewFunctionToParameterTemplateStructure - val structure = bindingNewFunctionToParameterTemplateStructure[classOverride]?.let { structure -> - require(structure.sumOf { it.valueParameters.size } == classOverride.explicitParametersCount) { - "Bad parameters structure: $structure" - } - if (defaultImplFun.explicitParametersCount == irFunction.explicitParametersCount) { - null - } else { - require(structure.size == defaultImplFun.explicitParametersCount) { "Bad parameters structure: $structure" } - structure - } - } - require(structure == null || structure.first() is RegularMapping) { - "Dispatch receiver for method replacement cannot be flattened" - } - val sourceFullValueParameterList = irFunction.fullValueParameterList - if (structure == null) { - for ((index, parameter) in sourceFullValueParameterList.withIndex()) { - putValueArgument(1 + index, irGet(parameter)) - } - } else { - var flattenedIndex = 0 - for (i in 1 until structure.size) { - when (val remappedParameter = structure[i]) { - is MultiFieldValueClassMapping -> { - val valueArguments = remappedParameter.valueParameters.indices.map { - irGet(sourceFullValueParameterList[flattenedIndex++]) - } - val boxedExpression = remappedParameter.rootMfvcNode.makeBoxedExpression( - this@irBlockBody, remappedParameter.typeArguments, valueArguments, registerPossibleExtraBoxCreation = {} - ) - putValueArgument(i, boxedExpression) - } - - is RegularMapping -> putValueArgument(i, irGet(sourceFullValueParameterList[flattenedIndex++])) - } - } + irFunction.body = irExprBody(irBlock { + val parameter2arguments = backendContext.multiFieldValueClassReplacements + .mapFunctionMfvcStructures(this, defaultImplFun, irFunction) { sourceParameter, _ -> + irGet(sourceParameter).let { + if (sourceParameter != irFunction.dispatchReceiverParameter) it + else it.reinterpretAsDispatchReceiverOfType(superClassType) } } - ) - } + +irCall(defaultImplFun.symbol, irFunction.returnType).apply { + for (index in superMethod.parentAsClass.typeParameters.indices) { + putTypeArgument(index, createPlaceholderAnyNType(context.irBuiltIns)) + } + passTypeArgumentsFrom(irFunction, offset = superMethod.parentAsClass.typeParameters.size) + + for ((parameter, argument) in parameter2arguments) { + if (argument != null) { + putArgument(parameter, argument) + } + } + } + }) } return irFunction diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultParameterInjector.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultParameterInjector.kt index f8c2bde37f4..68993eefc12 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultParameterInjector.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultParameterInjector.kt @@ -10,11 +10,18 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.ir.defaultValue import org.jetbrains.kotlin.backend.jvm.ir.getJvmVisibilityOfDefaultArgumentStub +import org.jetbrains.kotlin.ir.builders.IrBlockBuilder import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrValueParameter import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.util.explicitParametersCount +import org.jetbrains.kotlin.ir.util.render class JvmDefaultParameterInjector(context: JvmBackendContext) : DefaultParameterInjector( context = context, @@ -40,4 +47,57 @@ class JvmDefaultParameterInjector(context: JvmBackendContext) : DefaultParameter override fun isStatic(function: IrFunction): Boolean = function.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT || function.origin == JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT + + + override fun IrBlockBuilder.argumentsForCall( + expression: IrFunctionAccessExpression, stubFunction: IrFunction + ): Map { + val startOffset = expression.startOffset + val endOffset = expression.endOffset + val declaration = expression.symbol.owner + + val realArgumentsNumber = declaration.valueParameters.filterNot { it.isMovedReceiver() }.size + val maskValues = IntArray((realArgumentsNumber + 31) / 32) + + val oldArguments: Map = buildMap { + declaration.dispatchReceiverParameter?.let { put(it, expression.dispatchReceiver) } + declaration.extensionReceiverParameter?.let { put(it, expression.extensionReceiver) } + putAll(declaration.valueParameters.mapIndexed { index, parameter -> parameter to expression.getValueArgument(index) }) + } + + val indexes = declaration.valueParameters.filterNot { it.isMovedReceiver() }.withIndex().associate { it.value to it.index } + val mainArguments = this@JvmDefaultParameterInjector.context.multiFieldValueClassReplacements + .mapFunctionMfvcStructures(this, stubFunction, declaration) { sourceParameter: IrValueParameter, targetParameterType: IrType -> + val valueArgument = oldArguments[sourceParameter] + if (valueArgument == null) { + val index = indexes[sourceParameter]!! + maskValues[index / 32] = maskValues[index / 32] or (1 shl (index % 32)) + } + valueArgument ?: IrCompositeImpl( + expression.startOffset, + expression.endOffset, + targetParameterType, + IrStatementOrigin.DEFAULT_VALUE, + listOf(nullConst(startOffset, endOffset, targetParameterType)) + ) + } + + + assert(stubFunction.explicitParametersCount - mainArguments.size - maskValues.size in listOf(0, 1)) { + "argument count mismatch: expected $realArgumentsNumber arguments + ${maskValues.size} masks + optional handler/marker, " + + "got ${stubFunction.explicitParametersCount} total in ${stubFunction.render()}" + } + + return buildMap { + putAll(mainArguments) + val restParameters = stubFunction.valueParameters.filterNot { it in mainArguments } + for ((maskParameter, maskValue) in restParameters zip maskValues.asList()) { + put(maskParameter, IrConstImpl.int(startOffset, endOffset, maskParameter.type, maskValue)) + } + if (restParameters.size > maskValues.size) { + val lastParameter = restParameters.last() + put(lastParameter, IrConstImpl.constNull(startOffset, endOffset, lastParameter.type)) + } + } + } } 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 164a1e1f238..59cd9e9bed4 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 @@ -518,15 +518,21 @@ internal class JvmMultiFieldValueClassLowering( name = mangledName returnType = source.returnType }.apply { - val isAnyOverriddenReplaced = replacement.overriddenSymbols.any { - it.owner in replacements.bindingNewFunctionToParameterTemplateStructure + val implementationStructure = if (isFakeOverride) { + val superDeclaration = replacement.allOverridden().singleOrNull { it.body != null } + ?: error("${this.render()} is fake override and has no implementation") + replacements.bindingNewFunctionToParameterTemplateStructure[superDeclaration] + ?: superDeclaration.explicitParameters.map { RegularMapping(it) } + } else { + replacements.bindingNewFunctionToParameterTemplateStructure[replacement] + ?: error("${replacement.render()} must have MFVC structure") } - if (source.parentAsClass.isMultiFieldValueClass && isAnyOverriddenReplaced) { - copyTypeParametersFrom(source) // without static type parameters - val substitutionMap = makeTypeParameterSubstitutionMap(source, this) - dispatchReceiverParameter = source.dispatchReceiverParameter!!.let { // source!!! - it.copyTo(this, type = it.type.substitute(substitutionMap)) - } + copyTypeParametersFrom(source) // without static type parameters + val substitutionMap = makeTypeParameterSubstitutionMap(source, this) + dispatchReceiverParameter = source.dispatchReceiverParameter!!.let { // source!!! + it.copyTo(this, type = it.type.substitute(substitutionMap)) + } + if ((source.parent as? IrClass)?.isMultiFieldValueClass == true) { require(replacement.dispatchReceiverParameter == null) { """ Ambiguous receivers: @@ -534,19 +540,32 @@ internal class JvmMultiFieldValueClassLowering( ${replacement.dispatchReceiverParameter!!.render()} """.trimIndent() } - require(replacement.extensionReceiverParameter == null) { - "Static replacement must have no extension receiver but ${replacement.extensionReceiverParameter!!.render()} found" - } - val replacementStructure = replacements.bindingNewFunctionToParameterTemplateStructure[replacement]!! - val offset = replacementStructure[0].valueParameters.size - valueParameters = replacement.valueParameters.drop(offset).map { - it.copyTo(this, type = it.type.substitute(substitutionMap), index = it.index - offset) - } - val parametersMapping = (replacement.valueParameters.drop(offset) zip valueParameters).toMap() - context.remapMultiFieldValueClassStructure(replacement, this, parametersMapping) - } else { - copyParameterDeclarationsFrom(source) } + require(replacement.extensionReceiverParameter == null) { + "Static replacement must have no extension receiver but ${replacement.extensionReceiverParameter!!.render()} found" + } + val structure = buildList(implementationStructure.size) { + var valueParameterIndex = 0 + add(RegularMapping(dispatchReceiverParameter!!)) + val valueParametersAsMutableList = mutableListOf() + for (expectedParameterStructure in implementationStructure.asSequence().drop(this.size)) { + fun IrValueParameter.copy() = + copyTo(this@apply, type = type.substitute(substitutionMap), index = valueParameterIndex++) + + val parameterStructure = when (expectedParameterStructure) { + is RegularMapping -> RegularMapping(expectedParameterStructure.valueParameter.copy()) + is MultiFieldValueClassMapping -> MultiFieldValueClassMapping( + expectedParameterStructure.rootMfvcNode, + substitutionMap, + expectedParameterStructure.valueParameters.map { it.copy() } + ) + } + add(parameterStructure) + valueParametersAsMutableList.addAll(parameterStructure.valueParameters) + } + valueParameters = valueParametersAsMutableList + } + replacements.bindingNewFunctionToParameterTemplateStructure[this] = structure 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 @@ -557,110 +576,17 @@ internal class JvmMultiFieldValueClassLowering( override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) { allScopes.push(createScope(source)) - source.body = context.createJvmIrBuilder(source.symbol).run { - val sourceExplicitParameters = source.explicitParameters - val targetExplicitParameters = target.explicitParameters + source.body = with(context.createJvmIrBuilder(source.symbol)) { irExprBody(irBlock { + val parameters2arguments = replacements.mapFunctionMfvcStructures(this, target, source) { sourceParameter, _ -> + irGet(sourceParameter) + } +irReturn(irCall(target).apply { passTypeArgumentsWithOffsets(target, source) { source.typeParameters[it].defaultType } - val sourceStructure: List? = replacements.bindingNewFunctionToParameterTemplateStructure[source] - val targetStructure: List? = replacements.bindingNewFunctionToParameterTemplateStructure[target] - val errorMessage = { - """ - Incompatible structures for - Source: $sourceStructure - ${source.render()} - Target: $targetStructure - ${target.render()} - """.trimIndent() - } - when (targetStructure) { - null -> when (sourceStructure) { - null -> require(targetExplicitParameters.size == sourceExplicitParameters.size, errorMessage) - else -> { - require(targetExplicitParameters.size == sourceStructure.size, errorMessage) - require(sourceExplicitParameters.size == sourceStructure.sumOf { it.valueParameters.size }, errorMessage) - } + for ((parameter, argument) in parameters2arguments) { + if (argument != null) { + putArgument(parameter, argument) } - - else -> when (sourceStructure) { - null -> { - require(targetStructure.size == sourceExplicitParameters.size, errorMessage) - require(targetStructure.sumOf { it.valueParameters.size } == targetExplicitParameters.size, errorMessage) - } - - else -> { - require(targetStructure.size == sourceStructure.size, errorMessage) - require(sourceStructure.sumOf { it.valueParameters.size } == sourceExplicitParameters.size, errorMessage) - require(targetStructure.sumOf { it.valueParameters.size } == targetExplicitParameters.size, errorMessage) - require((targetStructure zip sourceStructure).none { (t, s) -> - t is MultiFieldValueClassMapping && s is MultiFieldValueClassMapping && t.rootMfvcNode != s.rootMfvcNode - }, errorMessage) - } - } - } - val structuresSizes = sourceStructure?.size ?: targetStructure?.size ?: targetExplicitParameters.size - var flattenedSourceIndex = 0 - var flattenedTargetIndex = 0 - for (i in 0 until structuresSizes) { - val remappedSourceParameter = sourceStructure?.get(i) - val remappedTargetParameter = targetStructure?.get(i) - when (remappedSourceParameter) { - is MultiFieldValueClassMapping -> { - when (remappedTargetParameter) { - is MultiFieldValueClassMapping -> { - require(remappedTargetParameter.valueParameters.size == remappedSourceParameter.valueParameters.size) { - "Incompatible structures: $remappedTargetParameter, $remappedSourceParameter" - } - repeat(remappedTargetParameter.valueParameters.size) { - putArgument( - targetExplicitParameters[flattenedTargetIndex++], - irGet(sourceExplicitParameters[flattenedSourceIndex++]) - ) - } - } - - is RegularMapping, null -> { - val valueArguments = sourceExplicitParameters - .slice(flattenedSourceIndex until flattenedSourceIndex + remappedSourceParameter.valueParameters.size) - .map { irGet(it) } - val targetParameter = targetExplicitParameters[flattenedTargetIndex++] - val boxedExpression = remappedSourceParameter.rootMfvcNode.makeBoxedExpression( - this@irBlock, - remappedSourceParameter.typeArguments, - valueArguments, - ::registerPossibleExtraBoxUsage - ) - putArgument(targetParameter, boxedExpression) - .also { flattenedSourceIndex += remappedSourceParameter.valueParameters.size } - } - } - } - - is RegularMapping, null -> when (remappedTargetParameter) { - is MultiFieldValueClassMapping -> { - val receiver = sourceExplicitParameters[flattenedSourceIndex++] - val rootNode = remappedTargetParameter.rootMfvcNode - val instance = rootNode.createInstanceFromBox( - this@irBlock, irGet(receiver), AccessType.ChooseEffective, ::variablesSaver, - ) - val flattenedExpressions = instance.makeFlattenedGetterExpressions( - this@irBlock, irCurrentClass, ::registerPossibleExtraBoxUsage - ) - for (expression in flattenedExpressions) { - putArgument(targetExplicitParameters[flattenedTargetIndex++], expression) - } - } - - else -> putArgument( - targetExplicitParameters[flattenedTargetIndex++], - irGet(sourceExplicitParameters[flattenedSourceIndex++]) - ) - } - } - } - require(flattenedTargetIndex == targetExplicitParameters.size && flattenedSourceIndex == sourceExplicitParameters.size) { - "Incorrect source:\n${source.dump()}\n\nfor target\n${target.dump()}" } }) }) @@ -1076,7 +1002,9 @@ internal class JvmMultiFieldValueClassLowering( originalFunction: IrFunction, original: IrMemberAccessExpression<*>, replacement: IrFunction, - makeMemberAccessExpression: (IrFunctionSymbol) -> IrMemberAccessExpression<*> = ::irCall, + makeMemberAccessExpression: (IrFunctionSymbol) -> IrMemberAccessExpression<*> = { callee: IrFunctionSymbol -> + irCall(callee.owner, superQualifierSymbol = (original as? IrCall)?.superQualifierSymbol) + }, ): IrMemberAccessExpression<*> { val parameter2expression = typedArgumentList(originalFunction, original) val structure = replacements.bindingOldFunctionToParameterTemplateStructure[originalFunction]!! @@ -1302,6 +1230,9 @@ internal class JvmMultiFieldValueClassLowering( if (expression is IrConstructorCall) expression.symbol.owner.constructedClass else expression.type.erasedUpperBound ) val type = if (expression is IrConstructorCall) expression.symbol.owner.constructedClass.defaultType else expression.type + if (type == context.irBuiltIns.nothingType) { + return flattenExpressionTo(irImplicitCast(expression, instance.type), instance) + } val lowering = this@JvmMultiFieldValueClassLowering if (rootNode == null || !type.needsMfvcFlattening() || instance.size == 1) { require(instance.size == 1) { "Required 1 variable/field to store regular value but got ${instance.size}" } 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 0336c40d581..e8d00c93a52 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 @@ -211,7 +211,7 @@ internal abstract class JvmValueClassAbstractLowering( } ) - // Update the overridden symbols to point to their inline class replacements + // Update the overridden symbols to point to their value class replacements bridgeFunction.overriddenSymbols = replacement.overriddenSymbols // Replace the function body with a wrapper diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/StaticDefaultFunctionLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/StaticDefaultFunctionLowering.kt index ad659da4bc5..8380100ba30 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/StaticDefaultFunctionLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/StaticDefaultFunctionLowering.kt @@ -80,6 +80,11 @@ private class StaticDefaultFunctionLowering(val context: JvmBackendContext) : Ir private fun getStaticFunctionWithReceivers(function: IrSimpleFunction): IrSimpleFunction = context.staticDefaultStubs.getOrPut(function.symbol) { - context.irFactory.createStaticFunctionWithReceivers(function.parent, function.name, function) + context.irFactory.createStaticFunctionWithReceivers( + function.parent, + function.name, + function, + remapMultiFieldValueClassStructure = context::remapMultiFieldValueClassStructure + ) } } 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 e523a83a960..f1d2033a79d 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 @@ -220,7 +220,8 @@ class JvmCachedDeclarations( modality = Modality.OPEN, visibility = defaultImplsVisibility, isFakeOverride = false, - typeParametersFromContext = parent.typeParameters + typeParametersFromContext = parent.typeParameters, + remapMultiFieldValueClassStructure = context::remapMultiFieldValueClassStructure ).also { it.copyCorrespondingPropertyFrom(interfaceFun) @@ -257,7 +258,6 @@ class JvmCachedDeclarations( defaultImplsRedirections.getOrPut(fakeOverride) { assert(fakeOverride.isFakeOverride) val irClass = fakeOverride.parentAsClass - val mfvcReplacementStructure = context.multiFieldValueClassReplacements.bindingNewFunctionToParameterTemplateStructure val redirectFunction = context.irFactory.buildFun { origin = JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE name = fakeOverride.name @@ -282,7 +282,7 @@ class JvmCachedDeclarations( annotations = fakeOverride.annotations copyCorrespondingPropertyFrom(fakeOverride) } - mfvcReplacementStructure[fakeOverride]?.let { mfvcReplacementStructure[redirectFunction] = it } + context.remapMultiFieldValueClassStructure(fakeOverride, redirectFunction, parametersMappingOrNull = null) redirectFunction } 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 48e194d475e..b8c54aa3a76 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 @@ -5,23 +5,32 @@ package org.jetbrains.kotlin.backend.jvm +import org.jetbrains.kotlin.backend.common.lower.parents import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.MultiFieldValueClassMapping +import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.RegularMapping import org.jetbrains.kotlin.backend.jvm.ir.* import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.builders.IrBlockBuilder import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor +import org.jetbrains.kotlin.ir.builders.irComposite import org.jetbrains.kotlin.ir.builders.irExprBody import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrComposite import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.defaultType +import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import java.util.concurrent.ConcurrentHashMap /** @@ -43,7 +52,7 @@ class MemoizedMultiFieldValueClassReplacements( originWhenFlattened: IrDeclarationOrigin, ): RemappedParameter { val oldParam = this - if (!type.needsMfvcFlattening()) return RemappedParameter.RegularMapping( + if (!type.needsMfvcFlattening()) return RegularMapping( targetFunction.addValueParameter { updateFrom(oldParam) this.name = oldParam.name @@ -238,7 +247,7 @@ class MemoizedMultiFieldValueClassReplacements( dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this, index = -1) val newFlattenedParameters = makeAndAddGroupedValueParametersFrom(function, includeDispatcherReceiver = false, mapOf(), this) val receiver = dispatchReceiverParameter - return if (receiver != null) listOf(RemappedParameter.RegularMapping(receiver)) + newFlattenedParameters else newFlattenedParameters + return if (receiver != null) listOf(RegularMapping(receiver)) + newFlattenedParameters else newFlattenedParameters } /** @@ -369,4 +378,107 @@ class MemoizedMultiFieldValueClassReplacements( } private val IrProperty.backingFieldIfNotToRemove get() = backingField?.takeUnless { it in getFieldsToRemove(this.parentAsClass) } + + @Suppress("ClassName") + private object FLATTENED_NOTHING_DEFAULT_VALUE : IrStatementOriginImpl("FLATTENED_NOTHING_DEFAULT_VALUE") + + fun mapFunctionMfvcStructures( + irBuilder: IrBlockBuilder, + targetFunction: IrFunction, + sourceFunction: IrFunction, + getArgument: (sourceParameter: IrValueParameter, targetParameterType: IrType) -> IrExpression? + ): Map { + val targetStructure = bindingNewFunctionToParameterTemplateStructure[targetFunction] + ?: targetFunction.explicitParameters.map { RegularMapping(it) } + val sourceStructure = bindingNewFunctionToParameterTemplateStructure[sourceFunction] + ?: sourceFunction.explicitParameters.map { RegularMapping(it) } + verifyStructureCompatibility(targetStructure, sourceStructure) + return buildMap { + for ((targetParameterStructure, sourceParameterStructure) in targetStructure zip sourceStructure) { + when (targetParameterStructure) { + is RegularMapping -> when (sourceParameterStructure) { + is RegularMapping -> put( + targetParameterStructure.valueParameter, + getArgument(sourceParameterStructure.valueParameter, targetParameterStructure.valueParameter.type) + ) + is MultiFieldValueClassMapping -> { + val valueArguments = sourceParameterStructure.valueParameters.map { + getArgument(it, it.type) ?: error("Expected an argument for $sourceParameterStructure") + } + val newArgument = + if (valueArguments.all { it is IrComposite && it.origin == FLATTENED_NOTHING_DEFAULT_VALUE }) { + (valueArguments[0] as IrComposite).statements[0] as IrExpression + } else { + sourceParameterStructure.rootMfvcNode.makeBoxedExpression( + irBuilder, + sourceParameterStructure.typeArguments, + valueArguments, + registerPossibleExtraBoxCreation = {} + ) + } + put(targetParameterStructure.valueParameter, newArgument) + } + } + is MultiFieldValueClassMapping -> when (sourceParameterStructure) { + is RegularMapping -> with(irBuilder) { + val argument = getArgument(sourceParameterStructure.valueParameter, targetParameterStructure.boxedType) + ?: error("Expected an argument for $sourceParameterStructure") + if (sourceParameterStructure.valueParameter.type.isNothing()) { + for ((index, parameter) in targetParameterStructure.valueParameters.withIndex()) { + put(parameter, irComposite(origin = FLATTENED_NOTHING_DEFAULT_VALUE) { + if (index == 0) +argument + +parameter.type.defaultValue( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, this@MemoizedMultiFieldValueClassReplacements.context + ) + }) + } + } else { + val instance = targetParameterStructure.rootMfvcNode.createInstanceFromBox( + scope = this, + receiver = argument, + accessType = AccessType.ChooseEffective, + saveVariable = { +it } + ) + val arguments = instance.makeFlattenedGetterExpressions( + this, sourceFunction.parents.firstIsInstance(), registerPossibleExtraBoxCreation = {} + ) + for ((targetParameter, expression) in targetParameterStructure.valueParameters zip arguments) { + put(targetParameter, expression) + } + } + } + is MultiFieldValueClassMapping -> { + for ((sourceParameter, targetParameter) in sourceParameterStructure.valueParameters zip targetParameterStructure.valueParameters) { + put(targetParameter, getArgument(sourceParameter, targetParameter.type)) + } + } + } + } + } + } + } + + @OptIn(ExperimentalStdlibApi::class) + private fun verifyStructureCompatibility( + targetStructure: List, + sourceStructure: List + ) { + for ((targetParameterStructure, sourceParameterStructure) in targetStructure zip sourceStructure) { + if (targetParameterStructure is MultiFieldValueClassMapping && sourceParameterStructure is MultiFieldValueClassMapping) { + require(targetParameterStructure.rootMfvcNode.mfvc.classId == sourceParameterStructure.rootMfvcNode.mfvc.classId) { + "Incompatible parameter structures:\n$targetParameterStructure inside $targetStructure\n$sourceParameterStructure inside $sourceStructure" + } + } + } + for (extraParameterStructure in targetStructure.slice(sourceStructure.size.. +/** + * Instance-agnostic tree node describing structure of multi-field value class + */ sealed interface MfvcNode { val type: IrType val leavesCount: Int + /** + * Create instance-specific [ReceiverBasedMfvcNodeInstance] from instance-agnostic [MfvcNode] using a boxed [receiver] as data source. + */ fun createInstanceFromBox( scope: IrBlockBuilder, typeArguments: TypeArguments, @@ -40,6 +46,9 @@ sealed interface MfvcNode { ): ReceiverBasedMfvcNodeInstance } +/** + * Create instance-specific [ReceiverBasedMfvcNodeInstance] from instance-agnostic [MfvcNode] using a boxed [receiver] as data source. + */ fun MfvcNode.createInstanceFromBox( scope: IrBlockBuilder, receiver: IrExpression, @@ -48,10 +57,16 @@ fun MfvcNode.createInstanceFromBox( ) = createInstanceFromBox(scope, makeTypeArgumentsFromType(receiver.type as IrSimpleType), receiver, accessType, saveVariable) +/** + * Create instance-specific [ValueDeclarationMfvcNodeInstance] from instance-agnostic [MfvcNode] using new flattened variables as data source. + */ fun MfvcNode.createInstanceFromValueDeclarationsAndBoxType( scope: IrBuilderWithScope, type: IrSimpleType, name: Name, saveVariable: (IrVariable) -> Unit, isVar: Boolean ): ValueDeclarationMfvcNodeInstance = createInstanceFromValueDeclarations(scope, makeTypeArgumentsFromType(type), name, saveVariable, isVar) +/** + * Create instance-specific [ValueDeclarationMfvcNodeInstance] from instance-agnostic [MfvcNode] using new flattened variables as data source. + */ fun MfvcNode.createInstanceFromValueDeclarations( scope: IrBuilderWithScope, typeArguments: TypeArguments, name: Name, saveVariable: (IrVariable) -> Unit, isVar: Boolean ): ValueDeclarationMfvcNodeInstance { @@ -67,6 +82,9 @@ fun MfvcNode.createInstanceFromValueDeclarations( return ValueDeclarationMfvcNodeInstance(this, typeArguments, valueDeclarations) } +/** + * Create instance-specific [ValueDeclarationMfvcNodeInstance] from instance-agnostic [MfvcNode] using flattened [fieldValues] as data source. + */ fun MfvcNode.createInstanceFromValueDeclarationsAndBoxType( type: IrSimpleType, fieldValues: List ): ValueDeclarationMfvcNodeInstance = @@ -83,19 +101,41 @@ fun makeTypeArgumentsFromType(type: IrSimpleType): TypeArguments { } +/** + * Non-root [MfvcNode]. It contains an unbox method and a name. + */ sealed interface NameableMfvcNode : MfvcNode { val namedNodeImpl: NameableMfvcNodeImpl val hasPureUnboxMethod: Boolean } +/** + * List of names of the root node of the [NameableMfvcNode] up to the node. + */ val NameableMfvcNode.nameParts: List get() = namedNodeImpl.nameParts + +/** + * The last [nameParts] which distinguishes the [NameableMfvcNode] from its parent. + */ val NameableMfvcNode.name: Name get() = nameParts.last() + +/** + * Unbox method of the [NameableMfvcNode]. + */ val NameableMfvcNode.unboxMethod: IrSimpleFunction get() = namedNodeImpl.unboxMethod + +/** + * An unbox function or getter function method name of the [NameableMfvcNode]. + */ val NameableMfvcNode.fullMethodName: Name get() = namedNodeImpl.fullMethodName + +/** + * A field name corresponding to the [NameableMfvcNode]. + */ val NameableMfvcNode.fullFieldName: Name get() = namedNodeImpl.fullFieldName @@ -138,7 +178,9 @@ fun MfvcNode.getSubnodeAndIndices(name: Name): Pair? val indices = subnodeIndices[node] ?: error("existing node without indices") return node to indices } - +/** + * Non-leaf [MfvcNode]. It contains a box method and children. + */ sealed class MfvcNodeWithSubnodes(val subnodes: List) : MfvcNode { abstract override val type: IrSimpleType abstract val boxMethod: IrSimpleFunction @@ -159,6 +201,9 @@ sealed class MfvcNodeWithSubnodes(val subnodes: List) : MfvcNo } } + /** + * Get child by [name]. + */ operator fun get(name: Name): NameableMfvcNode? = mapping[name] val leaves: List = subnodes.leaves @@ -178,6 +223,9 @@ sealed class MfvcNodeWithSubnodes(val subnodes: List) : MfvcNo } +/** + * Creates a box expression for the given [MfvcNodeWithSubnodes] by calling box methods with the given [typeArguments] and [valueArguments]. + */ fun MfvcNodeWithSubnodes.makeBoxedExpression( scope: IrBuilderWithScope, typeArguments: TypeArguments, @@ -195,6 +243,9 @@ fun MfvcNodeWithSubnodes.makeBoxedExpression( registerPossibleExtraBoxCreation() } +/** + * A shortcut to get children by name several times. + */ operator fun MfvcNodeWithSubnodes.get(names: List): MfvcNode? { var cur: MfvcNode = this for (name in names) { @@ -267,6 +318,9 @@ private fun validateGettingAccessorParameters(function: IrSimpleFunction) { require(function.typeParameters.isEmpty()) { "Type parameters are not expected for ${function.render()}" } } +/** + * [MfvcNode] which corresponds to non-MFVC field which is a field of some MFVC. + */ class LeafMfvcNode( override val type: IrType, methodFullNameMode: MethodFullNameMode, @@ -310,6 +364,9 @@ val MfvcNode.fields is LeafMfvcNode -> field?.let(::listOf) } +/** + * [MfvcNode] which corresponds to MFVC field which is a field of some class. + */ class IntermediateMfvcNode( override val type: IrSimpleType, methodFullNameMode: MethodFullNameMode, @@ -373,7 +430,9 @@ fun IrSimpleFunction.getGetterField(): IrField? { val statement = (body?.statements?.singleOrNull() as? IrReturn)?.value as? IrGetField ?: return null return statement.symbol.owner } - +/** + * [MfvcNode] which corresponds to MFVC itself. + */ class RootMfvcNode internal constructor( val mfvc: IrClass, subnodes: List, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeInstance.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeInstance.kt index 029dfeb5516..e233e92671c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeInstance.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeInstance.kt @@ -18,29 +18,52 @@ import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name +/** + * Instance-specific tree node describing structure of multi-field value class corresponding to the [MfvcNode] + */ interface MfvcNodeInstance { val node: MfvcNode val typeArguments: TypeArguments val type: IrSimpleType + /** + * Make expressions corresponding to the flattened representation of the [MfvcNodeInstance]. + */ fun makeFlattenedGetterExpressions( scope: IrBlockBuilder, currentClass: IrClass, registerPossibleExtraBoxCreation: () -> Unit ): List + /** + * Make expression that corresponds to read access of the instance + */ fun makeGetterExpression(scope: IrBuilderWithScope, currentClass: IrClass, registerPossibleExtraBoxCreation: () -> Unit): IrExpression + + /** + * Get child [MfvcNodeInstance] by [name] + */ operator fun get(name: Name): MfvcNodeInstance? - fun makeStatements(scope: IrBuilderWithScope, values: List): List + + /** + * Make setter statements corresponding assignments to the [values] of the given flattened representation. + */ + fun makeSetterStatements(scope: IrBuilderWithScope, values: List): List } private fun makeTypeFromMfvcNodeAndTypeArguments(node: MfvcNode, typeArguments: TypeArguments) = node.type.substitute(typeArguments) as IrSimpleType +/** + * Make and add setter statements corresponding assignments to the [values] of the given flattened representation. + */ fun MfvcNodeInstance.addSetterStatements(scope: IrBlockBuilder, values: List) = with(scope) { - for (statement in makeStatements(this, values)) { + for (statement in makeSetterStatements(this, values)) { +statement } } +/** + * Make a block of setter statements corresponding assignments to the [values] of the given flattened representation. + */ fun MfvcNodeInstance.makeSetterExpressions(scope: IrBuilderWithScope, values: List): IrExpression = scope.irBlock { addSetterStatements(this, values) } @@ -49,6 +72,9 @@ private fun MfvcNodeInstance.checkValuesCount(values: List) { require(values.size == node.leavesCount) { "Node $node requires ${node.leavesCount} values but got ${values.map { it.render() }}" } } +/** + * [MfvcNodeInstance] that stores flattened instance in variables and parameters. + */ class ValueDeclarationMfvcNodeInstance( override val node: MfvcNode, override val typeArguments: TypeArguments, @@ -77,7 +103,7 @@ class ValueDeclarationMfvcNodeInstance( return ValueDeclarationMfvcNodeInstance(newNode, typeArguments, valueDeclarations.slice(indices)) } - override fun makeStatements(scope: IrBuilderWithScope, values: List): List { + override fun makeSetterStatements(scope: IrBuilderWithScope, values: List): List { checkValuesCount(values) return valueDeclarations.zip(values) { declaration, value -> scope.irSet(declaration, value) } } @@ -143,6 +169,9 @@ fun IrExpression?.isRepeatableAccessor(): Boolean = isRepeatableGetter() || isRe enum class AccessType { UseFields, ChooseEffective } +/** + * [MfvcNodeInstance] that stores boxed instance in variables and parameters. + */ class ReceiverBasedMfvcNodeInstance( private val scope: IrBlockBuilder, override val node: MfvcNode, @@ -235,7 +264,7 @@ class ReceiverBasedMfvcNodeInstance( return newNode.createInstanceFromBox(scope, typeArguments, makeReceiverCopy(), accessType, saveVariable) } - override fun makeStatements(scope: IrBuilderWithScope, values: List): List { + override fun makeSetterStatements(scope: IrBuilderWithScope, values: List): List { checkValuesCount(values) require(fields != null) { "$node is immutable as it has custom getter and so no backing fields" } return fields.zip(values) { field, expr -> scope.irSetField(makeReceiverCopy(), field, expr) } @@ -245,8 +274,6 @@ class ReceiverBasedMfvcNodeInstance( val MfvcNodeInstance.size: Int get() = node.leavesCount -fun IrContainerExpression.unwrapBlock(): IrExpression = statements.singleOrNull() as? IrExpression ?: this - /** * Creates a variable and doesn't add it to a container. It saves the variable with given saveVariable. * diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt index e174167e940..96f84a66f2e 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt @@ -110,7 +110,8 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT val newFun = context.irFactory.createStaticFunctionWithReceivers( function.parent, name = Name.identifier(function.name.asStringStripSpecialMarkers() + "__externalAdapter"), - function + function, + remapMultiFieldValueClassStructure = context::remapMultiFieldValueClassStructure ) function.valueParameters.forEachIndexed { index, newParameter -> @@ -157,7 +158,8 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT val newFun = context.irFactory.createStaticFunctionWithReceivers( function.parent, name = Name.identifier(function.name.asStringStripSpecialMarkers() + "__JsExportAdapter"), - function + function, + remapMultiFieldValueClassStructure = context::remapMultiFieldValueClassStructure ) newFun.valueParameters.forEachIndexed { index, newParameter -> diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt index 465a25ee1f6..82837cbeba9 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt @@ -7,10 +7,12 @@ package org.jetbrains.kotlin.ir.util import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.* +import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.declarations.addConstructor import org.jetbrains.kotlin.ir.builders.declarations.buildClass import org.jetbrains.kotlin.ir.builders.declarations.buildReceiverParameter import org.jetbrains.kotlin.ir.builders.declarations.buildTypeParameter +import org.jetbrains.kotlin.ir.builders.irImplicitCast import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* @@ -1183,7 +1185,8 @@ fun IrFactory.createStaticFunctionWithReceivers( visibility: DescriptorVisibility = oldFunction.visibility, isFakeOverride: Boolean = oldFunction.isFakeOverride, copyMetadata: Boolean = true, - typeParametersFromContext: List = listOf() + typeParametersFromContext: List = listOf(), + remapMultiFieldValueClassStructure: (IrFunction, IrFunction, Map?) -> Unit ): IrSimpleFunction { return createFunction( oldFunction.startOffset, oldFunction.endOffset, @@ -1271,12 +1274,19 @@ fun IrFactory.createStaticFunctionWithReceivers( ) } + remapMultiFieldValueClassStructure(oldFunction, this, null) + if (copyMetadata) metadata = oldFunction.metadata copyAttributes(oldFunction as? IrAttributeContainer) } } +fun IrBuilderWithScope.irCastIfNeeded(expression: IrExpression, to: IrType): IrExpression = + if (expression.type == to || to.isAny() || to.isNullableAny()) expression else irImplicitCast(expression, to) + +fun IrContainerExpression.unwrapBlock(): IrExpression = statements.singleOrNull() as? IrExpression ?: this + /** * Appends the parameters in [contextParameters] to the type parameters of * [this] function, renaming those that may clash with a provided collection of diff --git a/compiler/testData/codegen/box/valueClasses/nothingAsParameterType.kt b/compiler/testData/codegen/box/valueClasses/nothingAsParameterType.kt new file mode 100644 index 00000000000..91c3090063c --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/nothingAsParameterType.kt @@ -0,0 +1,50 @@ +// WITH_STDLIB +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses +// TARGET_BACKEND: JVM_IR + +interface I { + fun f(x: T) = println(x) + fun g(x: T = error("OK")) = println(x) + fun T.f(x: T) = println(x) + fun T.g(x: T = error("OK")) = println(x) +} + +@JvmInline +value class A(val x: Int, val y: Int): I + +@JvmInline +value class B(val x: Int, val y: Int): I { + override fun f(x: Nothing) = println(this) + override fun g(x: Nothing) = println(this) + fun h(x: Nothing) = println(this) + fun t(x: Nothing = error("OK")) = println(this) + override fun Nothing.f(x: Nothing) = println(this@B) + override fun Nothing.g(x: Nothing) = println(this@B) + fun Nothing.h(x: Nothing) = println(this@B) + fun Nothing.t(x: Nothing = error("OK")) = println(this@B) +} + +fun box(): String { + require(runCatching { A(1, 2).f(error("OK1")) }.exceptionOrNull()!!.message!! == "OK1") + require(runCatching { B(1, 2).f(error("OK2")) }.exceptionOrNull()!!.message!! == "OK2") + require(runCatching { A(1, 2).g(error("OK3")) }.exceptionOrNull()!!.message!! == "OK3") + require(runCatching { B(1, 2).g(error("OK4")) }.exceptionOrNull()!!.message!! == "OK4") + require(runCatching { A(1, 2).g() }.exceptionOrNull()!!.message!! == "OK") + require(runCatching { B(1, 2).g() }.exceptionOrNull()!!.message!! == "OK") + require(runCatching { B(1, 2).h(error("OK5")) }.exceptionOrNull()!!.message!! == "OK5") + require(runCatching { B(1, 2).t(error("OK6")) }.exceptionOrNull()!!.message!! == "OK6") + require(runCatching { B(1, 2).t() }.exceptionOrNull()!!.message!! == "OK") + + require(runCatching { A(1, 2).run { error("OK1").f(error("OK")) } }.exceptionOrNull()!!.message!! == "OK1") + require(runCatching { B(1, 2).run { error("OK2").f(error("OK")) } }.exceptionOrNull()!!.message!! == "OK2") + require(runCatching { A(1, 2).run { error("OK3").g(error("OK")) } }.exceptionOrNull()!!.message!! == "OK3") + require(runCatching { B(1, 2).run { error("OK4").g(error("OK")) } }.exceptionOrNull()!!.message!! == "OK4") + require(runCatching { A(1, 2).run { error("OK5").g() } }.exceptionOrNull()!!.message!! == "OK5") + require(runCatching { B(1, 2).run { error("OK6").g() } }.exceptionOrNull()!!.message!! == "OK6") + require(runCatching { B(1, 2).run { error("OK7").h(error("OK")) } }.exceptionOrNull()!!.message!! == "OK7") + require(runCatching { B(1, 2).run { error("OK8").t(error("OK")) } }.exceptionOrNull()!!.message!! == "OK8") + require(runCatching { B(1, 2).run { error("OK9").t() } }.exceptionOrNull()!!.message!! == "OK9") + + return "OK" +} diff --git a/compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameter.kt b/compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameter.kt index 598c7504ffc..df5c4674c30 100644 --- a/compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameter.kt +++ b/compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameter.kt @@ -2,9 +2,7 @@ // WORKS_WHEN_VALUE_CLASS // LANGUAGE: +ValueClasses -// TARGET_BACKEND: JVM -// IGNORE_BACKEND: JVM_IR -// Depends on KT-57973 +// TARGET_BACKEND: JVM_IR // JVM_TARGET: 1.8 interface Path { diff --git a/compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameterGeneric.kt b/compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameterGeneric.kt index 8c9aac37e72..83978f3c8a7 100644 --- a/compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameterGeneric.kt +++ b/compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameterGeneric.kt @@ -2,9 +2,7 @@ // WORKS_WHEN_VALUE_CLASS // LANGUAGE: +ValueClasses, +GenericInlineClassParameter -// TARGET_BACKEND: JVM -// IGNORE_BACKEND: JVM_IR -// Depends on KT-57973 +// TARGET_BACKEND: JVM_IR // JVM_TARGET: 1.8 interface Path { diff --git a/compiler/testData/codegen/box/valueClasses/overrides.txt b/compiler/testData/codegen/box/valueClasses/overrides.txt index 3999c97168b..79b180d4f1d 100644 --- a/compiler/testData/codegen/box/valueClasses/overrides.txt +++ b/compiler/testData/codegen/box/valueClasses/overrides.txt @@ -154,11 +154,11 @@ public final class GenericFakeOverrideMFVC { public method hashCode(): int public static method hashCode-impl(p0: double, p1: double): int public synthetic bridge method setP(p0: java.lang.Object): void - public method setP-nuuzChU(@org.jetbrains.annotations.NotNull p0: DPoint): void public static method setP-nuuzChU(p0: double, p1: double, p2: double, p3: double): void + public method setP-nuuzChU(p0: java.lang.Object): void public synthetic bridge method setP1(p0: java.lang.Object): void - public method setP1-nuuzChU(@org.jetbrains.annotations.NotNull p0: DPoint): void public static method setP1-nuuzChU(p0: double, p1: double, p2: double, p3: double): void + public method setP1-nuuzChU(p0: java.lang.Object): void public @org.jetbrains.annotations.NotNull method toString(): java.lang.String public static method toString-impl(p0: double, p1: double): java.lang.String public synthetic final method unbox-impl-field1(): double diff --git a/compiler/testData/codegen/box/valueClasses/overrides_inlineClass.txt b/compiler/testData/codegen/box/valueClasses/overrides_inlineClass.txt index 804cf7e33d6..81b9670d082 100644 --- a/compiler/testData/codegen/box/valueClasses/overrides_inlineClass.txt +++ b/compiler/testData/codegen/box/valueClasses/overrides_inlineClass.txt @@ -154,11 +154,11 @@ public final class GenericFakeOverrideMFVC { public method hashCode(): int public static method hashCode-impl(p0: double, p1: double): int public synthetic bridge method setP(p0: java.lang.Object): void - public method setP-nuuzChU(@org.jetbrains.annotations.NotNull p0: DPoint): void public static method setP-nuuzChU(p0: double, p1: double, p2: double, p3: double): void + public method setP-nuuzChU(p0: java.lang.Object): void public synthetic bridge method setP1(p0: java.lang.Object): void - public method setP1-nuuzChU(@org.jetbrains.annotations.NotNull p0: DPoint): void public static method setP1-nuuzChU(p0: double, p1: double, p2: double, p3: double): void + public method setP1-nuuzChU(p0: java.lang.Object): void public @org.jetbrains.annotations.NotNull method toString(): java.lang.String public static method toString-impl(p0: double, p1: double): java.lang.String public synthetic final method unbox-impl-field1(): double diff --git a/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.txt b/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.txt index 9c3c0524c2f..ff3aec8d85d 100644 --- a/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.txt +++ b/compiler/testData/codegen/box/valueClasses/overrides_typeParameters.txt @@ -64,10 +64,10 @@ public final class GenericFakeOverrideMFVC { public method hashCode(): int public static method hashCode-impl(p0: java.lang.Object, p1: java.lang.Object): int public synthetic bridge method setP(p0: java.lang.Object): void - public method setP-E-wHi0Q(@org.jetbrains.annotations.NotNull p0: XPoint): void + public method setP-E-wHi0Q(p0: java.lang.Object): void public static method setP-E-wHi0Q(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object): void public synthetic bridge method setP1(p0: java.lang.Object): void - public method setP1-E-wHi0Q(@org.jetbrains.annotations.NotNull p0: XPoint): void + public method setP1-E-wHi0Q(p0: java.lang.Object): void public static method setP1-E-wHi0Q(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object): void public @org.jetbrains.annotations.NotNull method toString(): java.lang.String public static method toString-impl(p0: java.lang.Object, p1: java.lang.Object): java.lang.String diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index 51d79aae138..ac9d29c638d 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -49328,18 +49328,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testAllFilesPresentInValueClasses() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); } - - @Test - @TestMetadata("overrideFunctionWithDefaultParameter.kt") - public void testOverrideFunctionWithDefaultParameter() throws Exception { - runTest("compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameter.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); - } - - @Test - @TestMetadata("overrideFunctionWithDefaultParameterGeneric.kt") - public void testOverrideFunctionWithDefaultParameterGeneric() throws Exception { - runTest("compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameterGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); - } } @Nested 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 9ca8a1719ac..d0fd1017297 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 @@ -52095,6 +52095,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/valueClasses/mutableSharedMfvcVar.kt"); } + @Test + @TestMetadata("nothingAsParameterType.kt") + public void testNothingAsParameterType() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/nothingAsParameterType.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("overrideFunctionWithDefaultParameter.kt") public void testOverrideFunctionWithDefaultParameter() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java index ff323fe1dce..012c1fc689d 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java @@ -52095,6 +52095,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack runTest("compiler/testData/codegen/box/valueClasses/mutableSharedMfvcVar.kt"); } + @Test + @TestMetadata("nothingAsParameterType.kt") + public void testNothingAsParameterType() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/nothingAsParameterType.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("overrideFunctionWithDefaultParameter.kt") public void testOverrideFunctionWithDefaultParameter() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 42ec02ad685..7d298fe53ca 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -40165,23 +40165,9 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } - private void runTest(String testDataFilePath, java.util.function.Function transformer) throws Exception { - KotlinTestUtils.runTest(path -> doTestWithTransformer(path, transformer), TargetBackend.JVM, testDataFilePath); - } - public void testAllFilesPresentInValueClasses() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); } - - @TestMetadata("overrideFunctionWithDefaultParameter.kt") - public void testOverrideFunctionWithDefaultParameter() throws Exception { - runTest("compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameter.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); - } - - @TestMetadata("overrideFunctionWithDefaultParameterGeneric.kt") - public void testOverrideFunctionWithDefaultParameterGeneric() throws Exception { - runTest("compiler/testData/codegen/box/valueClasses/overrideFunctionWithDefaultParameterGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); - } } @TestMetadata("compiler/testData/codegen/box/vararg")