Pass actual invoke parameters for inline functions with inline classes

#KT-25511 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2018-07-17 15:23:17 +03:00
parent 77959247d2
commit bb575866cb
8 changed files with 181 additions and 13 deletions
@@ -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)
}
@@ -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"
}
@@ -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"
}
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");