diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt index 282625b09ee..0351ef49c32 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt @@ -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) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index d264ea29a67..05f21b038e1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -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> + ) { + 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) { diff --git a/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.instructions b/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.instructions index 6872720c12c..f24b711eae8 100644 --- a/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.instructions +++ b/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.instructions @@ -20,35 +20,41 @@ L0: w(numbers|) INIT: in: {numbers=D} out: {numbers=ID} 2 mark({ for (i in numbers) { val b: Boolean if (1 < 2) { b = false } else { b = true } use(b) continue } }) INIT: in: {numbers=ID} out: {numbers=ID} USE: in: {numbers=READ} out: {numbers=READ} 3 r(numbers) -> USE: in: {} out: {numbers=READ} + mark(numbers) + call(numbers, iterator|) -> v(i) INIT: in: {numbers=ID} out: {i=D, numbers=ID} L2 [loop entry point]: L6 [condition entry point]: - jmp?(L3) INIT: in: {i=I?D, numbers=ID} out: {i=I?D, numbers=ID} - magic[LOOP_RANGE_ITERATION](numbers|) -> - w(i|) INIT: in: {i=I?D, numbers=ID} out: {i=ID, numbers=ID} + mark(numbers) INIT: in: {i=I?D, numbers=ID} out: {i=I?D, numbers=ID} + call(numbers, hasNext) -> + jmp?(L3) + mark(numbers) + call(numbers, next) -> + magic[LOOP_RANGE_ITERATION](numbers|) -> + w(i|) INIT: in: {i=I?D, numbers=ID} out: {i=ID, numbers=ID} mark(for (i in numbers) { val b: Boolean if (1 < 2) { b = false } else { b = true } use(b) continue }) INIT: in: {i=ID, numbers=ID} out: {i=ID, numbers=ID} USE: in: {} out: {} L4 [body entry point]: 4 mark({ val b: Boolean if (1 < 2) { b = false } else { b = true } use(b) continue }) v(val b: Boolean) INIT: in: {i=ID, numbers=ID} out: {b=D, i=ID, numbers=ID} mark(if (1 < 2) { b = false } else { b = true }) INIT: in: {b=D, i=ID, numbers=ID} out: {b=D, i=ID, numbers=ID} - r(1) -> - r(2) -> + r(1) -> + r(2) -> mark(1 < 2) - call(1 < 2, compareTo|, ) -> - jf(L7|) + call(1 < 2, compareTo|, ) -> + jf(L7|) 5 mark({ b = false }) - r(false) -> USE: in: {b=WRITTEN_AFTER_READ} out: {b=WRITTEN_AFTER_READ} - w(b|) INIT: in: {b=D, i=ID, numbers=ID} out: {b=ID, i=ID, numbers=ID} USE: in: {b=READ} out: {b=WRITTEN_AFTER_READ} + r(false) -> USE: in: {b=WRITTEN_AFTER_READ} out: {b=WRITTEN_AFTER_READ} + w(b|) INIT: in: {b=D, i=ID, numbers=ID} out: {b=ID, i=ID, numbers=ID} USE: in: {b=READ} out: {b=WRITTEN_AFTER_READ} 4 jmp(L8) INIT: in: {b=ID, i=ID, numbers=ID} out: {b=ID, i=ID, numbers=ID} USE: in: {b=READ} out: {b=READ} L7 [else branch]: 5 mark({ b = true }) INIT: in: {b=D, i=ID, numbers=ID} out: {b=D, i=ID, numbers=ID} - r(true) -> USE: in: {b=WRITTEN_AFTER_READ} out: {b=WRITTEN_AFTER_READ} - w(b|) INIT: in: {b=D, i=ID, numbers=ID} out: {b=ID, i=ID, numbers=ID} USE: in: {b=READ} out: {b=WRITTEN_AFTER_READ} + r(true) -> USE: in: {b=WRITTEN_AFTER_READ} out: {b=WRITTEN_AFTER_READ} + w(b|) INIT: in: {b=D, i=ID, numbers=ID} out: {b=ID, i=ID, numbers=ID} USE: in: {b=READ} out: {b=WRITTEN_AFTER_READ} L8 ['if' expression result]: - 4 merge(if (1 < 2) { b = false } else { b = true }|!, !) -> INIT: in: {b=ID, i=ID, numbers=ID} out: {b=ID, i=ID, numbers=ID} USE: in: {b=READ} out: {b=READ} - r(b) -> USE: in: {} out: {b=READ} + 4 merge(if (1 < 2) { b = false } else { b = true }|!, !) -> INIT: in: {b=ID, i=ID, numbers=ID} out: {b=ID, i=ID, numbers=ID} USE: in: {b=READ} out: {b=READ} + r(b) -> USE: in: {} out: {b=READ} mark(use(b)) - call(use(b), use|) -> + call(use(b), use|) -> jmp(L6) USE: in: {} out: {} - 3 jmp(L2) L3 [loop exit point]: @@ -77,4 +83,4 @@ error: INIT: in: {} out: {} sink: INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {} -===================== \ No newline at end of file +===================== diff --git a/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.values b/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.values index 05d6e38dbed..edaee80bded 100644 --- a/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.values +++ b/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.values @@ -13,25 +13,25 @@ fun foo(numbers: Collection) { } } --------------------- - : {<: Collection} NEW: magic[FAKE_INITIALIZER](numbers: Collection) -> - : Int NEW: magic[LOOP_RANGE_ITERATION](numbers|) -> -numbers : {<: Iterable} NEW: r(numbers) -> -1 : {<: Comparable} NEW: r(1) -> -2 : Int NEW: r(2) -> -1 < 2 : Boolean NEW: call(1 < 2, compareTo|, ) -> -false : Boolean NEW: r(false) -> -b = false !: * -{ b = false } !: * COPY -true : Boolean NEW: r(true) -> -b = true !: * -{ b = true } !: * COPY -if (1 < 2) { b = false } else { b = true } : * NEW: merge(if (1 < 2) { b = false } else { b = true }|!, !) -> -b : * NEW: r(b) -> -use(b) : * NEW: call(use(b), use|) -> -continue !: * -{ val b: Boolean if (1 < 2) { b = false } else { b = true } use(b) continue } !: * COPY -for (i in numbers) { val b: Boolean if (1 < 2) { b = false } else { b = true } use(b) continue } !: * -{ for (i in numbers) { val b: Boolean if (1 < 2) { b = false } else { b = true } use(b) continue } } !: * COPY + : {<: Collection} NEW: magic[FAKE_INITIALIZER](numbers: Collection) -> + : Int NEW: magic[LOOP_RANGE_ITERATION](numbers|) -> +numbers : {<: Iterable} NEW: call(numbers, next) -> +1 : {<: Comparable} NEW: r(1) -> +2 : Int NEW: r(2) -> +1 < 2 : Boolean NEW: call(1 < 2, compareTo|, ) -> +false : Boolean NEW: r(false) -> +b = false !: * +{ b = false } !: * COPY +true : Boolean NEW: r(true) -> +b = true !: * +{ b = true } !: * COPY +if (1 < 2) { b = false } else { b = true } : * NEW: merge(if (1 < 2) { b = false } else { b = true }|!, !) -> +b : * NEW: r(b) -> +use(b) : * NEW: call(use(b), use|) -> +continue !: * +{ val b: Boolean if (1 < 2) { b = false } else { b = true } use(b) continue } !: * COPY +for (i in numbers) { val b: Boolean if (1 < 2) { b = false } else { b = true } use(b) continue } !: * +{ for (i in numbers) { val b: Boolean if (1 < 2) { b = false } else { b = true } use(b) continue } } !: * COPY ===================== == use == fun use(vararg a: Any?) = a diff --git a/compiler/testData/cfg-variables/lexicalScopes/forScope.instructions b/compiler/testData/cfg-variables/lexicalScopes/forScope.instructions index 7d8f4e441b8..30130fc9fd6 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/forScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/forScope.instructions @@ -16,28 +16,34 @@ L0: r(10) -> mark(1..10) call(1..10, rangeTo|, ) -> + mark(1..10) + call(1..10, iterator|) -> v(i) INIT: in: {} out: {i=D} L2 [loop entry point]: L6 [condition entry point]: - jmp?(L3) INIT: in: {i=I?D} out: {i=I?D} - magic[LOOP_RANGE_ITERATION](1..10|) -> - w(i|) INIT: in: {i=I?D} out: {i=ID} + mark(1..10) INIT: in: {i=I?D} out: {i=I?D} + call(1..10, hasNext) -> + jmp?(L3) + mark(1..10) + call(1..10, next) -> + magic[LOOP_RANGE_ITERATION](1..10|) -> + w(i|) INIT: in: {i=I?D} out: {i=ID} mark(for (i in 1..10) { val a = i }) INIT: in: {i=ID} out: {i=ID} L4 [body entry point]: 4 mark({ val a = i }) v(val a = i) INIT: in: {i=ID} out: {a=D, i=ID} - r(i) -> INIT: in: {a=D, i=ID} out: {a=D, i=ID} - w(a|) INIT: in: {a=D, i=ID} out: {a=ID, i=ID} + r(i) -> INIT: in: {a=D, i=ID} out: {a=D, i=ID} + w(a|) INIT: in: {a=D, i=ID} out: {a=ID, i=ID} 3 jmp(L2) INIT: in: {i=ID} out: {i=ID} USE: in: {i=READ} out: {i=READ} L3 [loop exit point]: L5 [body exit point]: read (Unit) INIT: in: {i=I?D} out: {i=I?D} 2 mark("after") INIT: in: {} out: {} - r("after") -> + r("after") -> L1: 1 error: sink: USE: in: {} out: {} -===================== \ No newline at end of file +===================== diff --git a/compiler/testData/cfg-variables/lexicalScopes/forScope.values b/compiler/testData/cfg-variables/lexicalScopes/forScope.values index a291ce38e85..7c6e4ec492b 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/forScope.values +++ b/compiler/testData/cfg-variables/lexicalScopes/forScope.values @@ -7,12 +7,12 @@ fun foo() { "after" } --------------------- - : Int NEW: magic[LOOP_RANGE_ITERATION](1..10|) -> -"before" : * NEW: r("before") -> -1 : Int NEW: r(1) -> -10 : Int NEW: r(10) -> -1..10 : {<: Iterable} NEW: call(1..10, rangeTo|, ) -> -i : Int NEW: r(i) -> -"after" : * NEW: r("after") -> -{ "before" for (i in 1..10) { val a = i } "after" } : * COPY + : Int NEW: magic[LOOP_RANGE_ITERATION](1..10|) -> +"before" : * NEW: r("before") -> +1 : Int NEW: r(1) -> +10 : Int NEW: r(10) -> +1..10 : {<: Iterable} NEW: call(1..10, next) -> +i : Int NEW: r(i) -> +"after" : * NEW: r("after") -> +{ "before" for (i in 1..10) { val a = i } "after" } : * COPY ===================== diff --git a/compiler/testData/cfg/bugs/jumpToOuterScope.instructions b/compiler/testData/cfg/bugs/jumpToOuterScope.instructions index f9370cbc0cb..9f813cf0089 100644 --- a/compiler/testData/cfg/bugs/jumpToOuterScope.instructions +++ b/compiler/testData/cfg/bugs/jumpToOuterScope.instructions @@ -14,21 +14,27 @@ L0: w(c|) 2 mark({ for (e in c) { { break } } }) 3 r(c) -> + mark(c) + call(c, iterator|) -> v(e) L2 [loop entry point]: L6 [condition entry point]: - jmp?(L3) NEXT:[read (Unit), magic[LOOP_RANGE_ITERATION](c|) -> ] PREV:[v(e), jmp(L2)] - magic[LOOP_RANGE_ITERATION](c|) -> - w(e|) + mark(c) PREV:[v(e), jmp(L2)] + call(c, hasNext) -> + jmp?(L3) NEXT:[read (Unit), mark(c)] + mark(c) + call(c, next) -> + magic[LOOP_RANGE_ITERATION](c|) -> + w(e|) mark(for (e in c) { { break } }) L4 [body entry point]: 4 mark({ { break } }) mark({ break }) - jmp?(L7) NEXT:[r({ break }) -> , d({ break })] + jmp?(L7) NEXT:[r({ break }) -> , d({ break })] d({ break }) NEXT:[] L7 [after local declaration]: - r({ break }) -> PREV:[jmp?(L7)] - 3 jmp(L2) NEXT:[jmp?(L3)] + r({ break }) -> PREV:[jmp?(L7)] + 3 jmp(L2) NEXT:[mark(c)] L3 [loop exit point]: L5 [body exit point]: read (Unit) PREV:[jmp?(L3)] diff --git a/compiler/testData/cfg/bugs/jumpToOuterScope.values b/compiler/testData/cfg/bugs/jumpToOuterScope.values index 85d73202db1..96c17a69a96 100644 --- a/compiler/testData/cfg/bugs/jumpToOuterScope.values +++ b/compiler/testData/cfg/bugs/jumpToOuterScope.values @@ -8,18 +8,18 @@ fun foo(c: Collection) { } --------------------- : {<: Collection} NEW: magic[FAKE_INITIALIZER](c: Collection) -> - : Int NEW: magic[LOOP_RANGE_ITERATION](c|) -> -c : {<: Iterable} NEW: r(c) -> -{ break } : * NEW: r({ break }) -> -{ { break } } : * COPY -for (e in c) { { break } } !: * -{ for (e in c) { { break } } } !: * COPY + : Int NEW: magic[LOOP_RANGE_ITERATION](c|) -> +c : {<: Iterable} NEW: call(c, next) -> +{ break } : * NEW: r({ break }) -> +{ { break } } : * COPY +for (e in c) { { break } } !: * +{ for (e in c) { { break } } } !: * COPY ===================== == anonymous_0 == { break } --------------------- -break !: * +break !: * break !: * COPY ===================== diff --git a/compiler/testData/cfg/controlStructures/Finally.instructions b/compiler/testData/cfg/controlStructures/Finally.instructions index f6c750be2df..69568501224 100644 --- a/compiler/testData/cfg/controlStructures/Finally.instructions +++ b/compiler/testData/cfg/controlStructures/Finally.instructions @@ -476,48 +476,54 @@ L0: r(a) -> mark(1..a) call(1..a, rangeTo|, ) -> + mark(1..a) + call(1..a, iterator|) -> v(i) L2 [loop entry point]: L6 [condition entry point]: - jmp?(L3) NEXT:[read (Unit), magic[LOOP_RANGE_ITERATION](1..a|) -> ] PREV:[v(i), jmp(L6), jmp(L2)] - magic[LOOP_RANGE_ITERATION](1..a|) -> - w(i|) + mark(1..a) PREV:[v(i), jmp(L6), jmp(L2)] + call(1..a, hasNext) -> + jmp?(L3) NEXT:[read (Unit), mark(1..a)] + mark(1..a) + call(1..a, next) -> + magic[LOOP_RANGE_ITERATION](1..a|) -> + w(i|) mark(for (i in 1..a) { try { 1 if (2 > 3) { continue@l } } finally { 2 } }) L4 [body entry point]: 4 mark({ try { 1 if (2 > 3) { continue@l } } finally { 2 } }) mark(try { 1 if (2 > 3) { continue@l } } finally { 2 }) jmp?(L7) NEXT:[mark({ 2 }), mark({ 1 if (2 > 3) { continue@l } })] 5 mark({ 1 if (2 > 3) { continue@l } }) - r(1) -> + r(1) -> mark(if (2 > 3) { continue@l }) - r(2) -> - r(3) -> + r(2) -> + r(3) -> mark(2 > 3) - call(2 > 3, compareTo|, ) -> - jf(L8|) NEXT:[read (Unit), mark({ continue@l })] + call(2 > 3, compareTo|, ) -> + jf(L8|) NEXT:[read (Unit), mark({ continue@l })] 6 mark({ continue@l }) jmp?(L7) NEXT:[mark({ 2 }), mark({ 2 })] L9 [start finally]: 7 mark({ 2 }) - r(2) -> + r(2) -> L10 [finish finally]: - 6 jmp(L6) NEXT:[jmp?(L3)] -- 5 jmp(L11) NEXT:[merge(if (2 > 3) { continue@l }|!) -> ] PREV:[] + 6 jmp(L6) NEXT:[mark(1..a)] +- 5 jmp(L11) NEXT:[merge(if (2 > 3) { continue@l }|!) -> ] PREV:[] L8 [else branch]: - read (Unit) PREV:[jf(L8|)] + read (Unit) PREV:[jf(L8|)] L11 ['if' expression result]: - merge(if (2 > 3) { continue@l }|!) -> + merge(if (2 > 3) { continue@l }|!) -> 4 jmp?(L7) NEXT:[mark({ 2 }), jmp(L12)] jmp(L12) NEXT:[mark({ 2 })] L7 [onExceptionToFinallyBlock]: 7 mark({ 2 }) PREV:[jmp?(L7), jmp?(L7), jmp?(L7)] - r(2) -> + r(2) -> 4 jmp(error) NEXT:[] L12 [skipFinallyToErrorBlock]: 7 mark({ 2 }) PREV:[jmp(L12)] - r(2) -> - 4 merge(try { 1 if (2 > 3) { continue@l } } finally { 2 }|) -> - 3 jmp(L2) NEXT:[jmp?(L3)] + r(2) -> + 4 merge(try { 1 if (2 > 3) { continue@l } } finally { 2 }|) -> + 3 jmp(L2) NEXT:[mark(1..a)] L3 [loop exit point]: L5 [body exit point]: read (Unit) PREV:[jmp?(L3)] @@ -557,47 +563,53 @@ L0: r(a) -> mark(1..a) call(1..a, rangeTo|, ) -> + mark(1..a) + call(1..a, iterator|) -> v(i) L3 [loop entry point]: L7 [condition entry point]: - jmp?(L4) NEXT:[read (Unit), magic[LOOP_RANGE_ITERATION](1..a|) -> ] PREV:[v(i), jmp(L7), jmp(L3)] - magic[LOOP_RANGE_ITERATION](1..a|) -> - w(i|) + mark(1..a) PREV:[v(i), jmp(L7), jmp(L3)] + call(1..a, hasNext) -> + jmp?(L4) NEXT:[read (Unit), mark(1..a)] + mark(1..a) + call(1..a, next) -> + magic[LOOP_RANGE_ITERATION](1..a|) -> + w(i|) mark(for (i in 1..a) { 1 if (2 > 3) { continue@l } }) L5 [body entry point]: 5 mark({ 1 if (2 > 3) { continue@l } }) - r(1) -> + r(1) -> mark(if (2 > 3) { continue@l }) - r(2) -> - r(3) -> + r(2) -> + r(3) -> mark(2 > 3) - call(2 > 3, compareTo|, ) -> - jf(L8|) NEXT:[read (Unit), mark({ continue@l })] + call(2 > 3, compareTo|, ) -> + jf(L8|) NEXT:[read (Unit), mark({ continue@l })] 6 mark({ continue@l }) - jmp(L7) NEXT:[jmp?(L4)] -- 5 jmp(L9) NEXT:[merge(if (2 > 3) { continue@l }|!) -> ] PREV:[] + jmp(L7) NEXT:[mark(1..a)] +- 5 jmp(L9) NEXT:[merge(if (2 > 3) { continue@l }|!) -> ] PREV:[] L8 [else branch]: - read (Unit) PREV:[jf(L8|)] + read (Unit) PREV:[jf(L8|)] L9 ['if' expression result]: - merge(if (2 > 3) { continue@l }|!) -> - 4 jmp(L3) NEXT:[jmp?(L4)] + merge(if (2 > 3) { continue@l }|!) -> + 4 jmp(L3) NEXT:[mark(1..a)] L4 [loop exit point]: L6 [body exit point]: read (Unit) PREV:[jmp?(L4)] - 3 r(5) -> + 3 r(5) -> 2 jmp?(L2) NEXT:[mark({ 2 }), jmp(L10)] jmp(L10) NEXT:[mark({ 2 })] L2 [onExceptionToFinallyBlock]: L11 [start finally]: 3 mark({ 2 }) PREV:[jmp?(L2), jmp?(L2)] - r(2) -> + r(2) -> L12 [finish finally]: 2 jmp(error) NEXT:[] L10 [skipFinallyToErrorBlock]: L13 [copy of L2, onExceptionToFinallyBlock]: 3 mark({ 2 }) PREV:[jmp(L10)] - r(2) -> - 2 merge(try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } 5 } finally { 2 }|) -> + r(2) -> + 2 merge(try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } 5 } finally { 2 }|) -> L1: 1 NEXT:[] error: @@ -633,30 +645,36 @@ L0: r(a) -> mark(1..a) call(1..a, rangeTo|, ) -> + mark(1..a) + call(1..a, iterator|) -> v(i) L3 [loop entry point]: L7 [condition entry point]: - jmp?(L4) NEXT:[read (Unit), magic[LOOP_RANGE_ITERATION](1..a|) -> ] PREV:[v(i), jmp(L7), jmp(L3)] - magic[LOOP_RANGE_ITERATION](1..a|) -> - w(i|) + mark(1..a) PREV:[v(i), jmp(L7), jmp(L3)] + call(1..a, hasNext) -> + jmp?(L4) NEXT:[read (Unit), mark(1..a)] + mark(1..a) + call(1..a, next) -> + magic[LOOP_RANGE_ITERATION](1..a|) -> + w(i|) mark(for (i in 1..a) { 1 if (2 > 3) { continue@l } }) L5 [body entry point]: 5 mark({ 1 if (2 > 3) { continue@l } }) - r(1) -> + r(1) -> mark(if (2 > 3) { continue@l }) - r(2) -> - r(3) -> + r(2) -> + r(3) -> mark(2 > 3) - call(2 > 3, compareTo|, ) -> - jf(L8|) NEXT:[read (Unit), mark({ continue@l })] + call(2 > 3, compareTo|, ) -> + jf(L8|) NEXT:[read (Unit), mark({ continue@l })] 6 mark({ continue@l }) - jmp(L7) NEXT:[jmp?(L4)] -- 5 jmp(L9) NEXT:[merge(if (2 > 3) { continue@l }|!) -> ] PREV:[] + jmp(L7) NEXT:[mark(1..a)] +- 5 jmp(L9) NEXT:[merge(if (2 > 3) { continue@l }|!) -> ] PREV:[] L8 [else branch]: - read (Unit) PREV:[jf(L8|)] + read (Unit) PREV:[jf(L8|)] L9 ['if' expression result]: - merge(if (2 > 3) { continue@l }|!) -> - 4 jmp(L3) NEXT:[jmp?(L4)] + merge(if (2 > 3) { continue@l }|!) -> + 4 jmp(L3) NEXT:[mark(1..a)] L4 [loop exit point]: L6 [body exit point]: read (Unit) PREV:[jmp?(L4)] @@ -665,14 +683,14 @@ L6 [body exit point]: L2 [onExceptionToFinallyBlock]: L11 [start finally]: 3 mark({ 2 }) PREV:[jmp?(L2), jmp?(L2)] - r(2) -> + r(2) -> L12 [finish finally]: 2 jmp(error) NEXT:[] L10 [skipFinallyToErrorBlock]: L13 [copy of L2, onExceptionToFinallyBlock]: 3 mark({ 2 }) PREV:[jmp(L10)] - r(2) -> - 2 merge(try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } } finally { 2 }|!) -> + r(2) -> + 2 merge(try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } } finally { 2 }|!) -> L1: 1 NEXT:[] error: diff --git a/compiler/testData/cfg/controlStructures/Finally.values b/compiler/testData/cfg/controlStructures/Finally.values index 15c39869f82..4cca344fa9d 100644 --- a/compiler/testData/cfg/controlStructures/Finally.values +++ b/compiler/testData/cfg/controlStructures/Finally.values @@ -7,12 +7,12 @@ fun t1() { } } --------------------- -1 : * NEW: r(1) -> -{ 1 } : * COPY -2 : * NEW: r(2) -> -{ 2 } : * COPY +1 : * NEW: r(1) -> +{ 1 } : * COPY +2 : * NEW: r(2) -> +{ 2 } : * COPY try { 1 } finally { 2 } : * NEW: merge(try { 1 } finally { 2 }|) -> -{ try { 1 } finally { 2 } } : * COPY +{ try { 1 } finally { 2 } } : * COPY ===================== == t2 == fun t2() { @@ -26,18 +26,18 @@ fun t2() { } } --------------------- -1 : * NEW: r(1) -> -2 : {<: Comparable} NEW: r(2) -> -3 : Int NEW: r(3) -> -2 > 3 : Boolean NEW: call(2 > 3, compareTo|, ) -> -return !: * -{ return } !: * COPY -if (2 > 3) { return } : * NEW: merge(if (2 > 3) { return }|!) -> -{ 1 if (2 > 3) { return } } : * COPY -2 : * NEW: r(2) -> -{ 2 } : * COPY +1 : * NEW: r(1) -> +2 : {<: Comparable} NEW: r(2) -> +3 : Int NEW: r(3) -> +2 > 3 : Boolean NEW: call(2 > 3, compareTo|, ) -> +return !: * +{ return } !: * COPY +if (2 > 3) { return } : * NEW: merge(if (2 > 3) { return }|!) -> +{ 1 if (2 > 3) { return } } : * COPY +2 : * NEW: r(2) -> +{ 2 } : * COPY try { 1 if (2 > 3) { return } } finally { 2 } : * NEW: merge(try { 1 if (2 > 3) { return } } finally { 2 }|) -> -{ try { 1 if (2 > 3) { return } } finally { 2 } } : * COPY +{ try { 1 if (2 > 3) { return } } finally { 2 } } : * COPY ===================== == t3 == fun t3() { @@ -233,25 +233,25 @@ fun t8(a : Int) { } --------------------- : Int NEW: magic[FAKE_INITIALIZER](a : Int) -> - : Int NEW: magic[LOOP_RANGE_ITERATION](1..a|) -> + : Int NEW: magic[LOOP_RANGE_ITERATION](1..a|) -> 1 : Int NEW: r(1) -> a : Int NEW: r(a) -> -1..a : {<: Iterable} NEW: call(1..a, rangeTo|, ) -> -1 : * NEW: r(1) -> -2 : {<: Comparable} NEW: r(2) -> -3 : Int NEW: r(3) -> -2 > 3 : Boolean NEW: call(2 > 3, compareTo|, ) -> -continue@l !: * -{ continue@l } !: * COPY -if (2 > 3) { continue@l } : * NEW: merge(if (2 > 3) { continue@l }|!) -> -{ 1 if (2 > 3) { continue@l } } : * COPY -2 : * NEW: r(2) -> -{ 2 } : * COPY -try { 1 if (2 > 3) { continue@l } } finally { 2 } : * NEW: merge(try { 1 if (2 > 3) { continue@l } } finally { 2 }|) -> -{ try { 1 if (2 > 3) { continue@l } } finally { 2 } } : * COPY -for (i in 1..a) { try { 1 if (2 > 3) { continue@l } } finally { 2 } } !: * -l@ for (i in 1..a) { try { 1 if (2 > 3) { continue@l } } finally { 2 } } !: * COPY -{ l@ for (i in 1..a) { try { 1 if (2 > 3) { continue@l } } finally { 2 } } } !: * COPY +1..a : {<: Iterable} NEW: call(1..a, next) -> +1 : * NEW: r(1) -> +2 : {<: Comparable} NEW: r(2) -> +3 : Int NEW: r(3) -> +2 > 3 : Boolean NEW: call(2 > 3, compareTo|, ) -> +continue@l !: * +{ continue@l } !: * COPY +if (2 > 3) { continue@l } : * NEW: merge(if (2 > 3) { continue@l }|!) -> +{ 1 if (2 > 3) { continue@l } } : * COPY +2 : * NEW: r(2) -> +{ 2 } : * COPY +try { 1 if (2 > 3) { continue@l } } finally { 2 } : * NEW: merge(try { 1 if (2 > 3) { continue@l } } finally { 2 }|) -> +{ try { 1 if (2 > 3) { continue@l } } finally { 2 } } : * COPY +for (i in 1..a) { try { 1 if (2 > 3) { continue@l } } finally { 2 } } !: * +l@ for (i in 1..a) { try { 1 if (2 > 3) { continue@l } } finally { 2 } } !: * COPY +{ l@ for (i in 1..a) { try { 1 if (2 > 3) { continue@l } } finally { 2 } } } !: * COPY ===================== == t9 == fun t9(a : Int) { @@ -269,26 +269,26 @@ fun t9(a : Int) { } --------------------- : Int NEW: magic[FAKE_INITIALIZER](a : Int) -> - : Int NEW: magic[LOOP_RANGE_ITERATION](1..a|) -> + : Int NEW: magic[LOOP_RANGE_ITERATION](1..a|) -> 1 : Int NEW: r(1) -> a : Int NEW: r(a) -> -1..a : {<: Iterable} NEW: call(1..a, rangeTo|, ) -> -1 : * NEW: r(1) -> -2 : {<: Comparable} NEW: r(2) -> -3 : Int NEW: r(3) -> -2 > 3 : Boolean NEW: call(2 > 3, compareTo|, ) -> -continue@l !: * -{ continue@l } !: * COPY -if (2 > 3) { continue@l } : * NEW: merge(if (2 > 3) { continue@l }|!) -> -{ 1 if (2 > 3) { continue@l } } : * COPY -for (i in 1..a) { 1 if (2 > 3) { continue@l } } !: * -l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } !: * COPY -5 : * NEW: r(5) -> -{ l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } 5 } : * COPY -2 : * NEW: r(2) -> -{ 2 } : * COPY -try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } 5 } finally { 2 } : * NEW: merge(try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } 5 } finally { 2 }|) -> -{ try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } 5 } finally { 2 } } : * COPY +1..a : {<: Iterable} NEW: call(1..a, next) -> +1 : * NEW: r(1) -> +2 : {<: Comparable} NEW: r(2) -> +3 : Int NEW: r(3) -> +2 > 3 : Boolean NEW: call(2 > 3, compareTo|, ) -> +continue@l !: * +{ continue@l } !: * COPY +if (2 > 3) { continue@l } : * NEW: merge(if (2 > 3) { continue@l }|!) -> +{ 1 if (2 > 3) { continue@l } } : * COPY +for (i in 1..a) { 1 if (2 > 3) { continue@l } } !: * +l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } !: * COPY +5 : * NEW: r(5) -> +{ l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } 5 } : * COPY +2 : * NEW: r(2) -> +{ 2 } : * COPY +try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } 5 } finally { 2 } : * NEW: merge(try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } 5 } finally { 2 }|) -> +{ try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } 5 } finally { 2 } } : * COPY ===================== == t10 == fun t10(a : Int) { @@ -305,25 +305,25 @@ fun t10(a : Int) { } --------------------- : Int NEW: magic[FAKE_INITIALIZER](a : Int) -> - : Int NEW: magic[LOOP_RANGE_ITERATION](1..a|) -> + : Int NEW: magic[LOOP_RANGE_ITERATION](1..a|) -> 1 : Int NEW: r(1) -> a : Int NEW: r(a) -> -1..a : {<: Iterable} NEW: call(1..a, rangeTo|, ) -> -1 : * NEW: r(1) -> -2 : {<: Comparable} NEW: r(2) -> -3 : Int NEW: r(3) -> -2 > 3 : Boolean NEW: call(2 > 3, compareTo|, ) -> -continue@l !: * -{ continue@l } !: * COPY -if (2 > 3) { continue@l } : * NEW: merge(if (2 > 3) { continue@l }|!) -> -{ 1 if (2 > 3) { continue@l } } : * COPY -for (i in 1..a) { 1 if (2 > 3) { continue@l } } !: * -l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } !: * COPY -{ l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } } !: * COPY -2 : * NEW: r(2) -> -{ 2 } : * COPY -try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } } finally { 2 } : * NEW: merge(try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } } finally { 2 }|!) -> -{ try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } } finally { 2 } } : * COPY +1..a : {<: Iterable} NEW: call(1..a, next) -> +1 : * NEW: r(1) -> +2 : {<: Comparable} NEW: r(2) -> +3 : Int NEW: r(3) -> +2 > 3 : Boolean NEW: call(2 > 3, compareTo|, ) -> +continue@l !: * +{ continue@l } !: * COPY +if (2 > 3) { continue@l } : * NEW: merge(if (2 > 3) { continue@l }|!) -> +{ 1 if (2 > 3) { continue@l } } : * COPY +for (i in 1..a) { 1 if (2 > 3) { continue@l } } !: * +l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } !: * COPY +{ l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } } !: * COPY +2 : * NEW: r(2) -> +{ 2 } : * COPY +try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } } finally { 2 } : * NEW: merge(try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } } finally { 2 }|!) -> +{ try { l@ for (i in 1..a) { 1 if (2 > 3) { continue@l } } } finally { 2 } } : * COPY ===================== == t11 == fun t11() { @@ -335,14 +335,14 @@ fun t11() { } } --------------------- -1 : Unit NEW: r(1) -> -return 1 !: * -{ return 1 } !: * COPY -2 : Unit NEW: r(2) -> -return 2 !: * -{ return 2 } !: * COPY +1 : Unit NEW: r(1) -> +return 1 !: * +{ return 1 } !: * COPY +2 : Unit NEW: r(2) -> +return 2 !: * +{ return 2 } !: * COPY try { return 1 } finally { return 2 } : * NEW: merge(try { return 1 } finally { return 2 }|!) -> -{ try { return 1 } finally { return 2 } } : * COPY +{ try { return 1 } finally { return 2 } } : * COPY ===================== == t12 == fun t12() : Int { @@ -354,14 +354,14 @@ fun t12() : Int { } } --------------------- -1 : Int NEW: r(1) -> -return 1 !: * -{ return 1 } !: * COPY -3 : Int NEW: r(3) -> -doSmth(3) : * NEW: call(doSmth(3), doSmth|) -> -{ doSmth(3) } : * COPY +1 : Int NEW: r(1) -> +return 1 !: * +{ return 1 } !: * COPY +3 : Int NEW: r(3) -> +doSmth(3) : * NEW: call(doSmth(3), doSmth|) -> +{ doSmth(3) } : * COPY try { return 1 } finally { doSmth(3) } : * NEW: merge(try { return 1 } finally { doSmth(3) }|!) -> -{ try { return 1 } finally { doSmth(3) } } : * COPY +{ try { return 1 } finally { doSmth(3) } } : * COPY ===================== == t13 == fun t13() : Int { diff --git a/compiler/testData/cfg/controlStructures/For.instructions b/compiler/testData/cfg/controlStructures/For.instructions index a211a7a9789..d6d0cc6c119 100644 --- a/compiler/testData/cfg/controlStructures/For.instructions +++ b/compiler/testData/cfg/controlStructures/For.instructions @@ -12,19 +12,25 @@ L0: r(2) -> mark(1..2) call(1..2, rangeTo|, ) -> + mark(1..2) + call(1..2, iterator|) -> v(i) L2 [loop entry point]: L6 [condition entry point]: - jmp?(L3) NEXT:[read (Unit), magic[LOOP_RANGE_ITERATION](1..2|) -> ] PREV:[v(i), jmp(L2)] - magic[LOOP_RANGE_ITERATION](1..2|) -> - w(i|) + mark(1..2) PREV:[v(i), jmp(L2)] + call(1..2, hasNext) -> + jmp?(L3) NEXT:[read (Unit), mark(1..2)] + mark(1..2) + call(1..2, next) -> + magic[LOOP_RANGE_ITERATION](1..2|) -> + w(i|) mark(for (i in 1..2) { doSmth(i) }) L4 [body entry point]: 4 mark({ doSmth(i) }) - r(i) -> + r(i) -> mark(doSmth(i)) - call(doSmth(i), doSmth|) -> - 3 jmp(L2) NEXT:[jmp?(L3)] + call(doSmth(i), doSmth|) -> + 3 jmp(L2) NEXT:[mark(1..2)] L3 [loop exit point]: L5 [body exit point]: read (Unit) PREV:[jmp?(L3)] @@ -51,4 +57,4 @@ error: PREV:[] sink: PREV:[, ] -===================== \ No newline at end of file +===================== diff --git a/compiler/testData/cfg/controlStructures/For.values b/compiler/testData/cfg/controlStructures/For.values index ad69b1e1003..e6728607c89 100644 --- a/compiler/testData/cfg/controlStructures/For.values +++ b/compiler/testData/cfg/controlStructures/For.values @@ -5,15 +5,15 @@ fun t1() { } } --------------------- - : Int NEW: magic[LOOP_RANGE_ITERATION](1..2|) -> -1 : Int NEW: r(1) -> -2 : Int NEW: r(2) -> -1..2 : {<: Iterable} NEW: call(1..2, rangeTo|, ) -> -i : Int NEW: r(i) -> -doSmth(i) : * NEW: call(doSmth(i), doSmth|) -> -{ doSmth(i) } : * COPY -for (i in 1..2) { doSmth(i) } !: * -{ for (i in 1..2) { doSmth(i) } } !: * COPY + : Int NEW: magic[LOOP_RANGE_ITERATION](1..2|) -> +1 : Int NEW: r(1) -> +2 : Int NEW: r(2) -> +1..2 : {<: Iterable} NEW: call(1..2, next) -> +i : Int NEW: r(i) -> +doSmth(i) : * NEW: call(doSmth(i), doSmth|) -> +{ doSmth(i) } : * COPY +for (i in 1..2) { doSmth(i) } !: * +{ for (i in 1..2) { doSmth(i) } } !: * COPY ===================== == doSmth == fun doSmth(i: Int) {} diff --git a/compiler/testData/cfg/controlStructures/continueInFor.instructions b/compiler/testData/cfg/controlStructures/continueInFor.instructions index cf2e7d7cb6e..489a80bd2c2 100644 --- a/compiler/testData/cfg/controlStructures/continueInFor.instructions +++ b/compiler/testData/cfg/controlStructures/continueInFor.instructions @@ -16,26 +16,32 @@ L0: r(10) -> mark(1..10) call(1..10, rangeTo|, ) -> + mark(1..10) + call(1..10, iterator|) -> v(i) L2 [loop entry point]: L6 [condition entry point]: - jmp?(L3) NEXT:[read (Unit), magic[LOOP_RANGE_ITERATION](1..10|) -> ] PREV:[v(i), jmp(L6)] - magic[LOOP_RANGE_ITERATION](1..10|) -> - w(i|) + mark(1..10) PREV:[v(i), jmp(L6)] + call(1..10, hasNext) -> + jmp?(L3) NEXT:[read (Unit), mark(1..10)] + mark(1..10) + call(1..10, next) -> + magic[LOOP_RANGE_ITERATION](1..10|) -> + w(i|) mark(for (i in 1..10) { if (b) break; continue; }) L4 [body entry point]: 4 mark({ if (b) break; continue; }) mark(if (b) break) - r(b) -> - jf(L7|) NEXT:[read (Unit), jmp(L3)] + r(b) -> + jf(L7|) NEXT:[read (Unit), jmp(L3)] jmp(L3) NEXT:[read (Unit)] -- jmp(L8) NEXT:[merge(if (b) break|!) -> ] PREV:[] +- jmp(L8) NEXT:[merge(if (b) break|!) -> ] PREV:[] L7 [else branch]: - read (Unit) PREV:[jf(L7|)] + read (Unit) PREV:[jf(L7|)] L8 ['if' expression result]: - merge(if (b) break|!) -> - jmp(L6) NEXT:[jmp?(L3)] -- 3 jmp(L2) NEXT:[jmp?(L3)] PREV:[] + merge(if (b) break|!) -> + jmp(L6) NEXT:[mark(1..10)] +- 3 jmp(L2) NEXT:[mark(1..10)] PREV:[] L3 [loop exit point]: L5 [body exit point]: read (Unit) PREV:[jmp?(L3), jmp(L3)] @@ -45,4 +51,4 @@ error: PREV:[] sink: PREV:[, ] -===================== \ No newline at end of file +===================== diff --git a/compiler/testData/cfg/controlStructures/continueInFor.values b/compiler/testData/cfg/controlStructures/continueInFor.values index aaa7924708d..d3a7d4ef54d 100644 --- a/compiler/testData/cfg/controlStructures/continueInFor.values +++ b/compiler/testData/cfg/controlStructures/continueInFor.values @@ -7,15 +7,15 @@ fun test(b: Boolean) { } --------------------- : Boolean NEW: magic[FAKE_INITIALIZER](b: Boolean) -> - : Int NEW: magic[LOOP_RANGE_ITERATION](1..10|) -> + : Int NEW: magic[LOOP_RANGE_ITERATION](1..10|) -> 1 : Int NEW: r(1) -> 10 : Int NEW: r(10) -> -1..10 : {<: Iterable} NEW: call(1..10, rangeTo|, ) -> -b : Boolean NEW: r(b) -> -break !: * -if (b) break : * NEW: merge(if (b) break|!) -> -continue !: * -{ if (b) break; continue; } !: * COPY -for (i in 1..10) { if (b) break; continue; } !: * -{ for (i in 1..10) { if (b) break; continue; } } !: * COPY -===================== \ No newline at end of file +1..10 : {<: Iterable} NEW: call(1..10, next) -> +b : Boolean NEW: r(b) -> +break !: * +if (b) break : * NEW: merge(if (b) break|!) -> +continue !: * +{ if (b) break; continue; } !: * COPY +for (i in 1..10) { if (b) break; continue; } !: * +{ for (i in 1..10) { if (b) break; continue; } } !: * COPY +===================== diff --git a/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.instructions b/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.instructions index 22e5f2052c5..1806a840540 100644 --- a/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.instructions +++ b/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.instructions @@ -67,39 +67,45 @@ L4 [start finally]: r(2) -> mark(1..2) call(1..2, rangeTo|, ) -> + mark(1..2) + call(1..2, iterator|) -> v(i) L5 [loop entry point]: L9 [condition entry point]: - jmp?(L6) NEXT:[read (Unit), magic[LOOP_RANGE_ITERATION](1..2|) -> ] PREV:[v(i), jmp(L5)] - magic[LOOP_RANGE_ITERATION](1..2|) -> - w(i|) + mark(1..2) PREV:[v(i), jmp(L5)] + call(1..2, hasNext) -> + jmp?(L6) NEXT:[read (Unit), mark(1..2)] + mark(1..2) + call(1..2, next) -> + magic[LOOP_RANGE_ITERATION](1..2|) -> + w(i|) mark(for (i in 1..2) { }) L7 [body entry point]: 6 mark({ }) read (Unit) - 5 jmp(L5) NEXT:[jmp?(L6)] + 5 jmp(L5) NEXT:[mark(1..2)] L6 [loop exit point]: L8 [body exit point]: read (Unit) PREV:[jmp?(L6)] 4 mark(bar()) - call(bar(), bar) -> - ret(*|) L1 NEXT:[] + call(bar(), bar) -> + ret(*|) L1 NEXT:[] L10 [finish finally]: - 3 ret(*|) L1 NEXT:[] PREV:[] -- jmp(L11) NEXT:[merge(if (n < 0) return 0|!) -> ] PREV:[] +- jmp(L11) NEXT:[merge(if (n < 0) return 0|!) -> ] PREV:[] L3 [else branch]: read (Unit) PREV:[jf(L3|)] L11 ['if' expression result]: - merge(if (n < 0) return 0|!) -> + merge(if (n < 0) return 0|!) -> mark(n.let { return it }) - r(n) -> + r(n) -> mark({ return it }) - jmp?(L12) NEXT:[r({ return it }) -> , d({ return it })] + jmp?(L12) NEXT:[r({ return it }) -> , d({ return it })] d({ return it }) NEXT:[] L12 [after local declaration]: - r({ return it }) -> PREV:[jmp?(L12)] + r({ return it }) -> PREV:[jmp?(L12)] mark(let { return it }) - call(let { return it }, let|, ) -> + call(let { return it }, let|, ) -> 2 jmp?(L2) NEXT:[mark({ for (i in 1..2) { } return bar() }), jmp(L20)] jmp(L20) NEXT:[mark({ for (i in 1..2) { } return bar() })] L2 [onExceptionToFinallyBlock]: @@ -108,23 +114,29 @@ L2 [onExceptionToFinallyBlock]: r(2) -> mark(1..2) call(1..2, rangeTo|, ) -> + mark(1..2) + call(1..2, iterator|) -> v(i) L21 [copy of L5, loop entry point]: L25 [copy of L9, condition entry point]: - jmp?(L22) NEXT:[read (Unit), magic[LOOP_RANGE_ITERATION](1..2|) -> ] PREV:[v(i), jmp(L21)] - magic[LOOP_RANGE_ITERATION](1..2|) -> - w(i|) + mark(1..2) PREV:[v(i), jmp(L21)] + call(1..2, hasNext) -> + jmp?(L22) NEXT:[read (Unit), mark(1..2)] + mark(1..2) + call(1..2, next) -> + magic[LOOP_RANGE_ITERATION](1..2|) -> + w(i|) mark(for (i in 1..2) { }) L23 [copy of L7, body entry point]: 6 mark({ }) read (Unit) - 5 jmp(L21) NEXT:[jmp?(L22)] + 5 jmp(L21) NEXT:[mark(1..2)] L22 [copy of L6, loop exit point]: L24 [copy of L8, body exit point]: read (Unit) PREV:[jmp?(L22)] 4 mark(bar()) - call(bar(), bar) -> - ret(*|) L1 NEXT:[] + call(bar(), bar) -> + ret(*|) L1 NEXT:[] - 2 jmp(error) NEXT:[] PREV:[] L20 [skipFinallyToErrorBlock]: 4 mark({ for (i in 1..2) { } return bar() }) PREV:[jmp(L20)] @@ -132,26 +144,32 @@ L20 [skipFinallyToErrorBlock]: r(2) -> mark(1..2) call(1..2, rangeTo|, ) -> + mark(1..2) + call(1..2, iterator|) -> v(i) L26 [copy of L5, loop entry point]: L30 [copy of L9, condition entry point]: - jmp?(L27) NEXT:[read (Unit), magic[LOOP_RANGE_ITERATION](1..2|) -> ] PREV:[v(i), jmp(L26)] - magic[LOOP_RANGE_ITERATION](1..2|) -> - w(i|) + mark(1..2) PREV:[v(i), jmp(L26)] + call(1..2, hasNext) -> + jmp?(L27) NEXT:[read (Unit), mark(1..2)] + mark(1..2) + call(1..2, next) -> + magic[LOOP_RANGE_ITERATION](1..2|) -> + w(i|) mark(for (i in 1..2) { }) L28 [copy of L7, body entry point]: 6 mark({ }) read (Unit) - 5 jmp(L26) NEXT:[jmp?(L27)] + 5 jmp(L26) NEXT:[mark(1..2)] L27 [copy of L6, loop exit point]: L29 [copy of L8, body exit point]: read (Unit) PREV:[jmp?(L27)] 4 mark(bar()) - call(bar(), bar) -> - ret(*|) L1 NEXT:[] -- 2 merge(try { if (n < 0) return 0 n.let { return it } } finally { for (i in 1..2) { } return bar() }|) -> PREV:[] + call(bar(), bar) -> + ret(*|) L1 NEXT:[] +- 2 merge(try { if (n < 0) return 0 n.let { return it } } finally { for (i in 1..2) { } return bar() }|) -> PREV:[] L1: - 1 NEXT:[] PREV:[ret(*|) L1, ret(*|) L1, ret(*|) L1, ret(*|) L1] + 1 NEXT:[] PREV:[ret(*|) L1, ret(*|) L1, ret(*|) L1, ret(*|) L1] error: PREV:[] sink: @@ -169,29 +187,35 @@ L13: r(2) -> mark(1..2) call(1..2, rangeTo|, ) -> + mark(1..2) + call(1..2, iterator|) -> v(i) L15 [copy of L5, loop entry point]: L19 [copy of L9, condition entry point]: - jmp?(L16) NEXT:[read (Unit), magic[LOOP_RANGE_ITERATION](1..2|) -> ] PREV:[v(i), jmp(L15)] - magic[LOOP_RANGE_ITERATION](1..2|) -> - w(i|) + mark(1..2) PREV:[v(i), jmp(L15)] + call(1..2, hasNext) -> + jmp?(L16) NEXT:[read (Unit), mark(1..2)] + mark(1..2) + call(1..2, next) -> + magic[LOOP_RANGE_ITERATION](1..2|) -> + w(i|) mark(for (i in 1..2) { }) L17 [copy of L7, body entry point]: 6 mark({ }) read (Unit) - 5 jmp(L15) NEXT:[jmp?(L16)] + 5 jmp(L15) NEXT:[mark(1..2)] L16 [copy of L6, loop exit point]: L18 [copy of L8, body exit point]: - read (Unit) PREV:[jmp?(L16)] + read (Unit) PREV:[jmp?(L16)] 4 mark(bar()) - call(bar(), bar) -> - ret(*|) L1 NEXT:[] -- 5 ret(*|) L1 NEXT:[] PREV:[] -- 4 ret(*|!) L14 PREV:[] + call(bar(), bar) -> + ret(*|) L1 NEXT:[] +- 5 ret(*|) L1 NEXT:[] PREV:[] +- 4 ret(*|!) L14 PREV:[] L14: - NEXT:[] PREV:[] + NEXT:[] PREV:[] error: - PREV:[] + PREV:[] sink: - PREV:[, ] + PREV:[, ] ===================== diff --git a/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.values b/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.values index 23a0a73e018..0e5c229514e 100644 --- a/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.values +++ b/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.values @@ -25,32 +25,32 @@ fun foo(n: Int): Int { } --------------------- : Int NEW: magic[FAKE_INITIALIZER](n: Int) -> - : Int NEW: magic[LOOP_RANGE_ITERATION](1..2|) -> + : Int NEW: magic[LOOP_RANGE_ITERATION](1..2|) -> n : {<: Comparable} NEW: r(n) -> 0 : Int NEW: r(0) -> n < 0 : Boolean NEW: call(n < 0, compareTo|, ) -> 0 : Int NEW: r(0) -> -return 0 !: * -if (n < 0) return 0 : * NEW: merge(if (n < 0) return 0|!) -> -n : Int NEW: r(n) -> -{ return it } : {<: (Int) -> ???} NEW: r({ return it }) -> -let { return it } : * NEW: call(let { return it }, let|, ) -> -n.let { return it } : * COPY -{ if (n < 0) return 0 n.let { return it } } : * COPY +return 0 !: * +if (n < 0) return 0 : * NEW: merge(if (n < 0) return 0|!) -> +n : Int NEW: r(n) -> +{ return it } : {<: (Int) -> ???} NEW: r({ return it }) -> +let { return it } : * NEW: call(let { return it }, let|, ) -> +n.let { return it } : * COPY +{ if (n < 0) return 0 n.let { return it } } : * COPY 1 : Int NEW: r(1) -> 2 : Int NEW: r(2) -> -1..2 : {<: Iterable} NEW: call(1..2, rangeTo|, ) -> -bar() : Int NEW: call(bar(), bar) -> -return bar() !: * -{ for (i in 1..2) { } return bar() } !: * COPY -try { if (n < 0) return 0 n.let { return it } } finally { for (i in 1..2) { } return bar() } : * NEW: merge(try { if (n < 0) return 0 n.let { return it } } finally { for (i in 1..2) { } return bar() }|) -> -{ try { if (n < 0) return 0 n.let { return it } } finally { for (i in 1..2) { } return bar() } } : * COPY +1..2 : {<: Iterable} NEW: call(1..2, next) -> +bar() : Int NEW: call(bar(), bar) -> +return bar() !: * +{ for (i in 1..2) { } return bar() } !: * COPY +try { if (n < 0) return 0 n.let { return it } } finally { for (i in 1..2) { } return bar() } : * NEW: merge(try { if (n < 0) return 0 n.let { return it } } finally { for (i in 1..2) { } return bar() }|) -> +{ try { if (n < 0) return 0 n.let { return it } } finally { for (i in 1..2) { } return bar() } } : * COPY ===================== == anonymous_0 == { return it } --------------------- - : Int NEW: magic[LOOP_RANGE_ITERATION](1..2|) -> -it : Int NEW: r(it) -> + : Int NEW: magic[LOOP_RANGE_ITERATION](1..2|) -> +it : Int NEW: r(it) -> return it !: * -return it !: * COPY +return it !: * COPY ===================== diff --git a/compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt b/compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt new file mode 100644 index 00000000000..2c42a69ceee --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt @@ -0,0 +1,130 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +interface AsyncGenerator { + suspend fun yield(value: T) +} + +interface AsyncSequence { + operator fun iterator(): AsyncIterator +} + +interface AsyncIterator { + operator suspend fun hasNext(): Boolean + operator suspend fun next(): T +} + +fun asyncGenerate(block: suspend AsyncGenerator.() -> Unit): AsyncSequence = object : AsyncSequence { + override fun iterator(): AsyncIterator { + val iterator = AsyncGeneratorIterator() + iterator.nextStep = block.createCoroutine(receiver = iterator, completion = iterator) + return iterator + } +} + +class AsyncGeneratorIterator: AsyncIterator, AsyncGenerator, Continuation { + var computedNext = false + var nextValue: T? = null + var nextStep: Continuation? = null + + // if (computesNext) computeContinuation is Continuation + // if (!computesNext) computeContinuation is Continuation + var computesNext = false + var computeContinuation: Continuation<*>? = null + + override val context = EmptyCoroutineContext + + suspend fun computeHasNext(): Boolean = suspendCoroutineOrReturn { c -> + computesNext = false + computeContinuation = c + nextStep!!.resume(Unit) + SUSPENDED_MARKER + } + + suspend fun computeNext(): T = suspendCoroutineOrReturn { c -> + computesNext = true + computeContinuation = c + nextStep!!.resume(Unit) + SUSPENDED_MARKER + } + + @Suppress("UNCHECKED_CAST") + fun resumeIterator(exception: Throwable?) { + if (exception != null) { + done() + computeContinuation!!.resumeWithException(exception) + return + } + if (computesNext) { + computedNext = false + (computeContinuation as Continuation).resume(nextValue as T) + } else { + (computeContinuation as Continuation).resume(nextStep != null) + } + } + + override suspend fun hasNext(): Boolean { + if (!computedNext) return computeHasNext() + return nextStep != null + } + + override suspend fun next(): T { + if (!computedNext) return computeNext() + computedNext = false + return nextValue as T + } + + private fun done() { + computedNext = true + nextStep = null + } + + // Completion continuation implementation + override fun resume(value: Unit) { + done() + resumeIterator(null) + } + + override fun resumeWithException(exception: Throwable) { + done() + resumeIterator(exception) + } + + // Generator implementation + override suspend fun yield(value: T): Unit = suspendCoroutineOrReturn { c -> + computedNext = true + nextValue = value + nextStep = c + resumeIterator(null) + SUSPENDED_MARKER + } +} + +suspend fun AsyncSequence.toList(): List { + val out = arrayListOf() + for (e in this@toList) out += e // fails at this line + return out +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + val seq = asyncGenerate { + yield("O") + yield("K") + } + + var res = listOf() + + builder { + res = seq.toList() + } + + if (res.size > 2) return "fail 1: ${res.size}" + + return res[0] + res[1] +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/asyncIteratorToList.txt b/compiler/testData/codegen/light-analysis/coroutines/asyncIteratorToList.txt new file mode 100644 index 00000000000..ec35b258778 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/asyncIteratorToList.txt @@ -0,0 +1,81 @@ +@kotlin.Metadata +public interface AsyncGenerator { + public abstract @org.jetbrains.annotations.Nullable method yield(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class AsyncGeneratorIterator { + private @org.jetbrains.annotations.Nullable field computeContinuation: kotlin.coroutines.Continuation + private field computedNext: boolean + private field computesNext: boolean + private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.EmptyCoroutineContext + private @org.jetbrains.annotations.Nullable field nextStep: kotlin.coroutines.Continuation + private @org.jetbrains.annotations.Nullable field nextValue: java.lang.Object + public method (): void + public final @org.jetbrains.annotations.Nullable method computeHasNext(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public final @org.jetbrains.annotations.Nullable method computeNext(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + private final method done(): void + public final @org.jetbrains.annotations.Nullable method getComputeContinuation(): kotlin.coroutines.Continuation + public final method getComputedNext(): boolean + public final method getComputesNext(): boolean + public synthetic method getContext(): kotlin.coroutines.CoroutineContext + public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.EmptyCoroutineContext + public final @org.jetbrains.annotations.Nullable method getNextStep(): kotlin.coroutines.Continuation + public final @org.jetbrains.annotations.Nullable method getNextValue(): java.lang.Object + public @org.jetbrains.annotations.Nullable method hasNext(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public @org.jetbrains.annotations.Nullable method next(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public method resume(@org.jetbrains.annotations.NotNull p0: kotlin.Unit): void + public synthetic method resume(p0: java.lang.Object): void + public final @kotlin.Suppress method resumeIterator(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void + public final method setComputeContinuation(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void + public final method setComputedNext(p0: boolean): void + public final method setComputesNext(p0: boolean): void + public final method setNextStep(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void + public final method setNextValue(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public @org.jetbrains.annotations.Nullable method yield(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public interface AsyncIterator { + public abstract @org.jetbrains.annotations.Nullable method hasNext(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public abstract @org.jetbrains.annotations.Nullable method next(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class AsyncIteratorToListKt { + public final static @org.jetbrains.annotations.NotNull method asyncGenerate(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2): AsyncSequence + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void + public final static @org.jetbrains.annotations.Nullable method toList(@org.jetbrains.annotations.NotNull p0: AsyncSequence, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public interface AsyncSequence { + public abstract @org.jetbrains.annotations.NotNull method iterator(): AsyncIterator +} + +@kotlin.Metadata +public final class CoroutineUtilKt { + public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation +} + +@kotlin.Metadata +public class EmptyContinuation { + public final static field Companion: EmptyContinuation.Companion + private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.CoroutineContext + inner class EmptyContinuation/Companion + public @synthetic.kotlin.jvm.GeneratedByJvmOverloads method (): void + public method (@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.CoroutineContext): void + public synthetic method (p0: kotlin.coroutines.CoroutineContext, p1: int, p2: kotlin.jvm.internal.DefaultConstructorMarker): void + public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.CoroutineContext + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + +@kotlin.Metadata +public final static class EmptyContinuation/Companion { + inner class EmptyContinuation/Companion + private method (): void +} 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 6bd7b624807..41ef10726bb 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 @@ -4613,6 +4613,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("asyncIteratorToList.kt") + public void testAsyncIteratorToList() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt"); + doTest(fileName); + } + @TestMetadata("await.kt") public void testAwait() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 3488dea6191..da9d1c44b57 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -4613,6 +4613,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("asyncIteratorToList.kt") + public void testAsyncIteratorToList() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt"); + doTest(fileName); + } + @TestMetadata("await.kt") public void testAwait() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index cbc73944e01..605f61b4424 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -4613,6 +4613,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis doTest(fileName); } + @TestMetadata("asyncIteratorToList.kt") + public void testAsyncIteratorToList() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt"); + doTest(fileName); + } + @TestMetadata("await.kt") public void testAwait() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.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 c20f5e9a615..2771449f209 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 @@ -5304,6 +5304,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("asyncIteratorToList.kt") + public void testAsyncIteratorToList() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt"); + doTest(fileName); + } + @TestMetadata("await.kt") public void testAwait() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/await.kt");