From 436b0ec8731f782b526b15e275e393145299fa19 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 26 Sep 2016 12:51:24 +0300 Subject: [PATCH] CFA merge for definition / initialization : more accurate handling of unknown variables #KT-13969 Fixed (cherry picked from commit 4a96589) --- .../kotlin/cfg/PseudocodeTraverser.kt | 15 ------- .../kotlin/cfg/PseudocodeVariablesData.kt | 10 ++--- .../ExhaustiveInitialization.instructions | 4 +- .../basic/IfWithUninitialized.instructions | 2 +- .../basic/InitializedNotDeclared.instructions | 2 +- .../basic/UsageInFunctionLiteral.instructions | 8 ++-- .../UseUninitializedInLambda.instructions | 8 ++-- .../VariablesInitialization.instructions | 4 +- .../basic/VariablesUsage.instructions | 2 +- .../bugs/doWhileNotDefined.instructions | 43 +++++++++++++++++++ .../cfg-variables/bugs/doWhileNotDefined.kt | 6 +++ .../bugs/doWhileNotDefined.values | 17 ++++++++ .../cfg-variables/bugs/kt10243.instructions | 30 ++++++------- ...eferenceToPropertyInitializer.instructions | 20 ++++----- .../varInitializationInIfInCycle.instructions | 4 +- .../lexicalScopes/doWhileScope.instructions | 6 +-- .../functionLiteralScope.instructions | 6 +-- .../lexicalScopes/localClass.instructions | 4 +- .../localFunctionScope.instructions | 6 +-- ...localFunctionScopeWithoutBody.instructions | 6 +-- .../objectLiteralScope.instructions | 6 +-- .../propertyAccessorScope.instructions | 20 ++++----- .../controlFlowAnalysis/doWhileNotDefined.kt | 6 +++ .../controlFlowAnalysis/doWhileNotDefined.txt | 3 ++ .../diagnostics/tests/enum/localEnums.kt | 2 +- .../kotlin/cfg/DataFlowTestGenerated.java | 6 +++ .../kotlin/cfg/PseudoValueTestGenerated.java | 6 +++ .../checkers/DiagnosticsTestGenerated.java | 6 +++ 28 files changed, 167 insertions(+), 91 deletions(-) create mode 100644 compiler/testData/cfg-variables/bugs/doWhileNotDefined.instructions create mode 100644 compiler/testData/cfg-variables/bugs/doWhileNotDefined.kt create mode 100644 compiler/testData/cfg-variables/bugs/doWhileNotDefined.values create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileNotDefined.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileNotDefined.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeTraverser.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeTraverser.kt index c53e4808421..50176e5efea 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeTraverser.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeTraverser.kt @@ -63,7 +63,6 @@ fun > Pseudocode.collectData( initialInfo: I ): Map> { val edgesMap = LinkedHashMap>() - initializeEdgesMap(edgesMap, initialInfo) edgesMap.put(getStartInstruction(traversalOrder), Edges(initialInfo, initialInfo)) val changed = BooleanArray(1) @@ -77,20 +76,6 @@ fun > Pseudocode.collectData( return edgesMap } -private fun Pseudocode.initializeEdgesMap( - edgesMap: MutableMap>, - initialInfo: I -) { - val instructions = instructions - val initialEdge = Edges(initialInfo, initialInfo) - for (instruction in instructions) { - edgesMap.put(instruction, initialEdge) - if (instruction is LocalFunctionDeclarationInstruction) { - instruction.body.initializeEdgesMap(edgesMap, initialInfo) - } - } -} - private fun > Pseudocode.collectDataFromSubgraph( traversalOrder: TraversalOrder, mergeDataWithLocalDeclarations: Boolean, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt index 5d35b69c0df..51d3cf64d16 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt @@ -221,12 +221,10 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon var initState: InitState? = null var isDeclared = true for (edgeData in incomingEdgesData) { - val varControlFlowState = edgeData[variable] - if (varControlFlowState != null) { - initState = initState?.merge(varControlFlowState.initState) ?: varControlFlowState.initState - if (!varControlFlowState.isDeclared) { - isDeclared = false - } + val varControlFlowState = edgeData[variable] ?: VariableControlFlowState.create(isInitialized = false) + initState = initState?.merge(varControlFlowState.initState) ?: varControlFlowState.initState + if (!varControlFlowState.isDeclared) { + isDeclared = false } } if (initState == null) { diff --git a/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.instructions b/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.instructions index e5a1498f226..b6dac5b140b 100644 --- a/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.instructions +++ b/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.instructions @@ -22,7 +22,7 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {EAST=ID, NORTH=ID, SOUTH=ID, WEST=ID} out: {EAST=ID, NORTH=ID, SOUTH=ID, WEST=ID} USE: in: {} out: {} + INIT: in: {EAST=I?, NORTH=I?, SOUTH=I?, WEST=I?} out: {EAST=I?, NORTH=I?, SOUTH=I?, WEST=I?} USE: in: {} out: {} ===================== == foo == fun foo(dir: Direction): Int { @@ -99,5 +99,5 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {dir=ID} out: {dir=ID} USE: in: {} out: {} + INIT: in: {dir=I?} out: {dir=I?} USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/basic/IfWithUninitialized.instructions b/compiler/testData/cfg-variables/basic/IfWithUninitialized.instructions index 2f1c7309f78..8fef6c3a2bc 100644 --- a/compiler/testData/cfg-variables/basic/IfWithUninitialized.instructions +++ b/compiler/testData/cfg-variables/basic/IfWithUninitialized.instructions @@ -52,5 +52,5 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {} + INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {} ===================== \ No newline at end of file diff --git a/compiler/testData/cfg-variables/basic/InitializedNotDeclared.instructions b/compiler/testData/cfg-variables/basic/InitializedNotDeclared.instructions index db36bf06b60..10aae3a4779 100644 --- a/compiler/testData/cfg-variables/basic/InitializedNotDeclared.instructions +++ b/compiler/testData/cfg-variables/basic/InitializedNotDeclared.instructions @@ -18,5 +18,5 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {x=ID} out: {x=ID} USE: in: {} out: {} + INIT: in: {x=I?} out: {x=I?} USE: in: {} out: {} ===================== \ No newline at end of file diff --git a/compiler/testData/cfg-variables/basic/UsageInFunctionLiteral.instructions b/compiler/testData/cfg-variables/basic/UsageInFunctionLiteral.instructions index 1537bde073e..2ee4bfe79d0 100644 --- a/compiler/testData/cfg-variables/basic/UsageInFunctionLiteral.instructions +++ b/compiler/testData/cfg-variables/basic/UsageInFunctionLiteral.instructions @@ -16,9 +16,9 @@ L0: v(val f = { x: Int -> val y = x + a use(a) }) INIT: in: {a=ID} out: {a=ID, f=D} mark({ x: Int -> val y = x + a use(a) }) INIT: in: {a=ID, f=D} out: {a=ID, f=D} jmp?(L2) - d({ x: Int -> val y = x + a use(a) }) USE: in: {a=READ} out: {a=READ} + d({ x: Int -> val y = x + a use(a) }) INIT: in: {a=I?, f=-} out: {a=I?, f=-} USE: in: {a=READ} out: {a=READ} L2 [after local declaration]: - r({ x: Int -> val y = x + a use(a) }) -> + r({ x: Int -> val y = x + a use(a) }) -> INIT: in: {a=ID, f=D} out: {a=ID, f=D} w(f|) INIT: in: {a=ID, f=D} out: {a=ID, f=ID} L1: 1 INIT: in: {} out: {} @@ -54,7 +54,7 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {a=ID, f=D, x=ID} out: {a=ID, f=D, x=ID} USE: in: {} out: {} + INIT: in: {a=I?, f=-, x=I?} out: {a=I?, f=-, x=I?} USE: in: {} out: {} ===================== == use == fun use(vararg a: Any?) = a @@ -71,5 +71,5 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {} + INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {} ===================== \ No newline at end of file diff --git a/compiler/testData/cfg-variables/basic/UseUninitializedInLambda.instructions b/compiler/testData/cfg-variables/basic/UseUninitializedInLambda.instructions index 5fcde701ea8..9b9e755b8c0 100644 --- a/compiler/testData/cfg-variables/basic/UseUninitializedInLambda.instructions +++ b/compiler/testData/cfg-variables/basic/UseUninitializedInLambda.instructions @@ -15,7 +15,7 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {f=ID} out: {f=ID} USE: in: {} out: {} + INIT: in: {f=I?} out: {f=I?} USE: in: {} out: {} ===================== == foo == fun foo() { @@ -29,9 +29,9 @@ L0: v(var v: Any) INIT: in: {} out: {v=D} mark({ v.hashCode() }) INIT: in: {v=D} out: {v=D} jmp?(L2) - d({ v.hashCode() }) USE: in: {v=READ} out: {v=READ} + d({ v.hashCode() }) INIT: in: {v=-} out: {v=-} USE: in: {v=READ} out: {v=READ} L2 [after local declaration]: - r({ v.hashCode() }) -> + r({ v.hashCode() }) -> INIT: in: {v=D} out: {v=D} mark(bar { v.hashCode() }) call(bar { v.hashCode() }, bar|) -> L1: @@ -56,5 +56,5 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {v=D} out: {v=D} USE: in: {} out: {} + INIT: in: {v=-} out: {v=-} USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/basic/VariablesInitialization.instructions b/compiler/testData/cfg-variables/basic/VariablesInitialization.instructions index 8aa81131cf1..50aea77f14f 100644 --- a/compiler/testData/cfg-variables/basic/VariablesInitialization.instructions +++ b/compiler/testData/cfg-variables/basic/VariablesInitialization.instructions @@ -48,7 +48,7 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {foo=ID} out: {foo=ID} USE: in: {} out: {} + INIT: in: {foo=I?} out: {foo=I?} USE: in: {} out: {} ===================== == Foo == interface Foo { @@ -63,5 +63,5 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {c=D} out: {c=D} USE: in: {} out: {} + INIT: in: {c=-} out: {c=-} USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/basic/VariablesUsage.instructions b/compiler/testData/cfg-variables/basic/VariablesUsage.instructions index a6c1c962f94..87940f1b4cf 100644 --- a/compiler/testData/cfg-variables/basic/VariablesUsage.instructions +++ b/compiler/testData/cfg-variables/basic/VariablesUsage.instructions @@ -61,5 +61,5 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {} + INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {} ===================== \ No newline at end of file diff --git a/compiler/testData/cfg-variables/bugs/doWhileNotDefined.instructions b/compiler/testData/cfg-variables/bugs/doWhileNotDefined.instructions new file mode 100644 index 00000000000..4c237530648 --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/doWhileNotDefined.instructions @@ -0,0 +1,43 @@ +== test == +fun test(cond1: Boolean) { + do { + if (cond1) continue + val cond2 = false + } while (cond2) +} +--------------------- +L0: + 1 INIT: in: {} out: {} + 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} + 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) + r(cond1) -> + jf(L7|) + 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?} +L8 ['if' expression result]: + merge(if (cond1) continue|!) -> + v(val cond2 = false) INIT: in: {cond1=ID, cond2=I?} out: {cond1=ID, cond2=ID} + r(false) -> INIT: in: {cond1=ID, cond2=ID} out: {cond1=ID, cond2=ID} + w(cond2|) +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} +L3 [loop exit point]: + read (Unit) +L1: + 1 INIT: in: {cond1=ID} out: {cond1=ID} +error: + INIT: in: {} out: {} +sink: + INIT: in: {cond1=I?} out: {cond1=I?} USE: in: {} out: {} +===================== diff --git a/compiler/testData/cfg-variables/bugs/doWhileNotDefined.kt b/compiler/testData/cfg-variables/bugs/doWhileNotDefined.kt new file mode 100644 index 00000000000..3588350309a --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/doWhileNotDefined.kt @@ -0,0 +1,6 @@ +fun test(cond1: Boolean) { + do { + if (cond1) continue + val cond2 = false + } while (cond2) +} diff --git a/compiler/testData/cfg-variables/bugs/doWhileNotDefined.values b/compiler/testData/cfg-variables/bugs/doWhileNotDefined.values new file mode 100644 index 00000000000..a1e01c63f1d --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/doWhileNotDefined.values @@ -0,0 +1,17 @@ +== test == +fun test(cond1: Boolean) { + do { + if (cond1) continue + val cond2 = false + } while (cond2) +} +--------------------- + : Boolean NEW: magic[FAKE_INITIALIZER](cond1: Boolean) -> +cond1 : Boolean NEW: r(cond1) -> +continue !: * +if (cond1) continue : * NEW: merge(if (cond1) continue|!) -> +false : Boolean NEW: r(false) -> +cond2 : Boolean NEW: r(cond2) -> +do { if (cond1) continue val cond2 = false } while (cond2) !: * +{ do { if (cond1) continue val cond2 = false } while (cond2) } !: * COPY +===================== diff --git a/compiler/testData/cfg-variables/bugs/kt10243.instructions b/compiler/testData/cfg-variables/bugs/kt10243.instructions index 2e0801cc301..492ae881098 100644 --- a/compiler/testData/cfg-variables/bugs/kt10243.instructions +++ b/compiler/testData/cfg-variables/bugs/kt10243.instructions @@ -9,7 +9,7 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {x=D} out: {x=D} USE: in: {} out: {} + INIT: in: {x=-} out: {x=-} USE: in: {} out: {} ===================== == foo == fun foo(f: Boolean) { @@ -32,46 +32,46 @@ L0: mark(try { if (f) { x = 0 } } finally { fun bar() {} }) jmp?(L2) 3 mark({ if (f) { x = 0 } }) - mark(if (f) { x = 0 }) USE: in: {f=READ, x=ONLY_WRITTEN_NEVER_READ} out: {f=READ, x=ONLY_WRITTEN_NEVER_READ} - r(f) -> USE: in: {x=ONLY_WRITTEN_NEVER_READ} out: {f=READ, x=ONLY_WRITTEN_NEVER_READ} + mark(if (f) { x = 0 }) USE: in: {f=READ, x=ONLY_WRITTEN_NEVER_READ} out: {f=READ, x=ONLY_WRITTEN_NEVER_READ} + r(f) -> USE: in: {x=ONLY_WRITTEN_NEVER_READ} out: {f=READ, x=ONLY_WRITTEN_NEVER_READ} jf(L3|) 4 mark({ x = 0 }) - r(0) -> USE: in: {x=ONLY_WRITTEN_NEVER_READ} out: {x=ONLY_WRITTEN_NEVER_READ} - w(x|) INIT: in: {f=ID} out: {f=ID, x=I} USE: in: {} out: {x=ONLY_WRITTEN_NEVER_READ} + r(0) -> USE: in: {x=ONLY_WRITTEN_NEVER_READ} out: {x=ONLY_WRITTEN_NEVER_READ} + w(x|) INIT: in: {f=ID} out: {f=ID, x=I} USE: in: {} out: {x=ONLY_WRITTEN_NEVER_READ} 3 jmp(L4) INIT: in: {f=ID, x=I} out: {f=ID, x=I} 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} + merge(if (f) { x = 0 }|!) -> INIT: in: {f=ID, x=I?} out: {f=ID, x=I?} 2 jmp?(L2) jmp(L5) L2 [onExceptionToFinallyBlock]: L6 [start finally]: 3 mark({ fun bar() {} }) jmp?(L7) - d(fun bar() {}) + d(fun bar() {}) INIT: in: {f=I?, x=I?} out: {f=I?, x=I?} L7 [after local declaration]: L10 [finish finally]: - 2 jmp(error) + 2 jmp(error) INIT: in: {f=ID, x=I?} out: {f=ID, x=I?} L5 [skipFinallyToErrorBlock]: L11 [copy of L2, onExceptionToFinallyBlock]: 3 mark({ fun bar() {} }) jmp?(L12) - d(fun bar() {}) + d(fun bar() {}) INIT: in: {f=I?, x=I?} out: {f=I?, x=I?} L12 [copy of L7, after local declaration]: - 2 merge(try { if (f) { x = 0 } } finally { fun bar() {} }|) -> + 2 merge(try { if (f) { x = 0 } } finally { fun bar() {} }|) -> INIT: in: {f=ID, x=I?} out: {f=ID, x=I?} L1: 1 error: sink: - USE: in: {} out: {} + INIT: in: {f=I?, x=I?} out: {f=I?, x=I?} USE: in: {} out: {} ===================== == bar == fun bar() {} --------------------- L8: - 4 INIT: in: {f=ID, x=I} out: {f=ID, x=I} + 4 INIT: in: {f=ID, x=I?} out: {f=ID, x=I?} 5 mark({}) read (Unit) L9: @@ -79,13 +79,13 @@ L9: error: INIT: in: {} out: {} sink: - INIT: in: {f=ID, x=I} out: {f=ID, x=I} USE: in: {} out: {} + INIT: in: {f=I?, x=I?} out: {f=I?, x=I?} USE: in: {} out: {} ===================== == bar == fun bar() {} --------------------- L2 [copy of L8, null]: - 4 INIT: in: {f=ID, x=I} out: {f=ID, x=I} + 4 INIT: in: {f=ID, x=I?} out: {f=ID, x=I?} 5 mark({}) read (Unit) L3 [copy of L9, null]: @@ -93,5 +93,5 @@ L3 [copy of L9, null]: L0 [copy of error, null]: INIT: in: {} out: {} L1 [copy of sink, null]: - INIT: in: {f=ID, x=I} out: {f=ID, x=I} USE: in: {} out: {} + INIT: in: {f=I?, x=I?} out: {f=I?, x=I?} USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/bugs/referenceToPropertyInitializer.instructions b/compiler/testData/cfg-variables/bugs/referenceToPropertyInitializer.instructions index ddd4d2e8d87..1638bdaeb41 100644 --- a/compiler/testData/cfg-variables/bugs/referenceToPropertyInitializer.instructions +++ b/compiler/testData/cfg-variables/bugs/referenceToPropertyInitializer.instructions @@ -10,16 +10,16 @@ L0: v(val sum: (Int)->Int = { x: Int -> sum(x - 1) + x }) INIT: in: {} out: {sum=D} mark({ x: Int -> sum(x - 1) + x }) INIT: in: {sum=D} out: {sum=D} jmp?(L2) - d({ x: Int -> sum(x - 1) + x }) USE: in: {sum=READ} out: {sum=READ} + d({ x: Int -> sum(x - 1) + x }) INIT: in: {sum=-} out: {sum=-} USE: in: {sum=READ} out: {sum=READ} L2 [after local declaration]: - r({ x: Int -> sum(x - 1) + x }) -> + r({ x: Int -> sum(x - 1) + x }) -> INIT: in: {sum=D} out: {sum=D} w(sum|) INIT: in: {sum=D} out: {sum=ID} L1: INIT: in: {sum=ID} out: {sum=ID} error: INIT: in: {} out: {} sink: - INIT: in: {sum=I?D} out: {sum=I?D} USE: in: {} out: {} + INIT: in: {sum=I?} out: {sum=I?} USE: in: {} out: {} ===================== == anonymous_0 == { x: Int -> @@ -49,7 +49,7 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {sum=D, x=ID} out: {sum=D, x=ID} USE: in: {} out: {} + INIT: in: {sum=-, x=I?} out: {sum=-, x=I?} USE: in: {} out: {} ===================== == A == open class A(val a: A) @@ -64,7 +64,7 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {} + INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {} ===================== == TestObjectLiteral == class TestObjectLiteral { @@ -92,16 +92,16 @@ L0: r(obj|) -> w(x|) INIT: in: {obj=D, x=D} out: {obj=D, x=ID} 1 jmp?(L2) INIT: in: {obj=D} out: {obj=D} - d(fun foo() { val y = obj }) USE: in: {obj=READ} out: {obj=READ} + d(fun foo() { val y = obj }) INIT: in: {obj=-} out: {obj=-} USE: in: {obj=READ} out: {obj=READ} L2 [after local declaration]: - r(object: A(obj) { init { val x = obj } fun foo() { val y = obj } }) -> + r(object: A(obj) { init { val x = obj } fun foo() { val y = obj } }) -> INIT: in: {obj=D} out: {obj=D} w(obj|) INIT: in: {obj=D} out: {obj=ID} L1: INIT: in: {obj=ID} out: {obj=ID} error: INIT: in: {} out: {} sink: - INIT: in: {obj=I?D} out: {obj=I?D} USE: in: {} out: {} + INIT: in: {obj=I?} out: {obj=I?} USE: in: {} out: {} ===================== == foo == fun foo() { @@ -120,7 +120,7 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {obj=D} out: {obj=D} USE: in: {} out: {} + INIT: in: {obj=-} out: {obj=-} USE: in: {} out: {} ===================== == TestOther == class TestOther { @@ -141,5 +141,5 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {x=ID} out: {x=ID} USE: in: {} out: {} + INIT: in: {x=I?} out: {x=I?} USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.instructions b/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.instructions index 30e173c5232..6872720c12c 100644 --- a/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.instructions +++ b/compiler/testData/cfg-variables/bugs/varInitializationInIfInCycle.instructions @@ -59,7 +59,7 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {numbers=ID} out: {numbers=ID} USE: in: {} out: {} + INIT: in: {numbers=I?} out: {numbers=I?} USE: in: {} out: {} ===================== == use == fun use(vararg a: Any?) = a @@ -76,5 +76,5 @@ L1: error: INIT: in: {} out: {} sink: - INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {} + INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {} ===================== \ No newline at end of file diff --git a/compiler/testData/cfg-variables/lexicalScopes/doWhileScope.instructions b/compiler/testData/cfg-variables/lexicalScopes/doWhileScope.instructions index a44472d92e5..3941bd81b81 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/doWhileScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/doWhileScope.instructions @@ -15,9 +15,9 @@ L0: 3 mark(do { var a = 2 } while (a > 0)) L2 [loop entry point]: L4 [body entry point]: - mark({ var a = 2 }) INIT: in: {a=ID} out: {a=ID} - v(var a = 2) - r(2) -> + mark({ var a = 2 }) INIT: in: {a=I?} out: {a=I?} + v(var a = 2) INIT: in: {a=I?} out: {a=ID} + r(2) -> INIT: in: {a=ID} out: {a=ID} w(a|) L5 [body exit point]: L6 [condition entry point]: diff --git a/compiler/testData/cfg-variables/lexicalScopes/functionLiteralScope.instructions b/compiler/testData/cfg-variables/lexicalScopes/functionLiteralScope.instructions index a122fe549a2..c0ba798e242 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/functionLiteralScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/functionLiteralScope.instructions @@ -19,9 +19,9 @@ L0: v(val f = { x: Int -> val a = x + b }) INIT: in: {b=ID} out: {b=ID, f=D} mark({ x: Int -> val a = x + b }) INIT: in: {b=ID, f=D} out: {b=ID, f=D} jmp?(L2) - d({ x: Int -> val a = x + b }) USE: in: {b=READ} out: {b=READ} + d({ x: Int -> val a = x + b }) INIT: in: {b=I?, f=-} out: {b=I?, f=-} USE: in: {b=READ} out: {b=READ} L2 [after local declaration]: - r({ x: Int -> val a = x + b }) -> + r({ x: Int -> val a = x + b }) -> INIT: in: {b=ID, f=D} out: {b=ID, f=D} w(f|) INIT: in: {b=ID, f=D} out: {b=ID, f=ID} mark("after") INIT: in: {b=ID, f=ID} out: {b=ID, f=ID} r("after") -> @@ -54,5 +54,5 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {b=ID, f=D, x=ID} out: {b=ID, f=D, x=ID} USE: in: {} out: {} + INIT: in: {b=I?, f=-, x=I?} out: {b=I?, f=-, x=I?} USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions b/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions index 7e8aaa742b4..8e6ccec7907 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions @@ -27,7 +27,7 @@ L0: r(x|) -> w(a|) INIT: in: {a=D, x=ID} out: {a=ID, x=ID} 2 jmp?(L3) INIT: in: {x=ID} out: {x=ID} - d(fun foo() { val b = x }) USE: in: {x=READ} out: {x=READ} + d(fun foo() { val b = x }) INIT: in: {x=I?} out: {x=I?} USE: in: {x=READ} out: {x=READ} L2 [after local class]: L3 [after local declaration]: mark("after") @@ -56,5 +56,5 @@ L5: error: INIT: in: {} out: {} sink: - INIT: in: {x=ID} out: {x=ID} USE: in: {} out: {} + INIT: in: {x=I?} out: {x=I?} USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/lexicalScopes/localFunctionScope.instructions b/compiler/testData/cfg-variables/lexicalScopes/localFunctionScope.instructions index 869e81bc0e2..fb71ea9b39a 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/localFunctionScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/localFunctionScope.instructions @@ -17,9 +17,9 @@ L0: r(1) -> INIT: in: {b=D} out: {b=D} w(b|) INIT: in: {b=D} out: {b=ID} jmp?(L2) INIT: in: {b=ID} out: {b=ID} - d(fun local(x: Int) { val a = x + b }) USE: in: {b=READ} out: {b=READ} + d(fun local(x: Int) { val a = x + b }) INIT: in: {b=I?} out: {b=I?} USE: in: {b=READ} out: {b=READ} L2 [after local declaration]: - mark("after") + mark("after") INIT: in: {b=ID} out: {b=ID} r("after") -> L1: 1 INIT: in: {} out: {} @@ -50,5 +50,5 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {b=ID, x=ID} out: {b=ID, x=ID} USE: in: {} out: {} + INIT: in: {b=I?, x=I?} out: {b=I?, x=I?} USE: in: {} out: {} ===================== \ No newline at end of file diff --git a/compiler/testData/cfg-variables/lexicalScopes/localFunctionScopeWithoutBody.instructions b/compiler/testData/cfg-variables/lexicalScopes/localFunctionScopeWithoutBody.instructions index 04119bebf3b..8504e1ff778 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/localFunctionScopeWithoutBody.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/localFunctionScopeWithoutBody.instructions @@ -15,9 +15,9 @@ L0: r(1) -> INIT: in: {b=D} out: {b=D} w(b|) INIT: in: {b=D} out: {b=ID} jmp?(L2) INIT: in: {b=ID} out: {b=ID} - d(fun local(x: Int) = x + b) USE: in: {b=READ} out: {b=READ} + d(fun local(x: Int) = x + b) INIT: in: {b=I?} out: {b=I?} USE: in: {b=READ} out: {b=READ} L2 [after local declaration]: - mark("after") + mark("after") INIT: in: {b=ID} out: {b=ID} r("after") -> L1: 1 INIT: in: {} out: {} @@ -44,5 +44,5 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {b=ID, x=ID} out: {b=ID, x=ID} USE: in: {} out: {} + INIT: in: {b=I?, x=I?} out: {b=I?, x=I?} USE: in: {} out: {} ===================== \ No newline at end of file diff --git a/compiler/testData/cfg-variables/lexicalScopes/objectLiteralScope.instructions b/compiler/testData/cfg-variables/lexicalScopes/objectLiteralScope.instructions index 04384b957b6..a9daf48f3a8 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/objectLiteralScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/objectLiteralScope.instructions @@ -24,9 +24,9 @@ L0: r(1) -> INIT: in: {bar=D, x=D} out: {bar=D, x=D} w(x|) INIT: in: {bar=D, x=D} out: {bar=D, x=ID} 2 jmp?(L2) INIT: in: {bar=D} out: {bar=D} - d(fun foo() { val a = 2 }) + d(fun foo() { val a = 2 }) INIT: in: {bar=-} out: {bar=-} L2 [after local declaration]: - r(object { init { val x = 1 } fun foo() { val a = 2 } }) -> + r(object { init { val x = 1 } fun foo() { val a = 2 } }) -> INIT: in: {bar=D} out: {bar=D} w(bar|) INIT: in: {bar=D} out: {bar=ID} mark("after") INIT: in: {bar=ID} out: {bar=ID} r("after") -> @@ -53,5 +53,5 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {bar=D} out: {bar=D} USE: in: {} out: {} + INIT: in: {bar=-} out: {bar=-} USE: in: {} out: {} ===================== \ No newline at end of file diff --git a/compiler/testData/cfg-variables/lexicalScopes/propertyAccessorScope.instructions b/compiler/testData/cfg-variables/lexicalScopes/propertyAccessorScope.instructions index 2a54292eac2..3cc2e90ec1c 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/propertyAccessorScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/propertyAccessorScope.instructions @@ -16,11 +16,11 @@ L0: 2 mark({ class A { var a : Int get() { return field } set(v: Int) { field = v } } }) jmp?(L2) v(var a : Int get() { return field } set(v: Int) { field = v }) INIT: in: {} out: {a=D} - jmp?(L3) INIT: in: {a=D} out: {a=D} USE: in: {field=ONLY_WRITTEN_NEVER_READ, field=READ} out: {field=ONLY_WRITTEN_NEVER_READ, field=READ} - d(get() { return field }) USE: in: {field=READ} out: {field=READ} + jmp?(L3) INIT: in: {a=D} out: {a=D} USE: in: {field=ONLY_WRITTEN_NEVER_READ, field=READ} out: {field=ONLY_WRITTEN_NEVER_READ, field=READ} + d(get() { return field }) INIT: in: {a=-} out: {a=-} USE: in: {field=READ} out: {field=READ} L3 [after local declaration]: - jmp?(L6) - d(set(v: Int) { field = v }) INIT: in: {a=D, field=I} out: {a=D, field=I} USE: in: {field=ONLY_WRITTEN_NEVER_READ} out: {field=ONLY_WRITTEN_NEVER_READ} + jmp?(L6) INIT: in: {a=D} out: {a=D} + d(set(v: Int) { field = v }) INIT: in: {a=-, field=I?} out: {a=-, field=I?} USE: in: {field=ONLY_WRITTEN_NEVER_READ} out: {field=ONLY_WRITTEN_NEVER_READ} L1: L2 [after local class]: L6 [after local declaration]: @@ -28,7 +28,7 @@ L6 [after local declaration]: error: sink: - INIT: in: {field=I} out: {field=I} USE: in: {} out: {} + INIT: in: {field=I?} out: {field=I?} USE: in: {} out: {} ===================== == get_a == get() { @@ -45,7 +45,7 @@ L5: error: INIT: in: {} out: {} sink: - INIT: in: {a=D} out: {a=D} USE: in: {} out: {} + INIT: in: {a=-} out: {a=-} USE: in: {} out: {} ===================== == set_a == set(v: Int) { @@ -57,13 +57,13 @@ L7: v(v: Int) INIT: in: {a=D} out: {a=D, v=D} magic[FAKE_INITIALIZER](v: Int) -> INIT: in: {a=D, v=D} out: {a=D, v=D} w(v|) INIT: in: {a=D, v=D} out: {a=D, v=ID} - 4 mark({ field = v }) INIT: in: {a=D, v=ID} out: {a=D, v=ID} USE: in: {field=ONLY_WRITTEN_NEVER_READ, v=READ} out: {field=ONLY_WRITTEN_NEVER_READ, v=READ} - r(v) -> USE: in: {field=ONLY_WRITTEN_NEVER_READ} out: {field=ONLY_WRITTEN_NEVER_READ, v=READ} - w(field|) INIT: in: {a=D, v=ID} out: {a=D, field=I, v=ID} USE: in: {} out: {field=ONLY_WRITTEN_NEVER_READ} + 4 mark({ field = v }) INIT: in: {a=D, v=ID} out: {a=D, v=ID} USE: in: {field=ONLY_WRITTEN_NEVER_READ, v=READ} out: {field=ONLY_WRITTEN_NEVER_READ, v=READ} + r(v) -> USE: in: {field=ONLY_WRITTEN_NEVER_READ} out: {field=ONLY_WRITTEN_NEVER_READ, v=READ} + w(field|) INIT: in: {a=D, v=ID} out: {a=D, field=I, v=ID} USE: in: {} out: {field=ONLY_WRITTEN_NEVER_READ} L8: 3 INIT: in: {a=D, field=I, v=ID} out: {a=D, field=I, v=ID} error: INIT: in: {} out: {} sink: - INIT: in: {a=D, field=I, v=ID} out: {a=D, field=I, v=ID} USE: in: {} out: {} + INIT: in: {a=-, field=I?, v=I?} out: {a=-, field=I?, v=I?} USE: in: {} out: {} ===================== diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileNotDefined.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileNotDefined.kt new file mode 100644 index 00000000000..e41bcd1a76e --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileNotDefined.kt @@ -0,0 +1,6 @@ +fun test(cond1: Boolean) { + do { + if (cond1) continue + val cond2 = false + } while (cond2) // cond2 may be not defined here +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileNotDefined.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileNotDefined.txt new file mode 100644 index 00000000000..79431a5118b --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileNotDefined.txt @@ -0,0 +1,3 @@ +package + +public fun test(/*0*/ cond1: kotlin.Boolean): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/enum/localEnums.kt b/compiler/testData/diagnostics/tests/enum/localEnums.kt index e786e0a83ef..83fafb81034 100644 --- a/compiler/testData/diagnostics/tests/enum/localEnums.kt +++ b/compiler/testData/diagnostics/tests/enum/localEnums.kt @@ -5,7 +5,7 @@ fun foo() { FOO, BAR } - val foo = A.FOO + val foo = A.FOO val b = object { enum class B {} } diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java index 31989a18195..3bf4736d14b 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java @@ -94,6 +94,12 @@ public class DataFlowTestGenerated extends AbstractDataFlowTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg-variables/bugs"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("doWhileNotDefined.kt") + public void testDoWhileNotDefined() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/doWhileNotDefined.kt"); + doTest(fileName); + } + @TestMetadata("initializationInLocalClass.kt") public void testInitializationInLocalClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/initializationInLocalClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java index b9f13708c8f..a6b28396d52 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java @@ -852,6 +852,12 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg-variables/bugs"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("doWhileNotDefined.kt") + public void testDoWhileNotDefined() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/doWhileNotDefined.kt"); + doTest(fileName); + } + @TestMetadata("initializationInLocalClass.kt") public void testInitializationInLocalClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/initializationInLocalClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 85ca6ae751a..18dd0a937ff 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3330,6 +3330,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("doWhileNotDefined.kt") + public void testDoWhileNotDefined() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileNotDefined.kt"); + doTest(fileName); + } + @TestMetadata("elvisNotProcessed.kt") public void testElvisNotProcessed() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt");