diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index de19c7ca107..d264ea29a67 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -734,6 +734,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) { builder.bindLabel(loopInfo.conditionEntryPoint) val condition = expression.condition generateInstructions(condition) + builder.exitBlockScope(expression) if (!CompileTimeConstantUtils.canBeReducedToBooleanConstant(condition, trace.bindingContext, true)) { builder.jumpOnTrue(loopInfo.entryPoint, expression, builder.getBoundValue(expression.condition)) } @@ -744,7 +745,6 @@ class ControlFlowProcessor(private val trace: BindingTrace) { } builder.bindLabel(loopInfo.exitPoint) builder.loadUnit(expression) - builder.exitBlockScope(expression) } override fun visitForExpression(expression: KtForExpression) { diff --git a/compiler/testData/cfg-variables/bugs/doWhileAssignment.instructions b/compiler/testData/cfg-variables/bugs/doWhileAssignment.instructions new file mode 100644 index 00000000000..7024f321518 --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/doWhileAssignment.instructions @@ -0,0 +1,83 @@ +== test == +fun test() { + do { + val s: String + s = "" + } while (s == "") +} +--------------------- +L0: + 1 INIT: in: {} out: {} + 2 mark({ do { val s: String s = "" } while (s == "") }) USE: in: {} out: {} + 3 mark(do { val s: String s = "" } while (s == "")) +L2 [loop entry point]: +L4 [body entry point]: + mark({ val s: String s = "" }) + v(val s: String) INIT: in: {} out: {s=D} + mark("") INIT: in: {s=D} out: {s=D} + r("") -> USE: in: {s=WRITTEN_AFTER_READ} out: {s=WRITTEN_AFTER_READ} + w(s|) INIT: in: {s=D} out: {s=ID} USE: in: {s=READ} out: {s=WRITTEN_AFTER_READ} +L5 [body exit point]: +L6 [condition entry point]: + r(s) -> INIT: in: {s=ID} out: {s=ID} USE: in: {} out: {s=READ} + mark("") + r("") -> + mark(s == "") + call(s == "", equals|, ) -> + 2 jt(L2|) INIT: in: {} out: {} +L3 [loop exit point]: + read (Unit) +L1: + 1 +error: + +sink: + USE: in: {} out: {} +===================== +== test2 == +fun test2() { + while (true) { + val s: String + s = "" + if (s != "") break + } +} +--------------------- +L0: + 1 INIT: in: {} out: {} + 2 mark({ while (true) { val s: String s = "" if (s != "") break } }) +L2 [loop entry point]: +L6 [condition entry point]: + r(true) -> + mark(while (true) { val s: String s = "" if (s != "") break }) + magic[VALUE_CONSUMER](true|) -> USE: in: {} out: {} +L4 [body entry point]: + 3 mark({ val s: String s = "" if (s != "") break }) + v(val s: String) INIT: in: {} out: {s=D} + mark("") INIT: in: {s=D} out: {s=D} + r("") -> USE: in: {s=WRITTEN_AFTER_READ} out: {s=WRITTEN_AFTER_READ} + w(s|) INIT: in: {s=D} out: {s=ID} USE: in: {s=READ} out: {s=WRITTEN_AFTER_READ} + mark(if (s != "") break) INIT: in: {s=ID} out: {s=ID} USE: in: {s=READ} out: {s=READ} + r(s) -> USE: in: {} out: {s=READ} + mark("") + r("") -> + mark(s != "") + call(s != "", equals|, ) -> + jf(L7|) + jmp(L3) USE: in: {} out: {} +- jmp(L8) +L7 [else branch]: + read (Unit) INIT: in: {s=ID} out: {s=ID} +L8 ['if' expression result]: + merge(if (s != "") break|!) -> + 2 jmp(L2) INIT: in: {} out: {} +L3 [loop exit point]: +L5 [body exit point]: + read (Unit) +L1: + 1 +error: + +sink: + USE: in: {} out: {} +===================== diff --git a/compiler/testData/cfg-variables/bugs/doWhileAssignment.kt b/compiler/testData/cfg-variables/bugs/doWhileAssignment.kt new file mode 100644 index 00000000000..60355b11aa2 --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/doWhileAssignment.kt @@ -0,0 +1,16 @@ +// See KT-15334: incorrect reassignment in do...while + +fun test() { + do { + val s: String + s = "" + } while (s == "") +} + +fun test2() { + while (true) { + val s: String + s = "" + if (s != "") break + } +} diff --git a/compiler/testData/cfg-variables/bugs/doWhileAssignment.values b/compiler/testData/cfg-variables/bugs/doWhileAssignment.values new file mode 100644 index 00000000000..bf692ef878c --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/doWhileAssignment.values @@ -0,0 +1,38 @@ +== test == +fun test() { + do { + val s: String + s = "" + } while (s == "") +} +--------------------- +"" : String NEW: r("") -> +s = "" !: * +{ val s: String s = "" } !: * COPY +s : OR{*, *} NEW: r(s) -> +"" : * NEW: r("") -> +s == "" : Boolean NEW: call(s == "", equals|, ) -> +do { val s: String s = "" } while (s == "") !: * +{ do { val s: String s = "" } while (s == "") } !: * COPY +===================== +== test2 == +fun test2() { + while (true) { + val s: String + s = "" + if (s != "") break + } +} +--------------------- + : * NEW: magic[VALUE_CONSUMER](true|) -> +true : Boolean NEW: r(true) -> +"" : String NEW: r("") -> +s : OR{*, *} NEW: r(s) -> +"" : * NEW: r("") -> +s != "" : Boolean NEW: call(s != "", equals|, ) -> +break !: * +if (s != "") break : * NEW: merge(if (s != "") break|!) -> +{ val s: String s = "" if (s != "") break } : * COPY +while (true) { val s: String s = "" if (s != "") break } !: * +{ while (true) { val s: String s = "" if (s != "") break } } !: * COPY +===================== diff --git a/compiler/testData/cfg-variables/bugs/doWhileNotDefined.instructions b/compiler/testData/cfg-variables/bugs/doWhileNotDefined.instructions index a823b77e469..821275eb4c2 100644 --- a/compiler/testData/cfg-variables/bugs/doWhileNotDefined.instructions +++ b/compiler/testData/cfg-variables/bugs/doWhileNotDefined.instructions @@ -11,33 +11,33 @@ L0: v(cond1: Boolean) INIT: in: {} out: {cond1=D} magic[FAKE_INITIALIZER](cond1: Boolean) -> INIT: in: {cond1=D} out: {cond1=D} w(cond1|) INIT: in: {cond1=D} out: {cond1=ID} - 2 mark({ do { if (cond1) continue val cond2 = false } while (cond2) }) INIT: in: {cond1=ID} out: {cond1=ID} USE: in: {cond1=READ} out: {cond1=READ} + 2 mark({ do { if (cond1) continue val cond2 = false } while (cond2) }) INIT: in: {cond1=ID} out: {cond1=ID} USE: in: {cond1=READ} out: {cond1=READ} 3 mark(do { if (cond1) continue val cond2 = false } while (cond2)) L2 [loop entry point]: L4 [body entry point]: - mark({ if (cond1) continue val cond2 = false }) INIT: in: {cond1=ID, cond2=I?} out: {cond1=ID, cond2=I?} + mark({ if (cond1) continue val cond2 = false }) mark(if (cond1) continue) r(cond1) -> jf(L7|) - jmp(L6) USE: in: {cond1=READ, cond2=READ} out: {cond1=READ, cond2=READ} + jmp(L6) USE: in: {cond1=READ, cond2=READ} out: {cond1=READ, cond2=READ} - jmp(L8) L7 [else branch]: - read (Unit) INIT: in: {cond1=ID, cond2=I?} out: {cond1=ID, cond2=I?} + read (Unit) INIT: in: {cond1=ID} out: {cond1=ID} L8 ['if' expression result]: merge(if (cond1) continue|!) -> - v(val cond2 = false) INIT: in: {cond1=ID, cond2=I?} out: {cond1=ID, cond2=I?D} - r(false) -> INIT: in: {cond1=ID, cond2=I?D} out: {cond1=ID, cond2=I?D} - w(cond2|) INIT: in: {cond1=ID, cond2=I?D} out: {cond1=ID, cond2=ID} + v(val cond2 = false) INIT: in: {cond1=ID} out: {cond1=ID, cond2=D} + r(false) -> INIT: in: {cond1=ID, cond2=D} out: {cond1=ID, cond2=D} + w(cond2|) INIT: in: {cond1=ID, cond2=D} out: {cond1=ID, cond2=ID} USE: in: {cond1=READ, cond2=READ} out: {cond1=READ, cond2=READ} L5 [body exit point]: L6 [condition entry point]: - r(cond2) -> INIT: in: {cond1=ID, cond2=I?} out: {cond1=ID, cond2=I?} - jt(L2|) USE: in: {cond1=READ, cond2=READ} out: {cond1=READ, cond2=READ} + r(cond2) -> INIT: in: {cond1=ID, cond2=I?} out: {cond1=ID, cond2=I?} USE: in: {cond1=READ} out: {cond1=READ, cond2=READ} + 2 jt(L2|) INIT: in: {cond1=ID} out: {cond1=ID} USE: in: {cond1=READ} out: {cond1=READ} L3 [loop exit point]: read (Unit) L1: - 1 INIT: in: {cond1=ID} out: {cond1=ID} + 1 error: INIT: in: {} out: {} sink: - INIT: in: {cond1=I?} out: {cond1=I?} USE: in: {} out: {} + INIT: in: {cond1=I?} out: {cond1=I?} USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/lexicalScopes/doWhileScope.instructions b/compiler/testData/cfg-variables/lexicalScopes/doWhileScope.instructions index 41dca936042..8fc9ebbfda1 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/doWhileScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/doWhileScope.instructions @@ -11,29 +11,29 @@ L0: 1 INIT: in: {} out: {} 2 mark({ "before" do { var a = 2 } while (a > 0) "after" }) mark("before") - r("before") -> USE: in: {} out: {} + r("before") -> USE: in: {} out: {} 3 mark(do { var a = 2 } while (a > 0)) L2 [loop entry point]: L4 [body entry point]: - mark({ var a = 2 }) INIT: in: {a=I?} out: {a=I?} - v(var a = 2) INIT: in: {a=I?} out: {a=I?D} - r(2) -> INIT: in: {a=I?D} out: {a=I?D} - w(a|) INIT: in: {a=I?D} out: {a=ID} + mark({ var a = 2 }) + v(var a = 2) INIT: in: {} out: {a=D} + r(2) -> INIT: in: {a=D} out: {a=D} + w(a|) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ} L5 [body exit point]: L6 [condition entry point]: - r(a) -> INIT: in: {a=ID} out: {a=ID} + r(a) -> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ} r(0) -> mark(a > 0) call(a > 0, compareTo|, ) -> - jt(L2|) USE: in: {a=READ} out: {a=READ} + 2 jt(L2|) INIT: in: {} out: {} L3 [loop exit point]: read (Unit) - 2 mark("after") INIT: in: {} out: {} + mark("after") r("after") -> L1: 1 error: sink: - USE: in: {} out: {} + USE: in: {} out: {} ===================== \ No newline at end of file diff --git a/compiler/testData/cfg/controlStructures/InfiniteLoops.instructions b/compiler/testData/cfg/controlStructures/InfiniteLoops.instructions index 899bfa4c7e1..038fdeaab84 100644 --- a/compiler/testData/cfg/controlStructures/InfiniteLoops.instructions +++ b/compiler/testData/cfg/controlStructures/InfiniteLoops.instructions @@ -30,11 +30,11 @@ L4 [body entry point]: L5 [body exit point]: L6 [condition entry point]: r(true) -> - magic[VALUE_CONSUMER](true|) -> + 2 magic[VALUE_CONSUMER](true|) -> jmp(L2) NEXT:[mark({ })] L3 [loop exit point]: - read (Unit) PREV:[] -- 2 mark(unreachable()) PREV:[] +- mark(unreachable()) PREV:[] - call(unreachable(), unreachable) -> PREV:[] L1: 1 NEXT:[] PREV:[] diff --git a/compiler/testData/cfg/controlStructures/OnlyWhileInFunctionBody.instructions b/compiler/testData/cfg/controlStructures/OnlyWhileInFunctionBody.instructions index 6913077c504..04c9d5e4086 100644 --- a/compiler/testData/cfg/controlStructures/OnlyWhileInFunctionBody.instructions +++ b/compiler/testData/cfg/controlStructures/OnlyWhileInFunctionBody.instructions @@ -50,7 +50,7 @@ L6 [condition entry point]: - r(1) -> PREV:[] - mark(0 > 1) PREV:[] - call(0 > 1, compareTo|, ) -> PREV:[] -- jt(L2|) NEXT:[read (Unit), mark({return})] PREV:[] +- 2 jt(L2|) NEXT:[read (Unit), mark({return})] PREV:[] L3 [loop exit point]: - read (Unit) PREV:[] L1: diff --git a/compiler/testData/cfg/controlStructures/continueInDoWhile.instructions b/compiler/testData/cfg/controlStructures/continueInDoWhile.instructions index c6b9b7bf667..9361f91dc93 100644 --- a/compiler/testData/cfg/controlStructures/continueInDoWhile.instructions +++ b/compiler/testData/cfg/controlStructures/continueInDoWhile.instructions @@ -29,7 +29,7 @@ L8 ['if' expression result]: L5 [body exit point]: L6 [condition entry point]: r(true) -> - magic[VALUE_CONSUMER](true|) -> + 2 magic[VALUE_CONSUMER](true|) -> jmp(L2) NEXT:[mark({ if (b) break; continue; })] L3 [loop exit point]: read (Unit) PREV:[jmp(L3)] diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.kt new file mode 100644 index 00000000000..d9c17b30497 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.kt @@ -0,0 +1,23 @@ +// See KT-15334: incorrect reassignment in do...while + +fun test() { + do { + val s: String + s = "" + } while (s == "") +} + +fun test2() { + do { + val s: String + s = "1" + s = s + "2" + } while (s == "1") +} + +fun test3() { + val s: String + do { + s = "" + } while (s != "") +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.txt new file mode 100644 index 00000000000..59ddb710cc8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.txt @@ -0,0 +1,5 @@ +package + +public fun test(): kotlin.Unit +public fun test2(): kotlin.Unit +public fun test3(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java index 7ef03827e75..c2fd4f85086 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java @@ -95,6 +95,12 @@ public class DataFlowTestGenerated extends AbstractDataFlowTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg-variables/bugs"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("doWhileAssignment.kt") + public void testDoWhileAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/doWhileAssignment.kt"); + doTest(fileName); + } + @TestMetadata("doWhileNotDefined.kt") public void testDoWhileNotDefined() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/doWhileNotDefined.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java index b2cb5e7296d..950f1333f66 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java @@ -865,6 +865,12 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg-variables/bugs"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("doWhileAssignment.kt") + public void testDoWhileAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/doWhileAssignment.kt"); + doTest(fileName); + } + @TestMetadata("doWhileNotDefined.kt") public void testDoWhileNotDefined() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/doWhileNotDefined.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index bcbabd87530..f65ee4e3c88 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3409,6 +3409,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("doWhileAssignment.kt") + public void testDoWhileAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.kt"); + doTest(fileName); + } + @TestMetadata("doWhileNotDefined.kt") public void testDoWhileNotDefined() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileNotDefined.kt");