diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index d6e84e3ec52..3abffaeb902 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.cfg import com.google.common.collect.Lists +import com.intellij.psi.PsiElement import com.intellij.psi.tree.IElementType import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.SmartFMap @@ -104,8 +105,12 @@ class ControlFlowProcessor(private val trace: BindingTrace) { builder.bindLabel(afterDeclaration) } + private class CatchFinallyLabels(val onException: Label?, val toFinally: Label?, val tryExpression: KtTryExpression?) + private inner class CFPVisitor(private val builder: ControlFlowBuilder) : KtVisitorVoid() { + private val catchFinallyStack = Stack() + private val conditionVisitor = object : KtVisitorVoid() { private fun getSubjectExpression(condition: KtWhenCondition): KtExpression? { @@ -567,9 +572,11 @@ class ControlFlowProcessor(private val trace: BindingTrace) { fun generate() { val finalExpression = finallyBlock?.finalExpression ?: return + catchFinallyStack.push(CatchFinallyLabels(null, null, null)) startFinally?.let { assert(finishFinally != null) { "startFinally label is set to $startFinally but finishFinally label is not set" } builder.repeatPseudocode(it, finishFinally!!) + catchFinallyStack.pop() return } builder.createUnboundLabel("start finally").let { @@ -581,6 +588,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) { finishFinally = it builder.bindLabel(it) } + catchFinallyStack.pop() } } @@ -647,7 +655,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) { } val tryBlock = expression.tryBlock + catchFinallyStack.push(CatchFinallyLabels(onException, onExceptionToFinallyBlock, expression)) generateInstructions(tryBlock) + generateJumpsToCatchAndFinally() + catchFinallyStack.pop() if (hasCatches && onException != null) { val afterCatches = builder.createUnboundLabel("afterCatches") @@ -796,6 +807,9 @@ class ControlFlowProcessor(private val trace: BindingTrace) { override fun visitBreakExpression(expression: KtBreakExpression) { val loop = getCorrespondingLoop(expression) if (loop != null) { + if (jumpCrossesTryCatchBoundary(expression, loop)) { + generateJumpsToCatchAndFinally() + } if (jumpDoesNotCrossFunctionBoundary(expression, loop)) { builder.getLoopExitPoint(loop)?.let { builder.jump(it, expression) } } @@ -805,6 +819,9 @@ class ControlFlowProcessor(private val trace: BindingTrace) { override fun visitContinueExpression(expression: KtContinueExpression) { val loop = getCorrespondingLoop(expression) if (loop != null) { + if (jumpCrossesTryCatchBoundary(expression, loop)) { + generateJumpsToCatchAndFinally() + } if (jumpDoesNotCrossFunctionBoundary(expression, loop)) { builder.getLoopConditionEntryPoint(loop)?.let { builder.jump(it, expression) } } @@ -860,6 +877,24 @@ class ControlFlowProcessor(private val trace: BindingTrace) { return loop } + private fun returnCrossesTryCatchBoundary(returnExpression: KtReturnExpression): Boolean { + val targetLabel = returnExpression.getTargetLabel() ?: return true + val labeledElement = trace.get(BindingContext.LABEL_TARGET, targetLabel) ?: return true + return jumpCrossesTryCatchBoundary(returnExpression, labeledElement) + } + + private fun jumpCrossesTryCatchBoundary(jumpExpression: KtExpressionWithLabel, jumpTarget: PsiElement): Boolean { + var current = jumpExpression.parent + while (current != null) { + when (current) { + jumpTarget -> return false + is KtTryExpression -> return true + else -> current = current.parent + } + } + return false + } + private fun jumpDoesNotCrossFunctionBoundary(jumpExpression: KtExpressionWithLabel, jumpTarget: KtLoopExpression): Boolean { val bindingContext = trace.bindingContext @@ -882,6 +917,9 @@ class ControlFlowProcessor(private val trace: BindingTrace) { } override fun visitReturnExpression(expression: KtReturnExpression) { + if (returnCrossesTryCatchBoundary(expression)) { + generateJumpsToCatchAndFinally() + } val returnedExpression = expression.returnedExpression if (returnedExpression != null) { generateInstructions(returnedExpression) @@ -1124,15 +1162,30 @@ class ControlFlowProcessor(private val trace: BindingTrace) { } } + private fun generateJumpsToCatchAndFinally() { + if (catchFinallyStack.isNotEmpty()) { + with(catchFinallyStack.peek()) { + if (tryExpression != null) { + onException?.let { + builder.nondeterministicJump(it, tryExpression, null) + } + toFinally?.let { + builder.nondeterministicJump(it, tryExpression, null) + } + } + } + } + } + override fun visitThrowExpression(expression: KtThrowExpression) { mark(expression) - val thrownExpression = expression.thrownExpression ?: return + generateJumpsToCatchAndFinally() + val thrownExpression = expression.thrownExpression ?: return generateInstructions(thrownExpression) val thrownValue = builder.getBoundValue(thrownExpression) ?: return - builder.throwException(expression, thrownValue) } diff --git a/compiler/testData/cfg-variables/bugs/kt10243.instructions b/compiler/testData/cfg-variables/bugs/kt10243.instructions index fd17f37087d..2e0801cc301 100644 --- a/compiler/testData/cfg-variables/bugs/kt10243.instructions +++ b/compiler/testData/cfg-variables/bugs/kt10243.instructions @@ -43,10 +43,11 @@ L3 [else branch]: read (Unit) INIT: in: {f=ID} out: {f=ID} L4 ['if' expression result]: merge(if (f) { x = 0 }|!) -> INIT: in: {f=ID, x=I} out: {f=ID, x=I} - 2 jmp(L5) + 2 jmp?(L2) + jmp(L5) L2 [onExceptionToFinallyBlock]: L6 [start finally]: - 3 mark({ fun bar() {} }) INIT: in: {f=ID} out: {f=ID} + 3 mark({ fun bar() {} }) jmp?(L7) d(fun bar() {}) L7 [after local declaration]: @@ -54,7 +55,7 @@ L10 [finish finally]: 2 jmp(error) L5 [skipFinallyToErrorBlock]: L11 [copy of L2, onExceptionToFinallyBlock]: - 3 mark({ fun bar() {} }) INIT: in: {f=ID, x=I} out: {f=ID, x=I} + 3 mark({ fun bar() {} }) jmp?(L12) d(fun bar() {}) L12 [copy of L7, after local declaration]: @@ -62,15 +63,15 @@ L12 [copy of L7, after local declaration]: L1: 1 error: - INIT: in: {f=ID} out: {f=ID} + sink: - INIT: in: {f=ID, x=I} out: {f=ID, x=I} USE: in: {} out: {} + USE: in: {} out: {} ===================== == bar == fun bar() {} --------------------- L8: - 4 INIT: in: {f=ID} out: {f=ID} + 4 INIT: in: {f=ID, x=I} out: {f=ID, x=I} 5 mark({}) read (Unit) L9: @@ -78,7 +79,7 @@ L9: error: INIT: in: {} out: {} sink: - INIT: in: {f=ID} out: {f=ID} USE: in: {} out: {} + INIT: in: {f=ID, x=I} out: {f=ID, x=I} USE: in: {} out: {} ===================== == bar == fun bar() {} diff --git a/compiler/testData/cfg-variables/bugs/kt4764.instructions b/compiler/testData/cfg-variables/bugs/kt4764.instructions index 74ab5e7d8ea..b08aa6912e0 100644 --- a/compiler/testData/cfg-variables/bugs/kt4764.instructions +++ b/compiler/testData/cfg-variables/bugs/kt4764.instructions @@ -32,6 +32,7 @@ L0: r(false) -> jf(L3|) 4 mark({ return "fail" }) + jmp?(L2) mark("fail") r("fail") -> L4 [start finally]: @@ -60,12 +61,15 @@ L3 [else branch]: mark("test") r("test") -> USE: in: {foo=WRITTEN_AFTER_READ} out: {foo=WRITTEN_AFTER_READ} w(foo|) USE: in: {foo=READ} out: {foo=WRITTEN_AFTER_READ} - 6 jmp(L11) + 6 jmp?(L9) + jmp?(L10) + jmp(L11) L9 [onException]: 7 v(e: Exception) INIT: in: {foo=ID} out: {e=D, foo=ID} magic[FAKE_INITIALIZER](e: Exception) -> INIT: in: {e=D, foo=ID} out: {e=D, foo=ID} w(e|) INIT: in: {e=D, foo=ID} out: {e=ID, foo=ID} 8 mark({ return "fail" }) INIT: in: {e=ID, foo=ID} out: {e=ID, foo=ID} + jmp?(L2) mark("fail") r("fail") -> L12 [start finally]: @@ -99,6 +103,7 @@ L8 [else branch]: read (Unit) L15 ['if' expression result]: merge(if (false) { var foo: String? = null try { foo = "test" } catch (e: Exception) { return "fail" } finally { println(foo) // Variable 'foo' must be initialized } }|) -> + jmp?(L2) mark("fail") r("fail") -> mark({}) @@ -111,7 +116,8 @@ L16 ['if' expression result]: merge(if (false) { if (false) { var foo: String? = null try { foo = "test" } catch (e: Exception) { return "fail" } finally { println(foo) // Variable 'foo' must be initialized } } return "fail" }|!) -> L6 ['if' expression result]: 3 merge(if (false) { return "fail" } else { if (false) { if (false) { var foo: String? = null try { foo = "test" } catch (e: Exception) { return "fail" } finally { println(foo) // Variable 'foo' must be initialized } } return "fail" } }|!, ) -> - 2 jmp(L17) + 2 jmp?(L2) + jmp(L17) L2 [onExceptionToFinallyBlock]: 5 mark({}) read (Unit) diff --git a/compiler/testData/cfg-variables/bugs/kt5469.instructions b/compiler/testData/cfg-variables/bugs/kt5469.instructions new file mode 100644 index 00000000000..9b8a48de51c --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/kt5469.instructions @@ -0,0 +1,47 @@ +== f == +fun f() { + var foo = 1 + try { + foo = 2 + throw RuntimeException() + } catch (e: Throwable) { + println(foo) + } +} +--------------------- +L0: + 1 INIT: in: {} out: {} USE: in: {} out: {} + 2 mark({ var foo = 1 try { foo = 2 throw RuntimeException() } catch (e: Throwable) { println(foo) } }) + v(var foo = 1) INIT: in: {} out: {foo=D} + r(1) -> INIT: in: {foo=D} out: {foo=D} + w(foo|) INIT: in: {foo=D} out: {foo=ID} + mark(try { foo = 2 throw RuntimeException() } catch (e: Throwable) { println(foo) }) INIT: in: {foo=ID} out: {foo=ID} + jmp?(L2) USE: in: {foo=READ} out: {foo=READ} + 3 mark({ foo = 2 throw RuntimeException() }) + r(2) -> USE: in: {foo=WRITTEN_AFTER_READ} out: {foo=WRITTEN_AFTER_READ} + w(foo|) USE: in: {foo=READ} out: {foo=WRITTEN_AFTER_READ} + mark(throw RuntimeException()) + jmp?(L2) USE: in: {foo=READ} out: {foo=READ} + mark(RuntimeException()) + call(RuntimeException(), ) -> + throw (throw RuntimeException()|) USE: in: {} out: {} +- 2 jmp?(L2) +- jmp(L3) +L2 [onException]: + 3 v(e: Throwable) INIT: in: {foo=ID} out: {e=D, foo=ID} + magic[FAKE_INITIALIZER](e: Throwable) -> INIT: in: {e=D, foo=ID} out: {e=D, foo=ID} + w(e|) INIT: in: {e=D, foo=ID} out: {e=ID, foo=ID} + 4 mark({ println(foo) }) INIT: in: {e=ID, foo=ID} out: {e=ID, foo=ID} USE: in: {foo=READ} out: {foo=READ} + r(foo) -> USE: in: {} out: {foo=READ} + mark(println(foo)) + magic[UNRESOLVED_CALL](println(foo)|, !) -> + 3 jmp(L3) +L3 [afterCatches]: + 2 merge(try { foo = 2 throw RuntimeException() } catch (e: Throwable) { println(foo) }|!, ) -> INIT: in: {foo=ID} out: {foo=ID} +L1: + 1 INIT: in: {} out: {} +error: + +sink: + USE: in: {} out: {} +===================== diff --git a/compiler/testData/cfg-variables/bugs/kt5469.kt b/compiler/testData/cfg-variables/bugs/kt5469.kt new file mode 100644 index 00000000000..29f21759943 --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/kt5469.kt @@ -0,0 +1,9 @@ +fun f() { + var foo = 1 + try { + foo = 2 + throw RuntimeException() + } catch (e: Throwable) { + println(foo) + } +} diff --git a/compiler/testData/cfg-variables/bugs/kt5469.values b/compiler/testData/cfg-variables/bugs/kt5469.values new file mode 100644 index 00000000000..170f16975aa --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/kt5469.values @@ -0,0 +1,24 @@ +== f == +fun f() { + var foo = 1 + try { + foo = 2 + throw RuntimeException() + } catch (e: Throwable) { + println(foo) + } +} +--------------------- + : {<: Throwable} NEW: magic[FAKE_INITIALIZER](e: Throwable) -> +1 : Int NEW: r(1) -> +2 : Int NEW: r(2) -> +RuntimeException() : {<: Throwable} NEW: call(RuntimeException(), ) -> +throw RuntimeException() !: * +{ foo = 2 throw RuntimeException() } !: * COPY +println !: * +foo : * NEW: r(foo) -> +println(foo) : * NEW: magic[UNRESOLVED_CALL](println(foo)|, !) -> +{ println(foo) } : * COPY +try { foo = 2 throw RuntimeException() } catch (e: Throwable) { println(foo) } : * NEW: merge(try { foo = 2 throw RuntimeException() } catch (e: Throwable) { println(foo) }|!, ) -> +{ var foo = 1 try { foo = 2 throw RuntimeException() } catch (e: Throwable) { println(foo) } } : * COPY +===================== diff --git a/compiler/testData/cfg-variables/bugs/kt9825.instructions b/compiler/testData/cfg-variables/bugs/kt9825.instructions index 0ec9d7e0691..22e64c96aa9 100644 --- a/compiler/testData/cfg-variables/bugs/kt9825.instructions +++ b/compiler/testData/cfg-variables/bugs/kt9825.instructions @@ -11,7 +11,7 @@ fun test5() { } --------------------- L0: - 1 INIT: in: {} out: {} USE: in: {} out: {} + 1 INIT: in: {} out: {} USE: in: {} out: {} 2 mark({ var a: Int try { a = 3 } finally { a = 5 } a.hashCode() }) v(var a: Int) INIT: in: {} out: {a=D} mark(try { a = 3 } finally { a = 5 }) INIT: in: {a=D} out: {a=D} @@ -19,22 +19,23 @@ L0: 3 mark({ a = 3 }) r(3) -> w(a|) INIT: in: {a=D} out: {a=ID} - 2 jmp(L3) INIT: in: {a=ID} out: {a=ID} USE: in: {a=WRITTEN_AFTER_READ} out: {a=WRITTEN_AFTER_READ} + 2 jmp?(L2) INIT: in: {a=ID} out: {a=ID} + jmp(L3) USE: in: {a=WRITTEN_AFTER_READ} out: {a=WRITTEN_AFTER_READ} L2 [onExceptionToFinallyBlock]: L4 [start finally]: - 3 mark({ a = 5 }) INIT: in: {a=D} out: {a=D} - r(5) -> USE: in: {a=ONLY_WRITTEN_NEVER_READ} out: {a=ONLY_WRITTEN_NEVER_READ} - w(a|) INIT: in: {a=D} out: {a=ID} USE: in: {} out: {a=ONLY_WRITTEN_NEVER_READ} + 3 mark({ a = 5 }) INIT: in: {a=I?D} out: {a=I?D} + r(5) -> USE: in: {a=ONLY_WRITTEN_NEVER_READ} out: {a=ONLY_WRITTEN_NEVER_READ} + w(a|) INIT: in: {a=I?D} out: {a=ID} USE: in: {} out: {a=ONLY_WRITTEN_NEVER_READ} L5 [finish finally]: - 2 jmp(error) INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {} + 2 jmp(error) INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {} L3 [skipFinallyToErrorBlock]: L6 [copy of L2, onExceptionToFinallyBlock]: 3 mark({ a = 5 }) - r(5) -> USE: in: {a=WRITTEN_AFTER_READ} out: {a=WRITTEN_AFTER_READ} - w(a|) USE: in: {a=READ} out: {a=WRITTEN_AFTER_READ} + r(5) -> USE: in: {a=WRITTEN_AFTER_READ} out: {a=WRITTEN_AFTER_READ} + w(a|) USE: in: {a=READ} out: {a=WRITTEN_AFTER_READ} 2 merge(try { a = 3 } finally { a = 5 }|!) -> - mark(a.hashCode()) USE: in: {a=READ} out: {a=READ} - r(a) -> USE: in: {} out: {a=READ} + mark(a.hashCode()) USE: in: {a=READ} out: {a=READ} + r(a) -> USE: in: {} out: {a=READ} mark(hashCode()) call(hashCode(), hashCode|) -> L1: @@ -42,5 +43,5 @@ L1: error: sink: - USE: in: {} out: {} + USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/lexicalScopes/tryScope.instructions b/compiler/testData/cfg-variables/lexicalScopes/tryScope.instructions index 8abb8e4d393..8d5d8633284 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/tryScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/tryScope.instructions @@ -24,7 +24,9 @@ L0: 3 mark({ foo() }) mark(foo()) call(foo(), foo) -> - 2 jmp(L4) USE: in: {} out: {} + 2 jmp?(L2) + jmp?(L3) + jmp(L4) USE: in: {} out: {} L2 [onException]: 3 v(e: Exception) INIT: in: {} out: {e=D} magic[FAKE_INITIALIZER](e: Exception) -> INIT: in: {e=D} out: {e=D} @@ -59,4 +61,4 @@ error: sink: USE: in: {} out: {} -===================== \ No newline at end of file +===================== diff --git a/compiler/testData/cfg/controlStructures/Finally.instructions b/compiler/testData/cfg/controlStructures/Finally.instructions index 7215a5671ae..f6c750be2df 100644 --- a/compiler/testData/cfg/controlStructures/Finally.instructions +++ b/compiler/testData/cfg/controlStructures/Finally.instructions @@ -14,10 +14,11 @@ L0: jmp?(L2) NEXT:[mark({ 2 }), mark({ 1 })] 3 mark({ 1 }) r(1) -> - 2 jmp(L3) NEXT:[mark({ 2 })] + 2 jmp?(L2) NEXT:[mark({ 2 }), jmp(L3)] + jmp(L3) NEXT:[mark({ 2 })] L2 [onExceptionToFinallyBlock]: L4 [start finally]: - 3 mark({ 2 }) PREV:[jmp?(L2)] + 3 mark({ 2 }) PREV:[jmp?(L2), jmp?(L2)] r(2) -> L5 [finish finally]: 2 jmp(error) NEXT:[] @@ -59,6 +60,7 @@ L0: call(2 > 3, compareTo|, ) -> jf(L3|) NEXT:[read (Unit), mark({ return })] 4 mark({ return }) + jmp?(L2) NEXT:[mark({ 2 }), mark({ 2 })] L4 [start finally]: 5 mark({ 2 }) r(2) -> @@ -69,9 +71,10 @@ L3 [else branch]: read (Unit) PREV:[jf(L3|)] L6 ['if' expression result]: merge(if (2 > 3) { return }|!) -> - 2 jmp(L7) NEXT:[mark({ 2 })] + 2 jmp?(L2) NEXT:[mark({ 2 }), jmp(L7)] + jmp(L7) NEXT:[mark({ 2 })] L2 [onExceptionToFinallyBlock]: - 5 mark({ 2 }) PREV:[jmp?(L2)] + 5 mark({ 2 }) PREV:[jmp?(L2), jmp?(L2), jmp?(L2)] r(2) -> 2 jmp(error) NEXT:[] L7 [skipFinallyToErrorBlock]: @@ -112,10 +115,11 @@ L0: d({ -> if (2 > 3) { return@l } }) NEXT:[] L3 [after local declaration]: r({ -> if (2 > 3) { return@l } }) -> PREV:[jmp?(L3)] - 2 jmp(L8) NEXT:[mark({ 2 })] + 2 jmp?(L2) NEXT:[mark({ 2 }), jmp(L8)] + jmp(L8) NEXT:[mark({ 2 })] L2 [onExceptionToFinallyBlock]: L9 [start finally]: - 3 mark({ 2 }) PREV:[jmp?(L2)] + 3 mark({ 2 }) PREV:[jmp?(L2), jmp?(L2)] r(2) -> L10 [finish finally]: 2 jmp(error) NEXT:[] @@ -217,6 +221,7 @@ L3: call(2 > 3, compareTo|, ) -> jf(L6|) NEXT:[read (Unit), mark({ return@l })] 6 mark({ return@l }) + jmp?(L5) NEXT:[mark({ 2 }), mark({ 2 })] L7 [start finally]: 7 mark({ 2 }) r(2) -> @@ -227,9 +232,10 @@ L6 [else branch]: read (Unit) PREV:[jf(L6|)] L9 ['if' expression result]: merge(if (2 > 3) { return@l }|!) -> - 4 jmp(L10) NEXT:[mark({ 2 })] + 4 jmp?(L5) NEXT:[mark({ 2 }), jmp(L10)] + jmp(L10) NEXT:[mark({ 2 })] L5 [onExceptionToFinallyBlock]: - 7 mark({ 2 }) PREV:[jmp?(L5)] + 7 mark({ 2 }) PREV:[jmp?(L5), jmp?(L5), jmp?(L5)] r(2) -> 4 jmp(error) NEXT:[] L10 [skipFinallyToErrorBlock]: @@ -279,6 +285,7 @@ L4 [body entry point]: call(2 > 3, compareTo|, ) -> jf(L8|) NEXT:[read (Unit), mark({ break@l })] 5 mark({ break@l }) + jmp?(L7) NEXT:[mark({ 2 }), mark({ 2 })] L9 [start finally]: 6 mark({ 2 }) r(2) -> @@ -289,9 +296,10 @@ L8 [else branch]: read (Unit) PREV:[jf(L8|)] L11 ['if' expression result]: merge(if (2 > 3) { break@l }|!) -> - 3 jmp(L12) NEXT:[mark({ 2 })] + 3 jmp?(L7) NEXT:[mark({ 2 }), jmp(L12)] + jmp(L12) NEXT:[mark({ 2 })] L7 [onExceptionToFinallyBlock]: - 6 mark({ 2 }) PREV:[jmp?(L7)] + 6 mark({ 2 }) PREV:[jmp?(L7), jmp?(L7), jmp?(L7)] r(2) -> 3 jmp(error) NEXT:[] L12 [skipFinallyToErrorBlock]: @@ -357,10 +365,11 @@ L4 [loop exit point]: L6 [body exit point]: read (Unit) PREV:[jmp(L4)] r(5) -> - 2 jmp(L10) NEXT:[mark({ 2 })] + 2 jmp?(L2) NEXT:[mark({ 2 }), jmp(L10)] + jmp(L10) NEXT:[mark({ 2 })] L2 [onExceptionToFinallyBlock]: L11 [start finally]: - 3 mark({ 2 }) PREV:[jmp?(L2)] + 3 mark({ 2 }) PREV:[jmp?(L2), jmp?(L2)] r(2) -> L12 [finish finally]: 2 jmp(error) NEXT:[] @@ -422,10 +431,11 @@ L9 ['if' expression result]: L4 [loop exit point]: L6 [body exit point]: read (Unit) PREV:[jmp(L4)] - 2 jmp(L10) NEXT:[mark({ 2 })] + 2 jmp?(L2) NEXT:[mark({ 2 }), jmp(L10)] + jmp(L10) NEXT:[mark({ 2 })] L2 [onExceptionToFinallyBlock]: L11 [start finally]: - 3 mark({ 2 }) PREV:[jmp?(L2)] + 3 mark({ 2 }) PREV:[jmp?(L2), jmp?(L2)] r(2) -> L12 [finish finally]: 2 jmp(error) NEXT:[] @@ -486,6 +496,7 @@ L4 [body entry point]: 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) -> @@ -496,9 +507,10 @@ L8 [else branch]: read (Unit) PREV:[jf(L8|)] L11 ['if' expression result]: merge(if (2 > 3) { continue@l }|!) -> - 4 jmp(L12) NEXT:[mark({ 2 })] + 4 jmp?(L7) NEXT:[mark({ 2 }), jmp(L12)] + jmp(L12) NEXT:[mark({ 2 })] L7 [onExceptionToFinallyBlock]: - 7 mark({ 2 }) PREV:[jmp?(L7)] + 7 mark({ 2 }) PREV:[jmp?(L7), jmp?(L7), jmp?(L7)] r(2) -> 4 jmp(error) NEXT:[] L12 [skipFinallyToErrorBlock]: @@ -573,10 +585,11 @@ L4 [loop exit point]: L6 [body exit point]: read (Unit) PREV:[jmp?(L4)] 3 r(5) -> - 2 jmp(L10) NEXT:[mark({ 2 })] + 2 jmp?(L2) NEXT:[mark({ 2 }), jmp(L10)] + jmp(L10) NEXT:[mark({ 2 })] L2 [onExceptionToFinallyBlock]: L11 [start finally]: - 3 mark({ 2 }) PREV:[jmp?(L2)] + 3 mark({ 2 }) PREV:[jmp?(L2), jmp?(L2)] r(2) -> L12 [finish finally]: 2 jmp(error) NEXT:[] @@ -647,10 +660,11 @@ L9 ['if' expression result]: L4 [loop exit point]: L6 [body exit point]: read (Unit) PREV:[jmp?(L4)] - 2 jmp(L10) NEXT:[mark({ 2 })] + 2 jmp?(L2) NEXT:[mark({ 2 }), jmp(L10)] + jmp(L10) NEXT:[mark({ 2 })] L2 [onExceptionToFinallyBlock]: L11 [start finally]: - 3 mark({ 2 }) PREV:[jmp?(L2)] + 3 mark({ 2 }) PREV:[jmp?(L2), jmp?(L2)] r(2) -> L12 [finish finally]: 2 jmp(error) NEXT:[] @@ -682,6 +696,7 @@ L0: mark(try { return 1 } finally { return 2 }) jmp?(L2) NEXT:[mark({ return 2 }), mark({ return 1 })] 3 mark({ return 1 }) + jmp?(L2) NEXT:[mark({ return 2 }), r(1) -> ] r(1) -> L3 [start finally]: 4 mark({ return 2 }) @@ -689,9 +704,10 @@ L3 [start finally]: ret(*|) L1 NEXT:[] L4 [finish finally]: - 3 ret(*|) L1 NEXT:[] PREV:[] -- 2 jmp(L5) NEXT:[mark({ return 2 })] PREV:[] +- 2 jmp?(L2) NEXT:[mark({ return 2 }), jmp(L5)] PREV:[] +- jmp(L5) NEXT:[mark({ return 2 })] PREV:[] L2 [onExceptionToFinallyBlock]: - 4 mark({ return 2 }) PREV:[jmp?(L2)] + 4 mark({ return 2 }) PREV:[jmp?(L2), jmp?(L2)] r(2) -> ret(*|) L1 NEXT:[] - 2 jmp(error) NEXT:[] PREV:[] @@ -723,6 +739,7 @@ L0: mark(try { return 1 } finally { doSmth(3) }) jmp?(L2) NEXT:[mark({ doSmth(3) }), mark({ return 1 })] 3 mark({ return 1 }) + jmp?(L2) NEXT:[mark({ doSmth(3) }), r(1) -> ] r(1) -> L3 [start finally]: 4 mark({ doSmth(3) }) @@ -731,9 +748,10 @@ L3 [start finally]: call(doSmth(3), doSmth|) -> L4 [finish finally]: 3 ret(*|) L1 NEXT:[] -- 2 jmp(L5) NEXT:[mark({ doSmth(3) })] PREV:[] +- 2 jmp?(L2) NEXT:[mark({ doSmth(3) }), jmp(L5)] PREV:[] +- jmp(L5) NEXT:[mark({ doSmth(3) })] PREV:[] L2 [onExceptionToFinallyBlock]: - 4 mark({ doSmth(3) }) PREV:[jmp?(L2)] + 4 mark({ doSmth(3) }) PREV:[jmp?(L2), jmp?(L2)] r(3) -> mark(doSmth(3)) call(doSmth(3), doSmth|) -> @@ -771,6 +789,8 @@ L0: jmp?(L2) NEXT:[v(e: UnsupportedOperationException), jmp?(L3)] jmp?(L3) NEXT:[mark({ doSmth(3) }), mark({ return 1 })] 3 mark({ return 1 }) + jmp?(L2) NEXT:[v(e: UnsupportedOperationException), jmp?(L3)] + jmp?(L3) NEXT:[mark({ doSmth(3) }), r(1) -> ] r(1) -> L4 [start finally]: 4 mark({ doSmth(3) }) @@ -779,9 +799,11 @@ L4 [start finally]: call(doSmth(3), doSmth|) -> L5 [finish finally]: 3 ret(*|) L1 NEXT:[] -- 2 jmp(L6) NEXT:[jmp(L7)] PREV:[] +- 2 jmp?(L2) NEXT:[v(e: UnsupportedOperationException), jmp?(L3)] PREV:[] +- jmp?(L3) NEXT:[mark({ doSmth(3) }), jmp(L6)] PREV:[] +- jmp(L6) NEXT:[jmp(L7)] PREV:[] L2 [onException]: - 3 v(e: UnsupportedOperationException) PREV:[jmp?(L2)] + 3 v(e: UnsupportedOperationException) PREV:[jmp?(L2), jmp?(L2)] magic[FAKE_INITIALIZER](e: UnsupportedOperationException) -> w(e|) 4 mark({ doSmth(2) }) @@ -792,7 +814,7 @@ L2 [onException]: L6 [afterCatches]: 2 jmp(L7) NEXT:[mark({ doSmth(3) })] L3 [onExceptionToFinallyBlock]: - 4 mark({ doSmth(3) }) PREV:[jmp?(L3)] + 4 mark({ doSmth(3) }) PREV:[jmp?(L3), jmp?(L3)] r(3) -> mark(doSmth(3)) call(doSmth(3), doSmth|) -> @@ -826,11 +848,13 @@ L0: mark(try { return 1 } catch (e: UnsupportedOperationException) { doSmth(2) }) jmp?(L2) NEXT:[v(e: UnsupportedOperationException), mark({ return 1 })] 3 mark({ return 1 }) + jmp?(L2) NEXT:[v(e: UnsupportedOperationException), r(1) -> ] r(1) -> ret(*|) L1 NEXT:[] -- 2 jmp(L3) NEXT:[merge(try { return 1 } catch (e: UnsupportedOperationException) { doSmth(2) }|!, ) -> ] PREV:[] +- 2 jmp?(L2) NEXT:[v(e: UnsupportedOperationException), jmp(L3)] PREV:[] +- jmp(L3) NEXT:[merge(try { return 1 } catch (e: UnsupportedOperationException) { doSmth(2) }|!, ) -> ] PREV:[] L2 [onException]: - 3 v(e: UnsupportedOperationException) PREV:[jmp?(L2)] + 3 v(e: UnsupportedOperationException) PREV:[jmp?(L2), jmp?(L2)] magic[FAKE_INITIALIZER](e: UnsupportedOperationException) -> w(e|) 4 mark({ doSmth(2) }) @@ -867,6 +891,8 @@ L0: jmp?(L2) NEXT:[v(e: UnsupportedOperationException), jmp?(L3)] jmp?(L3) NEXT:[mark({ doSmth(3) }), mark({ return 1 })] 3 mark({ return 1 }) + jmp?(L2) NEXT:[v(e: UnsupportedOperationException), jmp?(L3)] + jmp?(L3) NEXT:[mark({ doSmth(3) }), r(1) -> ] r(1) -> L4 [start finally]: 4 mark({ doSmth(3) }) @@ -875,9 +901,11 @@ L4 [start finally]: call(doSmth(3), doSmth|) -> L5 [finish finally]: 3 ret(*|) L1 NEXT:[] -- 2 jmp(L6) NEXT:[jmp(L7)] PREV:[] +- 2 jmp?(L2) NEXT:[v(e: UnsupportedOperationException), jmp?(L3)] PREV:[] +- jmp?(L3) NEXT:[mark({ doSmth(3) }), jmp(L6)] PREV:[] +- jmp(L6) NEXT:[jmp(L7)] PREV:[] L2 [onException]: - 3 v(e: UnsupportedOperationException) PREV:[jmp?(L2)] + 3 v(e: UnsupportedOperationException) PREV:[jmp?(L2), jmp?(L2)] magic[FAKE_INITIALIZER](e: UnsupportedOperationException) -> w(e|) 4 mark({ return 2 }) @@ -891,7 +919,7 @@ L2 [onException]: L6 [afterCatches]: - 2 jmp(L7) NEXT:[mark({ doSmth(3) })] PREV:[] L3 [onExceptionToFinallyBlock]: - 4 mark({ doSmth(3) }) PREV:[jmp?(L3)] + 4 mark({ doSmth(3) }) PREV:[jmp?(L3), jmp?(L3)] r(3) -> mark(doSmth(3)) call(doSmth(3), doSmth|) -> @@ -932,9 +960,11 @@ L0: r(1) -> mark(doSmth(1)) call(doSmth(1), doSmth|) -> - 2 jmp(L4) NEXT:[jmp(L7)] + 2 jmp?(L2) NEXT:[v(e: UnsupportedOperationException), jmp?(L3)] + jmp?(L3) NEXT:[mark({ doSmth(3) }), jmp(L4)] + jmp(L4) NEXT:[jmp(L7)] L2 [onException]: - 3 v(e: UnsupportedOperationException) PREV:[jmp?(L2)] + 3 v(e: UnsupportedOperationException) PREV:[jmp?(L2), jmp?(L2)] magic[FAKE_INITIALIZER](e: UnsupportedOperationException) -> w(e|) 4 mark({ return 2 }) @@ -950,7 +980,7 @@ L6 [finish finally]: L4 [afterCatches]: 2 jmp(L7) NEXT:[mark({ doSmth(3) })] PREV:[jmp(L4)] L3 [onExceptionToFinallyBlock]: - 5 mark({ doSmth(3) }) PREV:[jmp?(L3)] + 5 mark({ doSmth(3) }) PREV:[jmp?(L3), jmp?(L3)] r(3) -> mark(doSmth(3)) call(doSmth(3), doSmth|) -> diff --git a/compiler/testData/cfg/controlStructures/FinallyTestCopy.instructions b/compiler/testData/cfg/controlStructures/FinallyTestCopy.instructions index 0bc3f40d5a5..f56fd13ac11 100644 --- a/compiler/testData/cfg/controlStructures/FinallyTestCopy.instructions +++ b/compiler/testData/cfg/controlStructures/FinallyTestCopy.instructions @@ -79,9 +79,11 @@ L0: 3 mark({ doSmth() }) mark(doSmth()) call(doSmth(), doSmth) -> - 2 jmp(L4) NEXT:[jmp(L6)] + 2 jmp?(L2) NEXT:[jmp?(L5), jmp?(L3)] + jmp?(L3) NEXT:[mark({ return 1 }), jmp(L4)] + jmp(L4) NEXT:[jmp(L6)] L2 [onException]: - jmp?(L5) NEXT:[v(e: Exception), v(e: NullPointerException)] PREV:[jmp?(L2)] + jmp?(L5) NEXT:[v(e: Exception), v(e: NullPointerException)] PREV:[jmp?(L2), jmp?(L2)] 3 v(e: NullPointerException) magic[FAKE_INITIALIZER](e: NullPointerException) -> w(e|) @@ -101,7 +103,7 @@ L4 [afterCatches]: 2 jmp(L6) NEXT:[mark({ return 1 })] PREV:[jmp(L4), jmp(L4), jmp(L4)] L3 [onExceptionToFinallyBlock]: L7 [start finally]: - 3 mark({ return 1 }) PREV:[jmp?(L3)] + 3 mark({ return 1 }) PREV:[jmp?(L3), jmp?(L3)] r(1) -> ret(*|) L1 NEXT:[] L8 [finish finally]: @@ -155,9 +157,11 @@ L4 [body entry point]: 4 mark({ doSmth() }) mark(doSmth()) call(doSmth(), doSmth) -> - 3 jmp(L9) NEXT:[jmp(L11)] + 3 jmp?(L7) NEXT:[jmp?(L10), jmp?(L8)] + jmp?(L8) NEXT:[mark({ if (cond()) return else continue }), jmp(L9)] + jmp(L9) NEXT:[jmp(L11)] L7 [onException]: - jmp?(L10) NEXT:[v(e: Exception), v(e: NullPointerException)] PREV:[jmp?(L7)] + jmp?(L10) NEXT:[v(e: Exception), v(e: NullPointerException)] PREV:[jmp?(L7), jmp?(L7)] 4 v(e: NullPointerException) magic[FAKE_INITIALIZER](e: NullPointerException) -> w(e|) @@ -177,7 +181,7 @@ L9 [afterCatches]: 3 jmp(L11) NEXT:[mark({ if (cond()) return else continue })] PREV:[jmp(L9), jmp(L9), jmp(L9)] L8 [onExceptionToFinallyBlock]: L12 [start finally]: - 4 mark({ if (cond()) return else continue }) PREV:[jmp?(L8)] + 4 mark({ if (cond()) return else continue }) PREV:[jmp?(L8), jmp?(L8)] mark(if (cond()) return else continue) mark(cond()) call(cond(), cond) -> @@ -240,9 +244,11 @@ L0: 3 mark({ doSmth() }) mark(doSmth()) call(doSmth(), doSmth) -> - 2 jmp(L4) NEXT:[jmp(L6)] + 2 jmp?(L2) NEXT:[jmp?(L5), jmp?(L3)] + jmp?(L3) NEXT:[mark({ while (cond()); }), jmp(L4)] + jmp(L4) NEXT:[jmp(L6)] L2 [onException]: - jmp?(L5) NEXT:[v(e: Exception), v(e: NullPointerException)] PREV:[jmp?(L2)] + jmp?(L5) NEXT:[v(e: Exception), v(e: NullPointerException)] PREV:[jmp?(L2), jmp?(L2)] 3 v(e: NullPointerException) magic[FAKE_INITIALIZER](e: NullPointerException) -> w(e|) @@ -262,7 +268,7 @@ L4 [afterCatches]: 2 jmp(L6) NEXT:[mark({ while (cond()); })] PREV:[jmp(L4), jmp(L4), jmp(L4)] L3 [onExceptionToFinallyBlock]: L7 [start finally]: - 3 mark({ while (cond()); }) PREV:[jmp?(L3)] + 3 mark({ while (cond()); }) PREV:[jmp?(L3), jmp?(L3)] L8 [loop entry point]: L12 [condition entry point]: mark(cond()) PREV:[mark({ while (cond()); }), jmp(L8)] @@ -320,10 +326,11 @@ L0: 3 mark({ doSmth() }) mark(doSmth()) call(doSmth(), doSmth) -> - 2 jmp(L3) NEXT:[mark({ if(list != null) { } })] + 2 jmp?(L2) NEXT:[mark({ if(list != null) { } }), jmp(L3)] + jmp(L3) NEXT:[mark({ if(list != null) { } })] L2 [onExceptionToFinallyBlock]: L4 [start finally]: - 3 mark({ if(list != null) { } }) PREV:[jmp?(L2)] + 3 mark({ if(list != null) { } }) PREV:[jmp?(L2), jmp?(L2)] mark(if(list != null) { }) r(list) -> r(null) -> diff --git a/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.instructions b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.instructions index 8d176fbc4bf..e73ed280177 100644 --- a/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.instructions +++ b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.instructions @@ -33,6 +33,7 @@ L12 [condition entry point]: magic[VALUE_CONSUMER](true|) -> L10 [body entry point]: 5 mark({ continue@outer }) + jmp?(L7) NEXT:[mark({ break }), mark({ break })] L13 [start finally]: 6 mark({ break }) jmp(L3) NEXT:[read (Unit)] @@ -42,9 +43,10 @@ L14 [finish finally]: L9 [loop exit point]: L11 [body exit point]: - read (Unit) PREV:[] -- 3 jmp(L15) NEXT:[mark({ break })] PREV:[] +- 3 jmp?(L7) NEXT:[mark({ break }), jmp(L15)] PREV:[] +- jmp(L15) NEXT:[mark({ break })] PREV:[] L7 [onExceptionToFinallyBlock]: - 6 mark({ break }) PREV:[jmp?(L7)] + 6 mark({ break }) PREV:[jmp?(L7), jmp?(L7)] jmp(L3) NEXT:[read (Unit)] - 3 jmp(error) NEXT:[] PREV:[] L15 [skipFinallyToErrorBlock]: @@ -100,6 +102,7 @@ L12 [condition entry point]: magic[VALUE_CONSUMER](true|) -> L10 [body entry point]: 5 mark({ continue@outer }) + jmp?(L7) NEXT:[mark({ return "OK" }), mark({ return "OK" })] L13 [start finally]: 6 mark({ return "OK" }) mark("OK") @@ -111,9 +114,10 @@ L14 [finish finally]: L9 [loop exit point]: L11 [body exit point]: - read (Unit) PREV:[] -- 3 jmp(L15) NEXT:[mark({ return "OK" })] PREV:[] +- 3 jmp?(L7) NEXT:[mark({ return "OK" }), jmp(L15)] PREV:[] +- jmp(L15) NEXT:[mark({ return "OK" })] PREV:[] L7 [onExceptionToFinallyBlock]: - 6 mark({ return "OK" }) PREV:[jmp?(L7)] + 6 mark({ return "OK" }) PREV:[jmp?(L7), jmp?(L7)] mark("OK") r("OK") -> ret(*|) L1 NEXT:[] @@ -135,3 +139,71 @@ error: sink: PREV:[, ] ===================== +== baz == +fun baz(): String { + outer@while (true) { + try { + inner@while (true) { + continue@inner + } + } finally { + return "OK" + } + } +} +--------------------- +L0: + 1 + 2 mark({ outer@while (true) { try { inner@while (true) { continue@inner } } finally { return "OK" } } }) + mark(outer@while (true) { try { inner@while (true) { continue@inner } } finally { return "OK" } }) +L2 [loop entry point]: +L6 [condition entry point]: + r(true) -> + mark(while (true) { try { inner@while (true) { continue@inner } } finally { return "OK" } }) + magic[VALUE_CONSUMER](true|) -> +L4 [body entry point]: + 3 mark({ try { inner@while (true) { continue@inner } } finally { return "OK" } }) + mark(try { inner@while (true) { continue@inner } } finally { return "OK" }) + jmp?(L7) NEXT:[mark({ return "OK" }), mark({ inner@while (true) { continue@inner } })] + 4 mark({ inner@while (true) { continue@inner } }) + mark(inner@while (true) { continue@inner }) +L8 [loop entry point]: +L12 [condition entry point]: + r(true) -> PREV:[mark(inner@while (true) { continue@inner }), jmp(L12)] + mark(while (true) { continue@inner }) + magic[VALUE_CONSUMER](true|) -> +L10 [body entry point]: + 5 mark({ continue@inner }) + jmp(L12) NEXT:[r(true) -> ] +- 4 jmp(L8) NEXT:[r(true) -> ] PREV:[] +L9 [loop exit point]: +L11 [body exit point]: +- read (Unit) PREV:[] +- 3 jmp?(L7) NEXT:[mark({ return "OK" }), jmp(L13)] PREV:[] +- jmp(L13) NEXT:[mark({ return "OK" })] PREV:[] +L7 [onExceptionToFinallyBlock]: +L14 [start finally]: + 4 mark({ return "OK" }) PREV:[jmp?(L7)] + mark("OK") + r("OK") -> + ret(*|) L1 NEXT:[] +L15 [finish finally]: +- 3 jmp(error) NEXT:[] PREV:[] +L13 [skipFinallyToErrorBlock]: +L16 [copy of L7, onExceptionToFinallyBlock]: +- 4 mark({ return "OK" }) PREV:[] +- mark("OK") PREV:[] +- r("OK") -> PREV:[] +- ret(*|) L1 NEXT:[] PREV:[] +- 3 merge(try { inner@while (true) { continue@inner } } finally { return "OK" }|!) -> PREV:[] +- 2 jmp(L2) NEXT:[r(true) -> ] PREV:[] +L3 [loop exit point]: +L5 [body exit point]: +- read (Unit) PREV:[] +L1: + 1 NEXT:[] PREV:[ret(*|) L1] +error: + PREV:[] +sink: + PREV:[, ] +===================== diff --git a/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.kt b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.kt index a74455e8f34..6f73eda21cf 100644 --- a/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.kt +++ b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.kt @@ -22,3 +22,15 @@ fun bar(): String { } } } + +fun baz(): String { + outer@while (true) { + try { + inner@while (true) { + continue@inner + } + } finally { + return "OK" + } + } +} diff --git a/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.values b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.values index 91f1325a73c..2a2a66dbc0e 100644 --- a/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.values +++ b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.values @@ -61,3 +61,34 @@ while (true) { try { while (true) { continue@outer } } finally { return "OK" } } outer@while (true) { try { while (true) { continue@outer } } finally { return "OK" } } !: * COPY { outer@while (true) { try { while (true) { continue@outer } } finally { return "OK" } } } !: * COPY ===================== +== baz == +fun baz(): String { + outer@while (true) { + try { + inner@while (true) { + continue@inner + } + } finally { + return "OK" + } + } +} +--------------------- + : * NEW: magic[VALUE_CONSUMER](true|) -> + : * NEW: magic[VALUE_CONSUMER](true|) -> +true : Boolean NEW: r(true) -> +true : Boolean NEW: r(true) -> +continue@inner !: * +{ continue@inner } !: * COPY +while (true) { continue@inner } !: * +inner@while (true) { continue@inner } !: * COPY +{ inner@while (true) { continue@inner } } !: * COPY +"OK" : String NEW: r("OK") -> +return "OK" !: * +{ return "OK" } !: * COPY +try { inner@while (true) { continue@inner } } finally { return "OK" } : * NEW: merge(try { inner@while (true) { continue@inner } } finally { return "OK" }|!) -> +{ try { inner@while (true) { continue@inner } } finally { return "OK" } } : * COPY +while (true) { try { inner@while (true) { continue@inner } } finally { return "OK" } } !: * +outer@while (true) { try { inner@while (true) { continue@inner } } finally { return "OK" } } !: * COPY +{ outer@while (true) { try { inner@while (true) { continue@inner } } finally { return "OK" } } } !: * COPY +===================== diff --git a/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.instructions b/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.instructions index 3ad142a6297..22e5f2052c5 100644 --- a/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.instructions +++ b/compiler/testData/cfg/controlStructures/localAndNonlocalReturnsWithFinally.instructions @@ -58,7 +58,8 @@ L0: r(0) -> mark(n < 0) call(n < 0, compareTo|, ) -> - jf(L3|) NEXT:[read (Unit), r(0) -> ] + jf(L3|) NEXT:[read (Unit), jmp?(L2)] + jmp?(L2) NEXT:[mark({ for (i in 1..2) { } return bar() }), r(0) -> ] r(0) -> L4 [start finally]: 4 mark({ for (i in 1..2) { } return bar() }) @@ -99,9 +100,10 @@ L12 [after local declaration]: r({ return it }) -> PREV:[jmp?(L12)] mark(let { return it }) call(let { return it }, let|, ) -> - 2 jmp(L20) NEXT:[mark({ for (i in 1..2) { } return bar() })] + 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]: - 4 mark({ for (i in 1..2) { } return bar() }) PREV:[jmp?(L2)] + 4 mark({ for (i in 1..2) { } return bar() }) PREV:[jmp?(L2), jmp?(L2), jmp?(L2)] 5 r(1) -> r(2) -> mark(1..2) diff --git a/compiler/testData/cfg/controlStructures/localFunctionInFinally.instructions b/compiler/testData/cfg/controlStructures/localFunctionInFinally.instructions index 7a60ed97322..0558cae3ed9 100644 --- a/compiler/testData/cfg/controlStructures/localFunctionInFinally.instructions +++ b/compiler/testData/cfg/controlStructures/localFunctionInFinally.instructions @@ -21,10 +21,11 @@ L0: 3 mark({ i = 1 }) r(1) -> w(i|) - 2 jmp(L3) NEXT:[mark({ fun bar() {} return i })] + 2 jmp?(L2) NEXT:[mark({ fun bar() {} return i }), jmp(L3)] + jmp(L3) NEXT:[mark({ fun bar() {} return i })] L2 [onExceptionToFinallyBlock]: L4 [start finally]: - 3 mark({ fun bar() {} return i }) PREV:[jmp?(L2)] + 3 mark({ fun bar() {} return i }) PREV:[jmp?(L2), jmp?(L2)] jmp?(L5) NEXT:[r(i) -> , d(fun bar() {})] d(fun bar() {}) NEXT:[] L5 [after local declaration]: diff --git a/compiler/testData/cfg/tailCalls/finally.instructions b/compiler/testData/cfg/tailCalls/finally.instructions index 0300ad56801..33f73be7721 100644 --- a/compiler/testData/cfg/tailCalls/finally.instructions +++ b/compiler/testData/cfg/tailCalls/finally.instructions @@ -14,10 +14,11 @@ L0: jmp?(L2) NEXT:[mark({ test() }), mark({ // do nothing })] 3 mark({ // do nothing }) read (Unit) - 2 jmp(L3) NEXT:[mark({ test() })] + 2 jmp?(L2) NEXT:[mark({ test() }), jmp(L3)] + jmp(L3) NEXT:[mark({ test() })] L2 [onExceptionToFinallyBlock]: L4 [start finally]: - 3 mark({ test() }) PREV:[jmp?(L2)] + 3 mark({ test() }) PREV:[jmp?(L2), jmp?(L2)] mark(test()) call(test(), test) -> L5 [finish finally]: diff --git a/compiler/testData/cfg/tailCalls/finallyWithReturn.instructions b/compiler/testData/cfg/tailCalls/finallyWithReturn.instructions index 069a6a2ab55..c9faa0dc1d0 100644 --- a/compiler/testData/cfg/tailCalls/finallyWithReturn.instructions +++ b/compiler/testData/cfg/tailCalls/finallyWithReturn.instructions @@ -14,10 +14,11 @@ L0: jmp?(L2) NEXT:[mark({ return test() }), mark({ // do nothing })] 3 mark({ // do nothing }) read (Unit) - 2 jmp(L3) NEXT:[mark({ return test() })] + 2 jmp?(L2) NEXT:[mark({ return test() }), jmp(L3)] + jmp(L3) NEXT:[mark({ return test() })] L2 [onExceptionToFinallyBlock]: L4 [start finally]: - 3 mark({ return test() }) PREV:[jmp?(L2)] + 3 mark({ return test() }) PREV:[jmp?(L2), jmp?(L2)] mark(test()) call(test(), test) -> ret(*|) L1 NEXT:[] diff --git a/compiler/testData/cfg/tailCalls/try.instructions b/compiler/testData/cfg/tailCalls/try.instructions index e9f47805f08..72f134f1d18 100644 --- a/compiler/testData/cfg/tailCalls/try.instructions +++ b/compiler/testData/cfg/tailCalls/try.instructions @@ -13,12 +13,14 @@ L0: mark(try { return foo() } catch (e: Throwable) { }) jmp?(L2) NEXT:[v(e: Throwable), mark({ return foo() })] 3 mark({ return foo() }) + jmp?(L2) NEXT:[v(e: Throwable), mark(foo())] mark(foo()) call(foo(), foo) -> ret(*|) L1 NEXT:[] -- 2 jmp(L3) NEXT:[merge(try { return foo() } catch (e: Throwable) { }|!, !) -> ] PREV:[] +- 2 jmp?(L2) NEXT:[v(e: Throwable), jmp(L3)] PREV:[] +- jmp(L3) NEXT:[merge(try { return foo() } catch (e: Throwable) { }|!, !) -> ] PREV:[] L2 [onException]: - 3 v(e: Throwable) PREV:[jmp?(L2)] + 3 v(e: Throwable) PREV:[jmp?(L2), jmp?(L2)] magic[FAKE_INITIALIZER](e: Throwable) -> w(e|) 4 mark({ }) @@ -32,4 +34,4 @@ error: PREV:[] sink: PREV:[, ] -===================== \ No newline at end of file +===================== diff --git a/compiler/testData/cfg/tailCalls/tryCatchFinally.instructions b/compiler/testData/cfg/tailCalls/tryCatchFinally.instructions index 35cb888cde1..6fa75ec89d4 100644 --- a/compiler/testData/cfg/tailCalls/tryCatchFinally.instructions +++ b/compiler/testData/cfg/tailCalls/tryCatchFinally.instructions @@ -18,9 +18,11 @@ L0: 3 mark({ test() }) mark(test()) call(test(), test) -> - 2 jmp(L4) NEXT:[jmp(L5)] + 2 jmp?(L2) NEXT:[v(any : Exception), jmp?(L3)] + jmp?(L3) NEXT:[mark({ test() }), jmp(L4)] + jmp(L4) NEXT:[jmp(L5)] L2 [onException]: - 3 v(any : Exception) PREV:[jmp?(L2)] + 3 v(any : Exception) PREV:[jmp?(L2), jmp?(L2)] magic[FAKE_INITIALIZER](any : Exception) -> w(any|) 4 mark({ test() }) @@ -31,7 +33,7 @@ L4 [afterCatches]: 2 jmp(L5) NEXT:[mark({ test() })] PREV:[jmp(L4), jmp(L4)] L3 [onExceptionToFinallyBlock]: L6 [start finally]: - 3 mark({ test() }) PREV:[jmp?(L3)] + 3 mark({ test() }) PREV:[jmp?(L3), jmp?(L3)] mark(test()) call(test(), test) -> L7 [finish finally]: diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.kt new file mode 100644 index 00000000000..ea5d6a6ea4d --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.kt @@ -0,0 +1,102 @@ +// KT-13612 related tests (reassignment in try-catch-finally) + +fun f1() { + val n: Int + try { + n = 1 + throw Exception() + } + catch (e: Exception) { + // KT-13612: reassignment + n = 2 + } + n.hashCode() +} + +fun f2() { + val n: Int + try { + n = 1 + throw Exception() + } + finally { + n = 2 + } + n.hashCode() +} + +fun g1(flag: Boolean) { + val n: Int + try { + if (flag) throw Exception() + n = 1 + } + catch (e: Exception) { + // KT-13612: ? reassignment or definite assignment ? + n = 2 + } + n.hashCode() +} + +fun g2(flag: Boolean) { + val n: Int + try { + if (flag) throw Exception() + n = 1 + } + finally { + n = 2 + } + n.hashCode() +} + +fun h1(flag: Boolean) { + val n = try { + if (flag) throw Exception() + 1 + } + catch (e: Exception) { + 2 + } + n.hashCode() +} + +fun h2(flag: Boolean) { + val n = try { + if (flag) throw Exception() + 1 + } + finally { + 2 + } + n.hashCode() +} + +fun j(flag: Boolean) { + if (flag) throw Exception() +} + +fun k1(flag: Boolean) { + val n: Int + try { + n = 1 + j(flag) + } + catch (e: Exception) { + // KT-13612: reassignment + n = 2 + } + n.hashCode() +} + +fun k2(flag: Boolean) { + val n: Int + try { + n = 1 + j(flag) + } + finally { + n = 2 + } + n.hashCode() +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.txt new file mode 100644 index 00000000000..b7a1374a303 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.txt @@ -0,0 +1,11 @@ +package + +public fun f1(): kotlin.Unit +public fun f2(): kotlin.Unit +public fun g1(/*0*/ flag: kotlin.Boolean): kotlin.Unit +public fun g2(/*0*/ flag: kotlin.Boolean): kotlin.Unit +public fun h1(/*0*/ flag: kotlin.Boolean): kotlin.Unit +public fun h2(/*0*/ flag: kotlin.Boolean): kotlin.Unit +public fun j(/*0*/ flag: kotlin.Boolean): kotlin.Unit +public fun k1(/*0*/ flag: kotlin.Boolean): kotlin.Unit +public fun k2(/*0*/ flag: kotlin.Boolean): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatchWithJumps.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatchWithJumps.kt new file mode 100644 index 00000000000..f65b793e1c3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatchWithJumps.kt @@ -0,0 +1,55 @@ +fun exc(flag: Boolean) { + if (flag) throw Exception() +} + +fun f1(flag: Boolean) { + val n: Int + try { + if (flag) { + n = 1 + exc(flag) + return + } + } + catch (e: Exception) { + // KT-13612: reassignment + n = 3 + } + n.hashCode() +} + +fun f2(flag: Boolean) { + while (true) { + val n: Int + try { + if (flag) { + n = 1 + exc(flag) + break + } + } + catch (e: Exception) { + // KT-13612: reassignment + n = 3 + } + n.hashCode() + } +} + +fun f3(flag: Boolean) { + while (true) { + val n: Int + try { + if (flag) { + n = 1 + exc(flag) + continue + } + } + catch (e: Exception) { + // KT-13612: reassignment + n = 3 + } + n.hashCode() + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatchWithJumps.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatchWithJumps.txt new file mode 100644 index 00000000000..e5c31bc7111 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatchWithJumps.txt @@ -0,0 +1,6 @@ +package + +public fun exc(/*0*/ flag: kotlin.Boolean): kotlin.Unit +public fun f1(/*0*/ flag: kotlin.Boolean): kotlin.Unit +public fun f2(/*0*/ flag: kotlin.Boolean): kotlin.Unit +public fun f3(/*0*/ flag: kotlin.Boolean): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.kt new file mode 100644 index 00000000000..953ca2944d0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.kt @@ -0,0 +1,36 @@ +fun f() { + var foo = 1 + try { + foo = 2 + throw RuntimeException() + } catch (e: Throwable) { + foo.hashCode() + } + throw Exception() +} + +fun g() { + var foo = 1 + try { + foo = 2 + f() + } catch (e: Throwable) { + foo.hashCode() + } +} + +fun h() { + try { + + } + finally { + var foo = 1 + try { + foo = 2 + g() + } + catch (e: Throwable) { + foo.hashCode() + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.txt new file mode 100644 index 00000000000..4b87c06daef --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.txt @@ -0,0 +1,5 @@ +package + +public fun f(): kotlin.Unit +public fun g(): kotlin.Unit +public fun h(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java index b5336fbbcb5..31989a18195 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java @@ -112,6 +112,12 @@ public class DataFlowTestGenerated extends AbstractDataFlowTest { doTest(fileName); } + @TestMetadata("kt5469.kt") + public void testKt5469() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/kt5469.kt"); + doTest(fileName); + } + @TestMetadata("kt9825.kt") public void testKt9825() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/kt9825.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java index a00aa895d44..b9f13708c8f 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java @@ -870,6 +870,12 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest { doTest(fileName); } + @TestMetadata("kt5469.kt") + public void testKt5469() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/kt5469.kt"); + doTest(fileName); + } + @TestMetadata("kt9825.kt") public void testKt9825() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/kt9825.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 5de3b71d61d..035a4d76685 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3606,6 +3606,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("reassignmentInTryCatch.kt") + public void testReassignmentInTryCatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.kt"); + doTest(fileName); + } + + @TestMetadata("reassignmentInTryCatchWithJumps.kt") + public void testReassignmentInTryCatchWithJumps() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatchWithJumps.kt"); + doTest(fileName); + } + @TestMetadata("referenceToPropertyInitializer.kt") public void testReferenceToPropertyInitializer() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/referenceToPropertyInitializer.kt"); @@ -3630,6 +3642,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("tryWithAssignmentUsedInCatch.kt") + public void testTryWithAssignmentUsedInCatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.kt"); + doTest(fileName); + } + @TestMetadata("uninitializedInLocalDeclarations.kt") public void testUninitializedInLocalDeclarations() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedInLocalDeclarations.kt");