Fix incorrect coroutines codegen behavior

If all the suspension calls in a suspend function were "hidden"
under the for-convention (iterator/next/hasNext) calls,
control-flow didn't find them, thus supposing that there is no
suspension points and there is no need to generate a coroutine state machine

The solution is to add relevant calls to CFG

 #KT-15824 Fixed
This commit is contained in:
Denis Zharkov
2017-01-20 15:25:21 +03:00
parent 8cbea903f4
commit 02b40326cc
22 changed files with 609 additions and 291 deletions
@@ -931,9 +931,7 @@ class ControlFlowInformationProvider private constructor(
private inline fun traverseCalls(crossinline onCall: (instruction: CallInstruction, resolvedCall: ResolvedCall<*>) -> Unit) {
pseudocode.traverse(TraversalOrder.FORWARD) { instruction ->
if (instruction !is CallInstruction) return@traverse
val resolvedCall = instruction.element.getResolvedCall(trace.bindingContext) ?: return@traverse
onCall(instruction, resolvedCall)
onCall(instruction, instruction.resolvedCall)
}
}
@@ -56,6 +56,7 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat
import org.jetbrains.kotlin.resolve.scopes.receivers.*
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
import java.util.*
class ControlFlowProcessor(private val trace: BindingTrace) {
@@ -750,15 +751,18 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
override fun visitForExpression(expression: KtForExpression) {
builder.enterBlockScope(expression)
generateInstructions(expression.loopRange)
val loopRange = expression.loopRange
generateInstructions(loopRange)
generateLoopConventionCall(loopRange, BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL)
declareLoopParameter(expression)
// TODO : primitive cases
val loopInfo = builder.enterLoop(expression)
builder.bindLabel(loopInfo.conditionEntryPoint)
generateLoopConventionCall(loopRange, BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL)
builder.nondeterministicJump(loopInfo.exitPoint, expression, null)
generateLoopConventionCall(loopRange, BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL)
writeLoopParameterAssignment(expression)
@@ -773,6 +777,15 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
builder.exitBlockScope(expression)
}
private fun generateLoopConventionCall(
loopRange: KtExpression?,
callSlice: ReadOnlySlice<KtExpression, ResolvedCall<FunctionDescriptor>>
) {
if (loopRange == null) return
val resolvedCall = trace.bindingContext[callSlice, loopRange] ?: return
generateCall(resolvedCall)
}
private fun declareLoopParameter(expression: KtForExpression) {
val loopParameter = expression.loopParameter
if (loopParameter != null) {