Skip to content

Commit

Permalink
IC & Coroutines: Unbox inline classes of suspend lambdas
Browse files Browse the repository at this point in the history
inside 'invoke' if 'create' does not override 'create' from
BaseContinuationImpl. In other words, when suspend lambda accepts more
than one parameter (including receiver).

Do that only if we do not generate bridge 'invoke' method, since
inline classes are unboxed in the bridge.

Use mangled name for 'create' function in this case inside 'invoke'.

 #KT-43249 In progress
 #KT-39847 Fixed
 #KT-38937 Fixed
  • Loading branch information
ilmirus committed Nov 27, 2020
1 parent a173e0e commit a48f76d
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ class CoroutineCodegenForLambda private constructor(
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, funDescriptor,
object : FunctionGenerationStrategy.CodegenBased(state) {
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
codegen.v.generateInvokeMethod(signature)
codegen.v.generateInvokeMethod(signature, funDescriptor)
}
})
}
Expand All @@ -316,13 +316,13 @@ class CoroutineCodegenForLambda private constructor(
)
mv.visitCode()
with(InstructionAdapter(mv)) {
generateInvokeMethod(jvmMethodSignature)
generateInvokeMethod(jvmMethodSignature, untypedDescriptor)
}

FunctionCodegen.endVisit(mv, "invoke", element)
}

private fun InstructionAdapter.generateInvokeMethod(signature: JvmMethodSignature) {
private fun InstructionAdapter.generateInvokeMethod(signature: JvmMethodSignature, descriptor: FunctionDescriptor) {
// this
load(0, AsmTypes.OBJECT_TYPE)
val parameterTypes = signature.valueParameters.map { it.asmType }
Expand All @@ -348,16 +348,23 @@ class CoroutineCodegenForLambda private constructor(
load(arraySlot, AsmTypes.OBJECT_TYPE)
} else {
var index = 0
val fromKotlinTypes =
if (!generateErasedCreate && doNotGenerateInvokeBridge) funDescriptor.allValueParameterTypes()
else funDescriptor.allValueParameterTypes().map { funDescriptor.module.builtIns.nullableAnyType }
val toKotlinTypes =
if (!generateErasedCreate && doNotGenerateInvokeBridge) createCoroutineDescriptor.allValueParameterTypes()
else descriptor.allValueParameterTypes()
parameterTypes.withVariableIndices().forEach { (varIndex, type) ->
load(varIndex + 1, type)
StackValue.coerce(type, createArgumentTypes[index++], this)
StackValue.coerce(type, fromKotlinTypes[index], createArgumentTypes[index], toKotlinTypes[index], this)
index++
}
}

// this.create(..)
invokevirtual(
v.thisName,
createCoroutineDescriptor.name.identifier,
typeMapper.mapFunctionName(createCoroutineDescriptor, null),
Type.getMethodDescriptor(
languageVersionSettings.continuationAsmType(),
*createArgumentTypes.toTypedArray()
Expand Down Expand Up @@ -831,3 +838,6 @@ private object FailingFunctionGenerationStrategy : FunctionGenerationStrategy()
fun reportSuspensionPointInsideMonitor(element: KtElement, state: GenerationState, stackTraceElement: String) {
state.diagnostics.report(ErrorsJvm.SUSPENSION_POINT_INSIDE_MONITOR.on(element, stackTraceElement))
}

private fun FunctionDescriptor.allValueParameterTypes(): List<KotlinType> =
(listOfNotNull(extensionReceiverParameter?.type)) + valueParameters.map { it.type }

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// WITH_RUNTIME

import kotlin.coroutines.*

fun builder(c: suspend () -> Unit) {
c.startCoroutine(Continuation(EmptyCoroutineContext) {
it.getOrThrow()
})
}

inline class IC(val s: String)

fun box(): String {
var res = "FAIL"
val lambda: suspend (IC, IC) -> String = { a, b ->
a.s + b.s
}
builder {
res = lambda(IC("O"), IC("K"))
}
return res
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// WITH_RUNTIME

import kotlin.coroutines.*

fun builder(c: suspend () -> Unit) {
c.startCoroutine(Continuation(EmptyCoroutineContext) {
it.getOrThrow()
})
}

inline class IC(val s: String)

var c: Continuation<Any>? = null

var res = "FAIL"

fun box(): String {
val lambda: suspend (IC, IC) -> String = { _, _ ->
suspendCoroutine<String> {
@Suppress("UNCHECKED_CAST")
c = it as Continuation<Any>
}
}
builder {
res = lambda(IC("_"), IC("_"))
}
c?.resume("OK")
return res
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// WITH_RUNTIME
// WITH_COROUTINES

import kotlin.coroutines.*
import helpers.*

var result = "FAIL"

fun builder(c: suspend () -> Unit) {
c.startCoroutine(handleExceptionContinuation {
result = it.message!!
})
}

inline class IC(val s: String)

var c: Continuation<Any>? = null

fun box(): String {
val lambda: suspend (IC, IC) -> String = { _, _ ->
suspendCoroutine<String> {
@Suppress("UNCHECKED_CAST")
c = it as Continuation<Any>
}
}
builder {
lambda(IC("O"), IC("K"))
}
c?.resumeWithException(IllegalStateException("OK"))
return result
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit a48f76d

Please sign in to comment.