Pass actual invoke parameters for inline functions with inline classes
#KT-25511 Fixed
This commit is contained in:
@@ -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<Type>,
|
||||
directOrderOfArguments: List<ParameterDescriptor>,
|
||||
directOrderOfInvokeParameters: List<ValueParameterDescriptor>,
|
||||
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)
|
||||
}
|
||||
|
||||
+46
@@ -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<T>(val a: Any?) {
|
||||
fun getOrThrow(): T = a as T
|
||||
}
|
||||
|
||||
abstract class SuccessOrFailureReceiver<T> {
|
||||
abstract suspend fun receive(result: SuccessOrFailure<T>)
|
||||
}
|
||||
|
||||
inline fun <T> SuccessOrFailureReceiver(crossinline f: (SuccessOrFailure<T>) -> Unit): SuccessOrFailureReceiver<T> =
|
||||
object : SuccessOrFailureReceiver<T>() {
|
||||
override suspend fun receive(result: SuccessOrFailure<T>) {
|
||||
f(result)
|
||||
}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
var invoked = false
|
||||
val receiver = SuccessOrFailureReceiver<String> { result ->
|
||||
val intResult = result.getOrThrow()
|
||||
invoked = true
|
||||
}
|
||||
|
||||
builder {
|
||||
receiver.receive(SuccessOrFailure("42"))
|
||||
}
|
||||
if (!invoked) {
|
||||
throw RuntimeException("Fail")
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
test()
|
||||
return "OK"
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// IGNORE_BACKEND: JVM_IR, JS_IR
|
||||
|
||||
inline class SuccessOrFailure<T>(val a: Any?) {
|
||||
fun getOrThrow(): T = a as T
|
||||
}
|
||||
|
||||
abstract class SuccessOrFailureReceiver<T> {
|
||||
abstract fun receive(result: SuccessOrFailure<T>)
|
||||
}
|
||||
|
||||
inline fun <T> SuccessOrFailureReceiver(crossinline f: (SuccessOrFailure<T>) -> Unit): SuccessOrFailureReceiver<T> =
|
||||
object : SuccessOrFailureReceiver<T>() {
|
||||
override fun receive(result: SuccessOrFailure<T>) {
|
||||
f(result)
|
||||
}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
var invoked = false
|
||||
val receiver = SuccessOrFailureReceiver<String> { result ->
|
||||
val intResult = result.getOrThrow()
|
||||
invoked = true
|
||||
}
|
||||
|
||||
receiver.receive(SuccessOrFailure("42"))
|
||||
if (!invoked) {
|
||||
throw RuntimeException("Fail")
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
test()
|
||||
return "OK"
|
||||
}
|
||||
Generated
+15
@@ -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");
|
||||
|
||||
+15
@@ -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");
|
||||
|
||||
+15
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
+15
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user