diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index 2dfff4186d6..2f89f18d954 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -915,7 +915,14 @@ class ControlFlowProcessor(private val trace: BindingTrace) { mark(expression) val statements = expression.statements for (statement in statements) { + val afterClassLabel = (statement as? KtClassOrObject)?.let { builder.createUnboundLabel("after local class") } + if (afterClassLabel != null) { + builder.nondeterministicJump(afterClassLabel, statement, null) + } generateInstructions(statement) + if (afterClassLabel != null) { + builder.bindLabel(afterClassLabel) + } } if (statements.isEmpty()) { builder.loadUnit(expression) diff --git a/compiler/testData/cfg-variables/bugs/initializationInLocalClass.instructions b/compiler/testData/cfg-variables/bugs/initializationInLocalClass.instructions new file mode 100644 index 00000000000..6b5f23273a4 --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/initializationInLocalClass.instructions @@ -0,0 +1,31 @@ +== foo == +fun foo() { + var x: String + class A { + init { + x = "" + } + } + x.length +} +--------------------- +L0: + 1 INIT: in: {} out: {} USE: in: {length=READ} out: {length=READ} + 2 mark({ var x: String class A { init { x = "" } } x.length }) + v(var x: String) INIT: in: {} out: {x=D} + jmp?(L2) INIT: in: {x=D} out: {x=D} USE: in: {length=READ, x=READ} out: {length=READ, x=READ} + 3 mark({ x = "" }) + mark("") + r("") -> USE: in: {length=READ, x=WRITTEN_AFTER_READ} out: {length=READ, x=WRITTEN_AFTER_READ} + w(x|) INIT: in: {x=D} out: {x=ID} USE: in: {length=READ, x=READ} out: {length=READ, x=WRITTEN_AFTER_READ} +L2 [after local class]: + 2 mark(x.length) INIT: in: {x=I?D} out: {x=I?D} USE: in: {length=READ, x=READ} out: {length=READ, x=READ} + r(x) -> USE: in: {length=READ} out: {length=READ, x=READ} + r(length|) -> USE: in: {} out: {length=READ} +L1: + 1 INIT: in: {} out: {} +error: + +sink: + USE: in: {} out: {} +===================== diff --git a/compiler/testData/cfg-variables/bugs/initializationInLocalClass.kt b/compiler/testData/cfg-variables/bugs/initializationInLocalClass.kt new file mode 100644 index 00000000000..aebec912bb8 --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/initializationInLocalClass.kt @@ -0,0 +1,9 @@ +fun foo() { + var x: String + class A { + init { + x = "" + } + } + x.length +} diff --git a/compiler/testData/cfg-variables/bugs/initializationInLocalClass.values b/compiler/testData/cfg-variables/bugs/initializationInLocalClass.values new file mode 100644 index 00000000000..aa4ff9c295c --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/initializationInLocalClass.values @@ -0,0 +1,19 @@ +== foo == +fun foo() { + var x: String + class A { + init { + x = "" + } + } + x.length +} +--------------------- +"" : String NEW: r("") -> +x = "" !: * +{ x = "" } !: * COPY +x : {<: CharSequence} NEW: r(x) -> +length : * NEW: r(length|) -> +x.length : * COPY +{ var x: String class A { init { x = "" } } x.length } : * COPY +===================== diff --git a/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions b/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions index 405f085c8e8..7e8aaa742b4 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions @@ -17,6 +17,7 @@ L0: 2 mark({ "before" class A(val x: Int) { init { val a = x } fun foo() { val b = x } } "after" }) mark("before") r("before") -> + jmp?(L2) v(val x: Int) INIT: in: {} out: {x=D} magic[FAKE_INITIALIZER](val x: Int) -> INIT: in: {x=D} out: {x=D} w(x|) INIT: in: {x=D} out: {x=ID} @@ -25,9 +26,10 @@ L0: magic[IMPLICIT_RECEIVER](x) -> INIT: in: {a=D, x=ID} out: {a=D, x=ID} r(x|) -> w(a|) INIT: in: {a=D, x=ID} out: {a=ID, x=ID} - 2 jmp?(L2) INIT: in: {x=ID} out: {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} -L2 [after local declaration]: +L2 [after local class]: +L3 [after local declaration]: mark("after") r("after") -> L1: @@ -42,14 +44,14 @@ fun foo() { val b = x } --------------------- -L3: +L4: 3 INIT: in: {x=ID} out: {x=ID} 4 mark({ val b = x }) v(val b = x) INIT: in: {x=ID} out: {b=D, x=ID} magic[IMPLICIT_RECEIVER](x) -> INIT: in: {b=D, x=ID} out: {b=D, x=ID} USE: in: {x=READ} out: {x=READ} r(x|) -> USE: in: {} out: {x=READ} w(b|) INIT: in: {b=D, x=ID} out: {b=ID, x=ID} -L4: +L5: 3 INIT: in: {x=ID} out: {x=ID} error: INIT: in: {} out: {} diff --git a/compiler/testData/cfg-variables/lexicalScopes/localObject.instructions b/compiler/testData/cfg-variables/lexicalScopes/localObject.instructions index 9d8f9fbecd0..a379fa0e05b 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/localObject.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/localObject.instructions @@ -17,13 +17,15 @@ L0: 2 mark({ "before" object A { init { val a = 1 } fun foo() { val b = 2 } } "after" }) mark("before") r("before") -> + jmp?(L2) 3 mark({ val a = 1 }) v(val a = 1) INIT: in: {} out: {a=D} r(1) -> INIT: in: {a=D} out: {a=D} w(a|) INIT: in: {a=D} out: {a=ID} - 2 jmp?(L2) INIT: in: {} out: {} + 2 jmp?(L3) INIT: in: {} out: {} d(fun foo() { val b = 2 }) -L2 [after local declaration]: +L2 [after local class]: +L3 [after local declaration]: mark("after") r("after") -> L1: @@ -38,13 +40,13 @@ fun foo() { val b = 2 } --------------------- -L3: +L4: 3 INIT: in: {} out: {} 4 mark({ val b = 2 }) v(val b = 2) INIT: in: {} out: {b=D} r(2) -> INIT: in: {b=D} out: {b=D} w(b|) INIT: in: {b=D} out: {b=ID} -L4: +L5: 3 INIT: in: {} out: {} error: diff --git a/compiler/testData/cfg-variables/lexicalScopes/propertyAccessorScope.instructions b/compiler/testData/cfg-variables/lexicalScopes/propertyAccessorScope.instructions index 79bd6c32213..2a54292eac2 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/propertyAccessorScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/propertyAccessorScope.instructions @@ -14,14 +14,16 @@ fun foo() { L0: 1 INIT: in: {} out: {} 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?(L2) INIT: in: {a=D} out: {a=D} USE: in: {field=ONLY_WRITTEN_NEVER_READ, field=READ} out: {field=ONLY_WRITTEN_NEVER_READ, 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 }) USE: in: {field=READ} out: {field=READ} -L2 [after local declaration]: - jmp?(L5) +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} L1: -L5 [after local declaration]: +L2 [after local class]: +L6 [after local declaration]: 1 INIT: in: {} out: {} error: @@ -33,12 +35,12 @@ get() { return field } --------------------- -L3: +L4: 3 INIT: in: {a=D} out: {a=D} 4 mark({ return field }) USE: in: {field=READ} out: {field=READ} r(field) -> USE: in: {} out: {field=READ} - ret(*|) L4 -L4: + ret(*|) L5 +L5: 3 error: INIT: in: {} out: {} @@ -50,7 +52,7 @@ set(v: Int) { field = v } --------------------- -L6: +L7: 3 INIT: in: {a=D} out: {a=D} 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} @@ -58,7 +60,7 @@ L6: 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} -L7: +L8: 3 INIT: in: {a=D, field=I, v=ID} out: {a=D, field=I, v=ID} error: INIT: in: {} out: {} diff --git a/compiler/testData/cfg/declarations/local/localClass.instructions b/compiler/testData/cfg/declarations/local/localClass.instructions index ea7ec9ebaee..84afadfcfbd 100644 --- a/compiler/testData/cfg/declarations/local/localClass.instructions +++ b/compiler/testData/cfg/declarations/local/localClass.instructions @@ -14,11 +14,13 @@ fun f() { L0: 1 2 mark({ class LocalClass() { fun f() { val x = "" fun loc() { val x3 = "" } } } }) - jmp?(L2) NEXT:[, d(fun f() { val x = "" fun loc() { val x3 = "" } })] + jmp?(L2) NEXT:[, jmp?(L3)] + jmp?(L3) NEXT:[, d(fun f() { val x = "" fun loc() { val x3 = "" } })] d(fun f() { val x = "" fun loc() { val x3 = "" } }) NEXT:[] L1: -L2 [after local declaration]: - 1 NEXT:[] PREV:[jmp?(L2)] +L2 [after local class]: +L3 [after local declaration]: + 1 NEXT:[] PREV:[jmp?(L2), jmp?(L3)] error: PREV:[] sink: @@ -33,18 +35,18 @@ fun f() { } } --------------------- -L3: +L4: 3 4 mark({ val x = "" fun loc() { val x3 = "" } }) v(val x = "") mark("") r("") -> w(x|) - jmp?(L5) NEXT:[, d(fun loc() { val x3 = "" })] + jmp?(L6) NEXT:[, d(fun loc() { val x3 = "" })] d(fun loc() { val x3 = "" }) NEXT:[] -L4: -L5 [after local declaration]: - 3 NEXT:[] PREV:[jmp?(L5)] +L5: +L6 [after local declaration]: + 3 NEXT:[] PREV:[jmp?(L6)] error: PREV:[] sink: @@ -55,17 +57,17 @@ fun loc() { val x3 = "" } --------------------- -L6: +L7: 5 6 mark({ val x3 = "" }) v(val x3 = "") mark("") r("") -> w(x3|) -L7: +L8: 5 NEXT:[] error: PREV:[] sink: PREV:[, ] -===================== \ No newline at end of file +===================== diff --git a/compiler/testData/cfg/declarations/local/localProperty.instructions b/compiler/testData/cfg/declarations/local/localProperty.instructions index ba91e1ed178..40e2332480c 100644 --- a/compiler/testData/cfg/declarations/local/localProperty.instructions +++ b/compiler/testData/cfg/declarations/local/localProperty.instructions @@ -12,12 +12,14 @@ fun foo() { L0: 1 2 mark({ class B { val a: Int get() { val b: Int return b } } }) + jmp?(L2) NEXT:[, v(val a: Int get() { val b: Int return b })] v(val a: Int get() { val b: Int return b }) - jmp?(L2) NEXT:[, d(get() { val b: Int return b })] + jmp?(L3) NEXT:[, d(get() { val b: Int return b })] d(get() { val b: Int return b }) NEXT:[] L1: -L2 [after local declaration]: - 1 NEXT:[] PREV:[jmp?(L2)] +L2 [after local class]: +L3 [after local declaration]: + 1 NEXT:[] PREV:[jmp?(L2), jmp?(L3)] error: PREV:[] sink: @@ -29,16 +31,16 @@ get() { return b } --------------------- -L3: +L4: 3 4 mark({ val b: Int return b }) v(val b: Int) r(b) -> - ret(*|) L4 -L4: + ret(*|) L5 +L5: 3 NEXT:[] error: PREV:[] sink: PREV:[, ] -===================== \ No newline at end of file +===================== diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLocalDeclarations.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLocalDeclarations.kt index 2ef3ce8afcd..81a3ac5df54 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLocalDeclarations.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLocalDeclarations.kt @@ -23,7 +23,7 @@ fun testObjectExpression1() { fun testClassDeclaration() { class C : Foo(todo()) {} - bar() + bar() } fun testFunctionDefaultArgument() { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.kt new file mode 100644 index 00000000000..a6088c4dc74 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.kt @@ -0,0 +1,21 @@ +fun foo() { + var x: String + class A { + init { + x = "" + } + } + // Error! See KT-10042 + x.length +} + +fun bar() { + var x: String + object: Any() { + init { + x = "" + } + } + // Ok + x.length +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.txt new file mode 100644 index 00000000000..a34eb4a98fa --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.txt @@ -0,0 +1,4 @@ +package + +public fun bar(): kotlin.Unit +public fun foo(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java index 80820c9cb08..6aa25267655 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("initializationInLocalClass.kt") + public void testInitializationInLocalClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/initializationInLocalClass.kt"); + doTest(fileName); + } + @TestMetadata("kt10243.kt") public void testKt10243() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/kt10243.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java index fa84fac429f..4ca34306ff2 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java @@ -840,6 +840,12 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg-variables/bugs"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("initializationInLocalClass.kt") + public void testInitializationInLocalClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/initializationInLocalClass.kt"); + doTest(fileName); + } + @TestMetadata("kt10243.kt") public void testKt10243() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/kt10243.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index edd95bc7455..7a7e8dd13f8 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -2895,6 +2895,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("initializationInLocalClass.kt") + public void testInitializationInLocalClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.kt"); + doTest(fileName); + } + @TestMetadata("initializationInLocalFun.kt") public void testInitializationInLocalFun() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalFun.kt");