diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt index b9baa18db49..1b953aed148 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -20,10 +20,13 @@ import org.jetbrains.kotlin.codegen.optimization.common.asSequence import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful import org.jetbrains.kotlin.codegen.optimization.fixStack.peek import org.jetbrains.kotlin.codegen.optimization.fixStack.top +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ParameterDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.resolve.isInlineClassType import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -224,11 +227,13 @@ class MethodInliner( // } // we should create additional parameter for continuation. var coroutineDesc = desc + val actualInvokeDescriptor: FunctionDescriptor if (info.invokeMethodDescriptor.isSuspend) { val coroutineInvokeMethodDescriptor = getOrCreateJvmSuspendFunctionView( info.invokeMethodDescriptor, inliningContext.state ) + actualInvokeDescriptor = coroutineInvokeMethodDescriptor val coroutineInvokeDesc = typeMapper.mapAsmMethod(coroutineInvokeMethodDescriptor).descriptor // And here we expect invoke(...Ljava/lang/Object;) be replaced with invoke(...Lkotlin/coroutines/Continuation;) // if this does not happen, insert fake continuation, since we could not have one yet. @@ -237,13 +242,19 @@ class MethodInliner( addFakeContinuationMarker(this) coroutineDesc = Type.getMethodDescriptor(Type.getReturnType(desc), *argumentTypes, AsmTypes.OBJECT_TYPE) } + } else { + actualInvokeDescriptor = info.invokeMethodDescriptor } + val valueParameters = - listOfNotNull(info.invokeMethodDescriptor.extensionReceiverParameter) + info.invokeMethodDescriptor.valueParameters + listOfNotNull(actualInvokeDescriptor.extensionReceiverParameter) + actualInvokeDescriptor.valueParameters + + val erasedInvokeFunction = ClosureCodegen.getErasedInvokeFunction(actualInvokeDescriptor) + val invokeParameters = erasedInvokeFunction.valueParameters val valueParamShift = Math.max(nextLocalIndex, markerShift)//NB: don't inline cause it changes putStackValuesIntoLocalsForLambdaOnInvoke( - listOf(*info.invokeMethod.argumentTypes), valueParameters, valueParamShift, this, coroutineDesc + listOf(*info.invokeMethod.argumentTypes), valueParameters, invokeParameters, valueParamShift, this, coroutineDesc ) if (invokeCall.lambdaInfo.invokeMethodDescriptor.valueParameters.isEmpty()) { @@ -285,8 +296,6 @@ class MethodInliner( result.mergeWithNotChangeInfo(lambdaResult) result.reifiedTypeParametersUsages.mergeAll(lambdaResult.reifiedTypeParametersUsages) - //return value boxing/unboxing - val erasedInvokeFunction = ClosureCodegen.getErasedInvokeFunction(info.invokeMethodDescriptor) val bridge = typeMapper.mapAsmMethod(erasedInvokeFunction) StackValue .onStack(info.invokeMethod.returnType, info.invokeMethodDescriptor.returnType) @@ -1018,6 +1027,7 @@ class MethodInliner( private fun putStackValuesIntoLocalsForLambdaOnInvoke( directOrder: List, directOrderOfArguments: List, + directOrderOfInvokeParameters: List, shift: Int, iv: InstructionAdapter, descriptor: String @@ -1029,18 +1039,25 @@ class MethodInliner( var currentShift = shift + directOrder.sumBy { it.size } - val safeToUseArgumentKotlinType = directOrder.size == directOrderOfArguments.size + val safeToUseArgumentKotlinType = + directOrder.size == directOrderOfArguments.size && directOrderOfArguments.size == directOrderOfInvokeParameters.size + directOrder.asReversed().forEachIndexed { index, type -> currentShift -= type.size val typeOnStack = actualParams[index] - if (typeOnStack != type) { - val argumentType = if (safeToUseArgumentKotlinType) - directOrderOfArguments[index].type.takeIf { it.isInlineClassType() } - else - null - // if argument type had inline class type then after substitution it will also have the same type, - // but probably boxed, which is OK because in terms of Kotlin types this is the same type - StackValue.onStack(typeOnStack, argumentType).put(type, argumentType, iv) + + val argumentKotlinType: KotlinType? + val invokeParameterKotlinType: KotlinType? + if (safeToUseArgumentKotlinType) { + argumentKotlinType = directOrderOfArguments[index].type + invokeParameterKotlinType = directOrderOfInvokeParameters[index].type + } else { + argumentKotlinType = null + invokeParameterKotlinType = null + } + + if (typeOnStack != type || invokeParameterKotlinType != argumentKotlinType) { + StackValue.onStack(typeOnStack, invokeParameterKotlinType).put(type, argumentKotlinType, iv) } iv.store(currentShift, type) } diff --git a/compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt b/compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt new file mode 100644 index 00000000000..99dc7271cd8 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt @@ -0,0 +1,46 @@ +// IGNORE_BACKEND: JS_IR, JVM_IR +// WITH_COROUTINES +// COMMON_COROUTINES_TEST + +import helpers.* +import COROUTINES_PACKAGE.* + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +@Suppress("UNSUPPORTED_FEATURE") +inline class SuccessOrFailure(val a: Any?) { + fun getOrThrow(): T = a as T +} + +abstract class SuccessOrFailureReceiver { + abstract suspend fun receive(result: SuccessOrFailure) +} + +inline fun SuccessOrFailureReceiver(crossinline f: (SuccessOrFailure) -> Unit): SuccessOrFailureReceiver = + object : SuccessOrFailureReceiver() { + override suspend fun receive(result: SuccessOrFailure) { + f(result) + } + } + +fun test() { + var invoked = false + val receiver = SuccessOrFailureReceiver { result -> + val intResult = result.getOrThrow() + invoked = true + } + + builder { + receiver.receive(SuccessOrFailure("42")) + } + if (!invoked) { + throw RuntimeException("Fail") + } +} + +fun box(): String { + test() + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/inlineClasses/crossinlineWithInlineClassInParameter.kt b/compiler/testData/codegen/box/inlineClasses/crossinlineWithInlineClassInParameter.kt new file mode 100644 index 00000000000..80f2b97c0ce --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/crossinlineWithInlineClassInParameter.kt @@ -0,0 +1,35 @@ +// !LANGUAGE: +InlineClasses +// IGNORE_BACKEND: JVM_IR, JS_IR + +inline class SuccessOrFailure(val a: Any?) { + fun getOrThrow(): T = a as T +} + +abstract class SuccessOrFailureReceiver { + abstract fun receive(result: SuccessOrFailure) +} + +inline fun SuccessOrFailureReceiver(crossinline f: (SuccessOrFailure) -> Unit): SuccessOrFailureReceiver = + object : SuccessOrFailureReceiver() { + override fun receive(result: SuccessOrFailure) { + f(result) + } + } + +fun test() { + var invoked = false + val receiver = SuccessOrFailureReceiver { result -> + val intResult = result.getOrThrow() + invoked = true + } + + receiver.receive(SuccessOrFailure("42")) + if (!invoked) { + throw RuntimeException("Fail") + } +} + +fun box(): String { + test() + return "OK" +} \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index b745ddd307d..5f2737ab8fa 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -6594,6 +6594,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); } + @TestMetadata("bridgeGenerationCrossinline.kt") + public void testBridgeGenerationCrossinline_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("bridgeGenerationCrossinline.kt") + public void testBridgeGenerationCrossinline_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt", "kotlin.coroutines"); + } + @TestMetadata("bridgeGenerationNonInline.kt") public void testBridgeGenerationNonInline_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationNonInline.kt", "kotlin.coroutines.experimental"); @@ -11196,6 +11206,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt"); } + @TestMetadata("crossinlineWithInlineClassInParameter.kt") + public void testCrossinlineWithInlineClassInParameter() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/crossinlineWithInlineClassInParameter.kt"); + } + @TestMetadata("elvisWithInlineClassAndNullConstant.kt") public void testElvisWithInlineClassAndNullConstant() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/elvisWithInlineClassAndNullConstant.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index bebbf3b738c..ec492cde326 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -6594,6 +6594,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("bridgeGenerationCrossinline.kt") + public void testBridgeGenerationCrossinline_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("bridgeGenerationCrossinline.kt") + public void testBridgeGenerationCrossinline_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt", "kotlin.coroutines"); + } + @TestMetadata("bridgeGenerationNonInline.kt") public void testBridgeGenerationNonInline_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationNonInline.kt", "kotlin.coroutines.experimental"); @@ -11196,6 +11206,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt"); } + @TestMetadata("crossinlineWithInlineClassInParameter.kt") + public void testCrossinlineWithInlineClassInParameter() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/crossinlineWithInlineClassInParameter.kt"); + } + @TestMetadata("elvisWithInlineClassAndNullConstant.kt") public void testElvisWithInlineClassAndNullConstant() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/elvisWithInlineClassAndNullConstant.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 091064a8302..6e809bafab7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -6594,6 +6594,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("bridgeGenerationCrossinline.kt") + public void testBridgeGenerationCrossinline_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("bridgeGenerationCrossinline.kt") + public void testBridgeGenerationCrossinline_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt", "kotlin.coroutines"); + } + @TestMetadata("bridgeGenerationNonInline.kt") public void testBridgeGenerationNonInline_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationNonInline.kt", "kotlin.coroutines.experimental"); @@ -11196,6 +11206,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt"); } + @TestMetadata("crossinlineWithInlineClassInParameter.kt") + public void testCrossinlineWithInlineClassInParameter() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/crossinlineWithInlineClassInParameter.kt"); + } + @TestMetadata("elvisWithInlineClassAndNullConstant.kt") public void testElvisWithInlineClassAndNullConstant() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/elvisWithInlineClassAndNullConstant.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index 544bc8769a0..8296feaf2c7 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -5704,6 +5704,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS_IR, true); } + @TestMetadata("bridgeGenerationCrossinline.kt") + public void testBridgeGenerationCrossinline_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt", "kotlin.coroutines.experimental"); + } + @TestMetadata("bridgeGenerationNonInline.kt") public void testBridgeGenerationNonInline_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationNonInline.kt", "kotlin.coroutines.experimental"); @@ -9831,6 +9836,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt"); } + @TestMetadata("crossinlineWithInlineClassInParameter.kt") + public void testCrossinlineWithInlineClassInParameter() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/crossinlineWithInlineClassInParameter.kt"); + } + @TestMetadata("elvisWithInlineClassAndNullConstant.kt") public void testElvisWithInlineClassAndNullConstant() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/elvisWithInlineClassAndNullConstant.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 8f072f90232..01832702d2a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -6319,6 +6319,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("bridgeGenerationCrossinline.kt") + public void testBridgeGenerationCrossinline_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("bridgeGenerationCrossinline.kt") + public void testBridgeGenerationCrossinline_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationCrossinline.kt", "kotlin.coroutines"); + } + @TestMetadata("bridgeGenerationNonInline.kt") public void testBridgeGenerationNonInline_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationNonInline.kt", "kotlin.coroutines.experimental"); @@ -10831,6 +10841,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/inlineClasses/createInlineClassInArgumentPosition.kt"); } + @TestMetadata("crossinlineWithInlineClassInParameter.kt") + public void testCrossinlineWithInlineClassInParameter() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/crossinlineWithInlineClassInParameter.kt"); + } + @TestMetadata("elvisWithInlineClassAndNullConstant.kt") public void testElvisWithInlineClassAndNullConstant() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/elvisWithInlineClassAndNullConstant.kt");