diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt index 8914ad44de2..1a33af1ceb1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt @@ -94,7 +94,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon ) { instruction: Instruction, incomingEdgesData: Collection -> - val enterInstructionData = mergeIncomingEdgesDataForInitializers(incomingEdgesData) + val enterInstructionData = mergeIncomingEdgesDataForInitializers(instruction, incomingEdgesData, blockScopeVariableInfo) val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny( instruction, enterInstructionData, blockScopeVariableInfo) Edges(enterInstructionData, exitInstructionData) @@ -207,7 +207,9 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon } private fun mergeIncomingEdgesDataForInitializers( - incomingEdgesData: Collection + instruction: Instruction, + incomingEdgesData: Collection, + blockScopeVariableInfo: BlockScopeVariableInfo ): InitControlFlowInfo { if (incomingEdgesData.size == 1) return incomingEdgesData.single() val variablesInScope = linkedSetOf() @@ -220,7 +222,8 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon var initState: InitState? = null var isDeclared = true for (edgeData in incomingEdgesData) { - val varControlFlowState = edgeData[variable] ?: VariableControlFlowState.create(isInitialized = false) + val varControlFlowState = edgeData[variable] + ?: getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo) initState = initState?.merge(varControlFlowState.initState) ?: varControlFlowState.initState if (!varControlFlowState.isDeclared) { isDeclared = false diff --git a/compiler/testData/cfg-variables/basic/UsageInFunctionLiteral.instructions b/compiler/testData/cfg-variables/basic/UsageInFunctionLiteral.instructions index 2ee4bfe79d0..58677b74e8f 100644 --- a/compiler/testData/cfg-variables/basic/UsageInFunctionLiteral.instructions +++ b/compiler/testData/cfg-variables/basic/UsageInFunctionLiteral.instructions @@ -16,7 +16,7 @@ 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) }) INIT: in: {a=I?, f=-} out: {a=I?, f=-} USE: in: {a=READ} out: {a=READ} + d({ x: Int -> val y = x + a use(a) }) INIT: in: {a=I, f=I?} out: {a=I, f=I?} USE: in: {a=READ} out: {a=READ} L2 [after local declaration]: 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} @@ -54,7 +54,7 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {a=I?, f=-, x=I?} out: {a=I?, f=-, x=I?} USE: in: {} out: {} + INIT: in: {a=I, f=I?, x=I?} out: {a=I, f=I?, x=I?} USE: in: {} out: {} ===================== == use == fun use(vararg a: Any?) = a diff --git a/compiler/testData/cfg-variables/basic/UseUninitializedInLambda.instructions b/compiler/testData/cfg-variables/basic/UseUninitializedInLambda.instructions index 9b9e755b8c0..db7d08ed818 100644 --- a/compiler/testData/cfg-variables/basic/UseUninitializedInLambda.instructions +++ b/compiler/testData/cfg-variables/basic/UseUninitializedInLambda.instructions @@ -24,12 +24,12 @@ fun foo() { } --------------------- L0: - 1 INIT: in: {} out: {} USE: in: {} out: {} + 1 INIT: in: {} out: {} USE: in: {} out: {} 2 mark({ var v: Any bar { v.hashCode() } }) v(var v: Any) INIT: in: {} out: {v=D} mark({ v.hashCode() }) INIT: in: {v=D} out: {v=D} jmp?(L2) - d({ v.hashCode() }) INIT: in: {v=-} out: {v=-} USE: in: {v=READ} out: {v=READ} + d({ v.hashCode() }) INIT: in: {v=I?} out: {v=I?} USE: in: {v=READ} out: {v=READ} L2 [after local declaration]: r({ v.hashCode() }) -> INIT: in: {v=D} out: {v=D} mark(bar { v.hashCode() }) @@ -39,7 +39,7 @@ L1: error: sink: - USE: in: {} out: {} + USE: in: {} out: {} ===================== == anonymous_0 == { v.hashCode() } @@ -47,8 +47,8 @@ sink: L3: 3 INIT: in: {v=D} out: {v=D} 4 mark(v.hashCode()) - mark(v.hashCode()) USE: in: {v=READ} out: {v=READ} - r(v) -> USE: in: {} out: {v=READ} + mark(v.hashCode()) USE: in: {v=READ} out: {v=READ} + r(v) -> USE: in: {} out: {v=READ} mark(hashCode()) call(hashCode(), hashCode|) -> L4: @@ -56,5 +56,5 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {v=-} out: {v=-} USE: in: {} out: {} + INIT: in: {v=I?} out: {v=I?} USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/bugs/kt10243.instructions b/compiler/testData/cfg-variables/bugs/kt10243.instructions index 492ae881098..70bdf82c8a4 100644 --- a/compiler/testData/cfg-variables/bugs/kt10243.instructions +++ b/compiler/testData/cfg-variables/bugs/kt10243.instructions @@ -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() {}) INIT: in: {f=I?, x=I?} out: {f=I?, x=I?} + d(fun bar() {}) INIT: in: {f=I, x=I} out: {f=I, x=I} L7 [after local declaration]: L10 [finish finally]: - 2 jmp(error) INIT: in: {f=ID, x=I?} out: {f=ID, x=I?} + 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() {}) INIT: in: {f=I?, x=I?} out: {f=I?, x=I?} + 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() {} }|) -> INIT: in: {f=ID, x=I?} out: {f=ID, x=I?} + 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: - INIT: in: {f=I?, x=I?} out: {f=I?, x=I?} 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=I?, x=I?} out: {f=I?, 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=I?, x=I?} out: {f=I?, 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/localObjectInConstructor.instructions b/compiler/testData/cfg-variables/bugs/localObjectInConstructor.instructions new file mode 100644 index 00000000000..efbf048f03f --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/localObjectInConstructor.instructions @@ -0,0 +1,485 @@ +== println == +fun println(obj: Any?) {} +--------------------- +L0: + 1 INIT: in: {} out: {} + v(obj: Any?) INIT: in: {} out: {obj=D} + magic[FAKE_INITIALIZER](obj: Any?) -> INIT: in: {obj=D} out: {obj=D} + w(obj|) INIT: in: {obj=D} out: {obj=ID} + 2 mark({}) INIT: in: {obj=ID} out: {obj=ID} + read (Unit) +L1: + 1 +error: + INIT: in: {} out: {} +sink: + INIT: in: {obj=I?} out: {obj=I?} USE: in: {} out: {} +===================== +== Demo0 == +class Demo0 { + private val some = object { + fun foo() { + println(state) // Ok + } + } + + private var state: Boolean = true +} +--------------------- +L0: + 1 INIT: in: {} out: {} + v(private val some = object { fun foo() { println(state) // Ok } }) INIT: in: {} out: {some=D} + mark(object { fun foo() { println(state) // Ok } }) INIT: in: {some=D} out: {some=D} + jmp?(L2) + d(fun foo() { println(state) // Ok }) INIT: in: {some=I?} out: {some=I?} USE: in: {state=READ} out: {state=READ} +L2 [after local declaration]: + r(object { fun foo() { println(state) // Ok } }) -> INIT: in: {some=D} out: {some=D} + w(some|) INIT: in: {some=D} out: {some=ID} + v(private var state: Boolean = true) INIT: in: {some=ID} out: {some=ID, state=D} + r(true) -> INIT: in: {some=ID, state=D} out: {some=ID, state=D} + w(state|) INIT: in: {some=ID, state=D} out: {some=ID, state=ID} +L1: + INIT: in: {some=ID, state=ID} out: {some=ID, state=ID} +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?, state=I?} out: {some=I?, state=I?} USE: in: {} out: {} +===================== +== foo == +fun foo() { + println(state) // Ok + } +--------------------- +L3: + 2 INIT: in: {some=D} out: {some=D} + 3 mark({ println(state) // Ok }) + magic[IMPLICIT_RECEIVER](state) -> USE: in: {state=READ} out: {state=READ} + r(state|) -> USE: in: {} out: {state=READ} + mark(println(state)) + call(println(state), println|) -> +L4: + 2 +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?} out: {some=I?} USE: in: {} out: {} +===================== +== Demo1 == +class Demo1 { + private val some = object { + fun foo() { + if (state) + state = true + + println(state) // must be initialized + } + } + + private var state: Boolean = true +} +--------------------- +L0: + 1 INIT: in: {} out: {} + v(private val some = object { fun foo() { if (state) state = true println(state) // must be initialized } }) INIT: in: {} out: {some=D} + mark(object { fun foo() { if (state) state = true println(state) // must be initialized } }) INIT: in: {some=D} out: {some=D} + jmp?(L2) + d(fun foo() { if (state) state = true println(state) // must be initialized }) INIT: in: {some=I?, state=I} out: {some=I?, state=I} USE: in: {state=READ} out: {state=READ} +L2 [after local declaration]: + r(object { fun foo() { if (state) state = true println(state) // must be initialized } }) -> INIT: in: {some=D} out: {some=D} + w(some|) INIT: in: {some=D} out: {some=ID} + v(private var state: Boolean = true) INIT: in: {some=ID} out: {some=ID, state=D} + r(true) -> INIT: in: {some=ID, state=D} out: {some=ID, state=D} + w(state|) INIT: in: {some=ID, state=D} out: {some=ID, state=ID} +L1: + INIT: in: {some=ID, state=ID} out: {some=ID, state=ID} +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?, state=I?} out: {some=I?, state=I?} USE: in: {} out: {} +===================== +== foo == +fun foo() { + if (state) + state = true + + println(state) // must be initialized + } +--------------------- +L3: + 2 INIT: in: {some=D} out: {some=D} + 3 mark({ if (state) state = true println(state) // must be initialized }) + mark(if (state) state = true) + magic[IMPLICIT_RECEIVER](state) -> + r(state|) -> + jf(L5|) USE: in: {state=READ} out: {state=READ} + magic[IMPLICIT_RECEIVER](state) -> + r(true) -> USE: in: {state=WRITTEN_AFTER_READ} out: {state=WRITTEN_AFTER_READ} + w(state|, ) INIT: in: {some=D} out: {some=D, state=I} USE: in: {state=READ} out: {state=WRITTEN_AFTER_READ} + jmp(L6) INIT: in: {some=D, state=I} out: {some=D, state=I} +L5 [else branch]: + read (Unit) INIT: in: {some=D} out: {some=D} +L6 ['if' expression result]: + merge(if (state) state = true|!) -> INIT: in: {some=D, state=I} out: {some=D, state=I} + magic[IMPLICIT_RECEIVER](state) -> USE: in: {state=READ} out: {state=READ} + r(state|) -> USE: in: {} out: {state=READ} + mark(println(state)) + call(println(state), println|) -> +L4: + 2 +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?, state=I} out: {some=I?, state=I} USE: in: {} out: {} +===================== +== Demo1A == +class Demo1A { + fun foo() { + if (state) + state = true + + println(state) // Ok + } + + private var state: Boolean = true +} +--------------------- +L0: + 1 INIT: in: {} out: {} + v(private var state: Boolean = true) INIT: in: {} out: {state=D} + r(true) -> INIT: in: {state=D} out: {state=D} + w(state|) INIT: in: {state=D} out: {state=ID} +L1: + INIT: in: {state=ID} out: {state=ID} +error: + INIT: in: {} out: {} +sink: + INIT: in: {state=I?} out: {state=I?} USE: in: {} out: {} +===================== +== foo == +fun foo() { + if (state) + state = true + + println(state) // Ok + } +--------------------- +L0: + 1 INIT: in: {} out: {} + 2 mark({ if (state) state = true println(state) // Ok }) + mark(if (state) state = true) + magic[IMPLICIT_RECEIVER](state) -> + r(state|) -> + jf(L2|) USE: in: {state=READ} out: {state=READ} + magic[IMPLICIT_RECEIVER](state) -> + r(true) -> USE: in: {state=WRITTEN_AFTER_READ} out: {state=WRITTEN_AFTER_READ} + w(state|, ) INIT: in: {} out: {state=I} USE: in: {state=READ} out: {state=WRITTEN_AFTER_READ} + jmp(L3) INIT: in: {state=I} out: {state=I} +L2 [else branch]: + read (Unit) INIT: in: {} out: {} +L3 ['if' expression result]: + merge(if (state) state = true|!) -> INIT: in: {state=I} out: {state=I} + magic[IMPLICIT_RECEIVER](state) -> USE: in: {state=READ} out: {state=READ} + r(state|) -> USE: in: {} out: {state=READ} + mark(println(state)) + call(println(state), println|) -> +L1: + 1 +error: + INIT: in: {} out: {} +sink: + INIT: in: {state=I} out: {state=I} USE: in: {} out: {} +===================== +== Demo2 == +class Demo2 { + private val some = object { + fun foo() { + if (state) + state = true + else + state = false + + println(state) // OK + } + } + + private var state: Boolean = true +} +--------------------- +L0: + 1 INIT: in: {} out: {} + v(private val some = object { fun foo() { if (state) state = true else state = false println(state) // OK } }) INIT: in: {} out: {some=D} + mark(object { fun foo() { if (state) state = true else state = false println(state) // OK } }) INIT: in: {some=D} out: {some=D} + jmp?(L2) + d(fun foo() { if (state) state = true else state = false println(state) // OK }) INIT: in: {some=I?, state=I} out: {some=I?, state=I} USE: in: {state=READ} out: {state=READ} +L2 [after local declaration]: + r(object { fun foo() { if (state) state = true else state = false println(state) // OK } }) -> INIT: in: {some=D} out: {some=D} + w(some|) INIT: in: {some=D} out: {some=ID} + v(private var state: Boolean = true) INIT: in: {some=ID} out: {some=ID, state=D} + r(true) -> INIT: in: {some=ID, state=D} out: {some=ID, state=D} + w(state|) INIT: in: {some=ID, state=D} out: {some=ID, state=ID} +L1: + INIT: in: {some=ID, state=ID} out: {some=ID, state=ID} +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?, state=I?} out: {some=I?, state=I?} USE: in: {} out: {} +===================== +== foo == +fun foo() { + if (state) + state = true + else + state = false + + println(state) // OK + } +--------------------- +L3: + 2 INIT: in: {some=D} out: {some=D} + 3 mark({ if (state) state = true else state = false println(state) // OK }) + mark(if (state) state = true else state = false) + magic[IMPLICIT_RECEIVER](state) -> USE: in: {state=READ} out: {state=READ} + r(state|) -> USE: in: {state=WRITTEN_AFTER_READ} out: {state=READ} + jf(L5|) + magic[IMPLICIT_RECEIVER](state) -> + r(true) -> USE: in: {state=WRITTEN_AFTER_READ} out: {state=WRITTEN_AFTER_READ} + w(state|, ) INIT: in: {some=D} out: {some=D, state=I} USE: in: {state=READ} out: {state=WRITTEN_AFTER_READ} + jmp(L6) INIT: in: {some=D, state=I} out: {some=D, state=I} USE: in: {state=READ} out: {state=READ} +L5 [else branch]: + magic[IMPLICIT_RECEIVER](state) -> INIT: in: {some=D} out: {some=D} + r(false) -> USE: in: {state=WRITTEN_AFTER_READ} out: {state=WRITTEN_AFTER_READ} + w(state|, ) INIT: in: {some=D} out: {some=D, state=I} USE: in: {state=READ} out: {state=WRITTEN_AFTER_READ} +L6 ['if' expression result]: + merge(if (state) state = true else state = false|!, !) -> INIT: in: {some=D, state=I} out: {some=D, state=I} + magic[IMPLICIT_RECEIVER](state) -> USE: in: {state=READ} out: {state=READ} + r(state|) -> USE: in: {} out: {state=READ} + mark(println(state)) + call(println(state), println|) -> +L4: + 2 +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?, state=I} out: {some=I?, state=I} USE: in: {} out: {} +===================== +== Demo3 == +class Demo3 { + private val some = run { + if (state) + state = true + + println(state) // OK + } + + private var state: Boolean = true +} +--------------------- +L0: + 1 INIT: in: {} out: {} + v(private val some = run { if (state) state = true println(state) // OK }) INIT: in: {} out: {some=D} + magic[IMPLICIT_RECEIVER](run { if (state) state = true println(state) // OK }) -> INIT: in: {some=D} out: {some=D} + mark({ if (state) state = true println(state) // OK }) + jmp?(L2) + d({ if (state) state = true println(state) // OK }) INIT: in: {some=I?} out: {some=I?} USE: in: {state=READ} out: {state=READ} +L2 [after local declaration]: + r({ if (state) state = true println(state) // OK }) -> INIT: in: {some=D} out: {some=D} + mark(run { if (state) state = true println(state) // OK }) + call(run { if (state) state = true println(state) // OK }, run|, ) -> + w(some|) INIT: in: {some=D} out: {some=ID} + v(private var state: Boolean = true) INIT: in: {some=ID} out: {some=ID, state=D} + r(true) -> INIT: in: {some=ID, state=D} out: {some=ID, state=D} + w(state|) INIT: in: {some=ID, state=D} out: {some=ID, state=ID} +L1: + INIT: in: {some=ID, state=ID} out: {some=ID, state=ID} +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?, state=I?} out: {some=I?, state=I?} USE: in: {} out: {} +===================== +== anonymous_0 == +{ + if (state) + state = true + + println(state) // OK + } +--------------------- +L3: + 2 INIT: in: {some=D} out: {some=D} + 3 mark(if (state) state = true println(state)) + mark(if (state) state = true) + magic[IMPLICIT_RECEIVER](state) -> + r(state|) -> + jf(L5|) USE: in: {state=READ} out: {state=READ} + magic[IMPLICIT_RECEIVER](state) -> + r(true) -> USE: in: {state=WRITTEN_AFTER_READ} out: {state=WRITTEN_AFTER_READ} + w(state|, ) USE: in: {state=READ} out: {state=WRITTEN_AFTER_READ} + jmp(L6) +L5 [else branch]: + read (Unit) +L6 ['if' expression result]: + merge(if (state) state = true|!) -> + magic[IMPLICIT_RECEIVER](state) -> USE: in: {state=READ} out: {state=READ} + r(state|) -> USE: in: {} out: {state=READ} + mark(println(state)) + call(println(state), println|) -> +L4: + 2 +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?} out: {some=I?} USE: in: {} out: {} +===================== +== exec == +fun exec(f: () -> T): T = f() +--------------------- +L0: + 1 INIT: in: {} out: {} + v(f: () -> T) INIT: in: {} out: {f=D} + magic[FAKE_INITIALIZER](f: () -> T) -> INIT: in: {f=D} out: {f=D} + w(f|) INIT: in: {f=D} out: {f=ID} USE: in: {f=READ} out: {f=READ} + r(f) -> INIT: in: {f=ID} out: {f=ID} USE: in: {} out: {f=READ} + mark(f()) + call(f(), invoke|) -> + ret(*|) L1 +L1: + +error: + INIT: in: {} out: {} +sink: + INIT: in: {f=I?} out: {f=I?} USE: in: {} out: {} +===================== +== Demo4 == +class Demo4 { + private val some = exec { + if (state) + state = true + + println(state) // must be initialized + } + + private var state: Boolean = true +} +--------------------- +L0: + 1 INIT: in: {} out: {} + v(private val some = exec { if (state) state = true println(state) // must be initialized }) INIT: in: {} out: {some=D} + mark({ if (state) state = true println(state) // must be initialized }) INIT: in: {some=D} out: {some=D} + jmp?(L2) + d({ if (state) state = true println(state) // must be initialized }) INIT: in: {some=I?, state=I} out: {some=I?, state=I} USE: in: {state=READ} out: {state=READ} +L2 [after local declaration]: + r({ if (state) state = true println(state) // must be initialized }) -> INIT: in: {some=D} out: {some=D} + mark(exec { if (state) state = true println(state) // must be initialized }) + call(exec { if (state) state = true println(state) // must be initialized }, exec|) -> + w(some|) INIT: in: {some=D} out: {some=ID} + v(private var state: Boolean = true) INIT: in: {some=ID} out: {some=ID, state=D} + r(true) -> INIT: in: {some=ID, state=D} out: {some=ID, state=D} + w(state|) INIT: in: {some=ID, state=D} out: {some=ID, state=ID} +L1: + INIT: in: {some=ID, state=ID} out: {some=ID, state=ID} +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?, state=I?} out: {some=I?, state=I?} USE: in: {} out: {} +===================== +== anonymous_1 == +{ + if (state) + state = true + + println(state) // must be initialized + } +--------------------- +L3: + 2 INIT: in: {some=D} out: {some=D} + 3 mark(if (state) state = true println(state)) + mark(if (state) state = true) + magic[IMPLICIT_RECEIVER](state) -> + r(state|) -> + jf(L5|) USE: in: {state=READ} out: {state=READ} + magic[IMPLICIT_RECEIVER](state) -> + r(true) -> USE: in: {state=WRITTEN_AFTER_READ} out: {state=WRITTEN_AFTER_READ} + w(state|, ) INIT: in: {some=D} out: {some=D, state=I} USE: in: {state=READ} out: {state=WRITTEN_AFTER_READ} + jmp(L6) INIT: in: {some=D, state=I} out: {some=D, state=I} +L5 [else branch]: + read (Unit) INIT: in: {some=D} out: {some=D} +L6 ['if' expression result]: + merge(if (state) state = true|!) -> INIT: in: {some=D, state=I} out: {some=D, state=I} + magic[IMPLICIT_RECEIVER](state) -> USE: in: {state=READ} out: {state=READ} + r(state|) -> USE: in: {} out: {state=READ} + mark(println(state)) + call(println(state), println|) -> +L4: + 2 +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?, state=I} out: {some=I?, state=I} USE: in: {} out: {} +===================== +== Demo5 == +class Demo5 { + private var state: Boolean = true + + private val some = object { + fun foo() { + if (state) + state = true + + println(state) // OK + } + } +} +--------------------- +L0: + 1 INIT: in: {} out: {} + v(private var state: Boolean = true) INIT: in: {} out: {state=D} + r(true) -> INIT: in: {state=D} out: {state=D} + w(state|) INIT: in: {state=D} out: {state=ID} + v(private val some = object { fun foo() { if (state) state = true println(state) // OK } }) INIT: in: {state=ID} out: {some=D, state=ID} + mark(object { fun foo() { if (state) state = true println(state) // OK } }) INIT: in: {some=D, state=ID} out: {some=D, state=ID} + jmp?(L2) + d(fun foo() { if (state) state = true println(state) // OK }) INIT: in: {some=I?, state=I} out: {some=I?, state=I} USE: in: {state=READ} out: {state=READ} +L2 [after local declaration]: + r(object { fun foo() { if (state) state = true println(state) // OK } }) -> INIT: in: {some=D, state=ID} out: {some=D, state=ID} + w(some|) INIT: in: {some=D, state=ID} out: {some=ID, state=ID} +L1: + INIT: in: {some=ID, state=ID} out: {some=ID, state=ID} +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?, state=I?} out: {some=I?, state=I?} USE: in: {} out: {} +===================== +== foo == +fun foo() { + if (state) + state = true + + println(state) // OK + } +--------------------- +L3: + 2 INIT: in: {some=D, state=ID} out: {some=D, state=ID} + 3 mark({ if (state) state = true println(state) // OK }) + mark(if (state) state = true) + magic[IMPLICIT_RECEIVER](state) -> + r(state|) -> + jf(L5|) USE: in: {state=READ} out: {state=READ} + magic[IMPLICIT_RECEIVER](state) -> + r(true) -> USE: in: {state=WRITTEN_AFTER_READ} out: {state=WRITTEN_AFTER_READ} + w(state|, ) USE: in: {state=READ} out: {state=WRITTEN_AFTER_READ} + jmp(L6) +L5 [else branch]: + read (Unit) +L6 ['if' expression result]: + merge(if (state) state = true|!) -> + magic[IMPLICIT_RECEIVER](state) -> USE: in: {state=READ} out: {state=READ} + r(state|) -> USE: in: {} out: {state=READ} + mark(println(state)) + call(println(state), println|) -> +L4: + 2 +error: + INIT: in: {} out: {} +sink: + INIT: in: {some=I?, state=I} out: {some=I?, state=I} USE: in: {} out: {} +===================== diff --git a/compiler/testData/cfg-variables/bugs/localObjectInConstructor.kt b/compiler/testData/cfg-variables/bugs/localObjectInConstructor.kt new file mode 100644 index 00000000000..704464b5a14 --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/localObjectInConstructor.kt @@ -0,0 +1,87 @@ +fun println(obj: Any?) {} + +class Demo0 { + private val some = object { + fun foo() { + println(state) // Ok + } + } + + private var state: Boolean = true +} + +class Demo1 { + private val some = object { + fun foo() { + if (state) + state = true + + println(state) // must be initialized + } + } + + private var state: Boolean = true +} + +class Demo1A { + fun foo() { + if (state) + state = true + + println(state) // Ok + } + + private var state: Boolean = true +} + +class Demo2 { + private val some = object { + fun foo() { + if (state) + state = true + else + state = false + + println(state) // OK + } + } + + private var state: Boolean = true +} + +class Demo3 { + private val some = run { + if (state) + state = true + + println(state) // OK + } + + private var state: Boolean = true +} + +fun exec(f: () -> T): T = f() + +class Demo4 { + private val some = exec { + if (state) + state = true + + println(state) // must be initialized + } + + private var state: Boolean = true +} + +class Demo5 { + private var state: Boolean = true + + private val some = object { + fun foo() { + if (state) + state = true + + println(state) // OK + } + } +} diff --git a/compiler/testData/cfg-variables/bugs/localObjectInConstructor.values b/compiler/testData/cfg-variables/bugs/localObjectInConstructor.values new file mode 100644 index 00000000000..dce307e1670 --- /dev/null +++ b/compiler/testData/cfg-variables/bugs/localObjectInConstructor.values @@ -0,0 +1,255 @@ +== println == +fun println(obj: Any?) {} +--------------------- + : * NEW: magic[FAKE_INITIALIZER](obj: Any?) -> +===================== +== Demo0 == +class Demo0 { + private val some = object { + fun foo() { + println(state) // Ok + } + } + + private var state: Boolean = true +} +--------------------- +object { fun foo() { println(state) // Ok } } : NEW: r(object { fun foo() { println(state) // Ok } }) -> +true : Boolean NEW: r(true) -> +===================== +== foo == +fun foo() { + println(state) // Ok + } +--------------------- + : Demo0 NEW: magic[IMPLICIT_RECEIVER](state) -> +state : * NEW: r(state|) -> +println(state) : * NEW: call(println(state), println|) -> +{ println(state) // Ok } : * COPY +===================== +== Demo1 == +class Demo1 { + private val some = object { + fun foo() { + if (state) + state = true + + println(state) // must be initialized + } + } + + private var state: Boolean = true +} +--------------------- +object { fun foo() { if (state) state = true println(state) // must be initialized } } : NEW: r(object { fun foo() { if (state) state = true println(state) // must be initialized } }) -> +true : Boolean NEW: r(true) -> +===================== +== foo == +fun foo() { + if (state) + state = true + + println(state) // must be initialized + } +--------------------- + : Demo1 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo1 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo1 NEW: magic[IMPLICIT_RECEIVER](state) -> +state : Boolean NEW: r(state|) -> +true : Boolean NEW: r(true) -> +state = true !: * +if (state) state = true : * NEW: merge(if (state) state = true|!) -> +state : * NEW: r(state|) -> +println(state) : * NEW: call(println(state), println|) -> +{ if (state) state = true println(state) // must be initialized } : * COPY +===================== +== Demo1A == +class Demo1A { + fun foo() { + if (state) + state = true + + println(state) // Ok + } + + private var state: Boolean = true +} +--------------------- +true : Boolean NEW: r(true) -> +===================== +== foo == +fun foo() { + if (state) + state = true + + println(state) // Ok + } +--------------------- + : Demo1A NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo1A NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo1A NEW: magic[IMPLICIT_RECEIVER](state) -> +state : Boolean NEW: r(state|) -> +true : Boolean NEW: r(true) -> +state = true !: * +if (state) state = true : * NEW: merge(if (state) state = true|!) -> +state : * NEW: r(state|) -> +println(state) : * NEW: call(println(state), println|) -> +{ if (state) state = true println(state) // Ok } : * COPY +===================== +== Demo2 == +class Demo2 { + private val some = object { + fun foo() { + if (state) + state = true + else + state = false + + println(state) // OK + } + } + + private var state: Boolean = true +} +--------------------- +object { fun foo() { if (state) state = true else state = false println(state) // OK } } : NEW: r(object { fun foo() { if (state) state = true else state = false println(state) // OK } }) -> +true : Boolean NEW: r(true) -> +===================== +== foo == +fun foo() { + if (state) + state = true + else + state = false + + println(state) // OK + } +--------------------- + : Demo2 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo2 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo2 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo2 NEW: magic[IMPLICIT_RECEIVER](state) -> +state : Boolean NEW: r(state|) -> +true : Boolean NEW: r(true) -> +state = true !: * +false : Boolean NEW: r(false) -> +state = false !: * +if (state) state = true else state = false : * NEW: merge(if (state) state = true else state = false|!, !) -> +state : * NEW: r(state|) -> +println(state) : * NEW: call(println(state), println|) -> +{ if (state) state = true else state = false println(state) // OK } : * COPY +===================== +== Demo3 == +class Demo3 { + private val some = run { + if (state) + state = true + + println(state) // OK + } + + private var state: Boolean = true +} +--------------------- + : Demo3 NEW: magic[IMPLICIT_RECEIVER](run { if (state) state = true println(state) // OK }) -> +{ if (state) state = true println(state) // OK } : {<: Demo3.() -> Unit} NEW: r({ if (state) state = true println(state) // OK }) -> +run { if (state) state = true println(state) // OK } : Unit NEW: call(run { if (state) state = true println(state) // OK }, run|, ) -> +true : Boolean NEW: r(true) -> +===================== +== anonymous_0 == +{ + if (state) + state = true + + println(state) // OK + } +--------------------- + : Demo3 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo3 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo3 NEW: magic[IMPLICIT_RECEIVER](state) -> +state : Boolean NEW: r(state|) -> +true : Boolean NEW: r(true) -> +state = true !: * +if (state) state = true : * NEW: merge(if (state) state = true|!) -> +state : * NEW: r(state|) -> +println(state) : * NEW: call(println(state), println|) -> +if (state) state = true println(state) : * COPY +===================== +== exec == +fun exec(f: () -> T): T = f() +--------------------- + : {<: () -> T} NEW: magic[FAKE_INITIALIZER](f: () -> T) -> +f : {<: () -> T} NEW: r(f) -> +f() : {<: T} NEW: call(f(), invoke|) -> +===================== +== Demo4 == +class Demo4 { + private val some = exec { + if (state) + state = true + + println(state) // must be initialized + } + + private var state: Boolean = true +} +--------------------- +{ if (state) state = true println(state) // must be initialized } : {<: () -> Unit} NEW: r({ if (state) state = true println(state) // must be initialized }) -> +exec { if (state) state = true println(state) // must be initialized } : Unit NEW: call(exec { if (state) state = true println(state) // must be initialized }, exec|) -> +true : Boolean NEW: r(true) -> +===================== +== anonymous_1 == +{ + if (state) + state = true + + println(state) // must be initialized + } +--------------------- + : Demo4 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo4 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo4 NEW: magic[IMPLICIT_RECEIVER](state) -> +state : Boolean NEW: r(state|) -> +true : Boolean NEW: r(true) -> +state = true !: * +if (state) state = true : * NEW: merge(if (state) state = true|!) -> +state : * NEW: r(state|) -> +println(state) : * NEW: call(println(state), println|) -> +if (state) state = true println(state) : * COPY +===================== +== Demo5 == +class Demo5 { + private var state: Boolean = true + + private val some = object { + fun foo() { + if (state) + state = true + + println(state) // OK + } + } +} +--------------------- +true : Boolean NEW: r(true) -> +object { fun foo() { if (state) state = true println(state) // OK } } : NEW: r(object { fun foo() { if (state) state = true println(state) // OK } }) -> +===================== +== foo == +fun foo() { + if (state) + state = true + + println(state) // OK + } +--------------------- + : Demo5 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo5 NEW: magic[IMPLICIT_RECEIVER](state) -> + : Demo5 NEW: magic[IMPLICIT_RECEIVER](state) -> +state : Boolean NEW: r(state|) -> +true : Boolean NEW: r(true) -> +state = true !: * +if (state) state = true : * NEW: merge(if (state) state = true|!) -> +state : * NEW: r(state|) -> +println(state) : * NEW: call(println(state), println|) -> +{ if (state) state = true println(state) // OK } : * COPY +===================== diff --git a/compiler/testData/cfg-variables/bugs/referenceToPropertyInitializer.instructions b/compiler/testData/cfg-variables/bugs/referenceToPropertyInitializer.instructions index 1638bdaeb41..89c0240de35 100644 --- a/compiler/testData/cfg-variables/bugs/referenceToPropertyInitializer.instructions +++ b/compiler/testData/cfg-variables/bugs/referenceToPropertyInitializer.instructions @@ -10,7 +10,7 @@ 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 }) INIT: in: {sum=-} out: {sum=-} USE: in: {sum=READ} out: {sum=READ} + d({ x: Int -> sum(x - 1) + x }) INIT: in: {sum=I?} out: {sum=I?} USE: in: {sum=READ} out: {sum=READ} L2 [after local declaration]: r({ x: Int -> sum(x - 1) + x }) -> INIT: in: {sum=D} out: {sum=D} w(sum|) INIT: in: {sum=D} out: {sum=ID} @@ -32,15 +32,15 @@ L3: magic[FAKE_INITIALIZER](x: Int) -> INIT: in: {sum=D, x=D} out: {sum=D, x=D} w(x|) INIT: in: {sum=D, x=D} out: {sum=D, x=ID} 3 mark(sum(x - 1) + x) INIT: in: {sum=D, x=ID} out: {sum=D, x=ID} - magic[IMPLICIT_RECEIVER](sum) -> USE: in: {sum=READ, x=READ} out: {sum=READ, x=READ} - r(sum|) -> USE: in: {x=READ} out: {sum=READ, x=READ} + magic[IMPLICIT_RECEIVER](sum) -> USE: in: {sum=READ, x=READ} out: {sum=READ, x=READ} + r(sum|) -> USE: in: {x=READ} out: {sum=READ, x=READ} r(x) -> r(1) -> mark(x - 1) call(x - 1, minus|, ) -> mark(sum(x - 1)) - call(sum(x - 1), invoke|, ) -> USE: in: {x=READ} out: {x=READ} - r(x) -> USE: in: {} out: {x=READ} + call(sum(x - 1), invoke|, ) -> USE: in: {x=READ} out: {x=READ} + r(x) -> USE: in: {} out: {x=READ} mark(sum(x - 1) + x) call(sum(x - 1) + x, plus|, ) -> 2 ret(*|) L4 @@ -49,7 +49,7 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {sum=-, x=I?} out: {sum=-, x=I?} USE: in: {} out: {} + INIT: in: {sum=I?, x=I?} out: {sum=I?, x=I?} USE: in: {} out: {} ===================== == A == open class A(val a: A) @@ -92,7 +92,7 @@ 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 }) INIT: in: {obj=-} out: {obj=-} USE: in: {obj=READ} out: {obj=READ} + d(fun foo() { val y = obj }) INIT: in: {obj=I?} out: {obj=I?} USE: in: {obj=READ} out: {obj=READ} L2 [after local declaration]: 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} @@ -120,7 +120,7 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {obj=-} out: {obj=-} USE: in: {} out: {} + INIT: in: {obj=I?} out: {obj=I?} USE: in: {} out: {} ===================== == TestOther == class TestOther { diff --git a/compiler/testData/cfg-variables/lexicalScopes/functionLiteralScope.instructions b/compiler/testData/cfg-variables/lexicalScopes/functionLiteralScope.instructions index c0ba798e242..683974c44bf 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/functionLiteralScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/functionLiteralScope.instructions @@ -19,7 +19,7 @@ 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 }) INIT: in: {b=I?, f=-} out: {b=I?, f=-} USE: in: {b=READ} out: {b=READ} + d({ x: Int -> val a = x + b }) INIT: in: {b=I, f=I?} out: {b=I, f=I?} USE: in: {b=READ} out: {b=READ} L2 [after local declaration]: 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} @@ -54,5 +54,5 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {b=I?, f=-, x=I?} out: {b=I?, f=-, x=I?} USE: in: {} out: {} + INIT: in: {b=I, f=I?, x=I?} out: {b=I, f=I?, x=I?} USE: in: {} out: {} ===================== diff --git a/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions b/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions index 8e6ccec7907..4e84af904ae 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/localClass.instructions @@ -27,10 +27,10 @@ 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 }) INIT: in: {x=I?} out: {x=I?} 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") + mark("after") INIT: in: {x=I?} out: {x=I?} r("after") -> L1: 1 INIT: in: {} out: {} @@ -56,5 +56,5 @@ L5: error: INIT: in: {} out: {} sink: - INIT: in: {x=I?} out: {x=I?} 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 fb71ea9b39a..7698ee755fd 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/localFunctionScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/localFunctionScope.instructions @@ -17,7 +17,7 @@ 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 }) INIT: in: {b=I?} out: {b=I?} 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") INIT: in: {b=ID} out: {b=ID} r("after") -> @@ -50,5 +50,5 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {b=I?, x=I?} out: {b=I?, x=I?} 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 8504e1ff778..003bc3b3273 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/localFunctionScopeWithoutBody.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/localFunctionScopeWithoutBody.instructions @@ -15,7 +15,7 @@ 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) INIT: in: {b=I?} out: {b=I?} 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") INIT: in: {b=ID} out: {b=ID} r("after") -> @@ -44,5 +44,5 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {b=I?, x=I?} out: {b=I?, x=I?} 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 a9daf48f3a8..9bd858d4db0 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/objectLiteralScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/objectLiteralScope.instructions @@ -24,7 +24,7 @@ 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 }) INIT: in: {bar=-} out: {bar=-} + d(fun foo() { val a = 2 }) INIT: in: {bar=I?} out: {bar=I?} L2 [after local declaration]: 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} @@ -53,5 +53,5 @@ L4: error: INIT: in: {} out: {} sink: - INIT: in: {bar=-} out: {bar=-} USE: in: {} out: {} + INIT: in: {bar=I?} out: {bar=I?} 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 3cc2e90ec1c..00fb06c2061 100644 --- a/compiler/testData/cfg-variables/lexicalScopes/propertyAccessorScope.instructions +++ b/compiler/testData/cfg-variables/lexicalScopes/propertyAccessorScope.instructions @@ -17,10 +17,10 @@ L0: 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 }) INIT: in: {a=-} out: {a=-} USE: in: {field=READ} out: {field=READ} + d(get() { return field }) INIT: in: {a=I?} out: {a=I?} USE: in: {field=READ} out: {field=READ} L3 [after local declaration]: 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} + d(set(v: Int) { field = v }) INIT: in: {a=I?, field=I} out: {a=I?, 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() { @@ -37,15 +37,15 @@ get() { --------------------- 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} + 4 mark({ return field }) USE: in: {field=READ} out: {field=READ} + r(field) -> USE: in: {} out: {field=READ} ret(*|) L5 L5: 3 error: INIT: in: {} out: {} sink: - INIT: in: {a=-} out: {a=-} USE: in: {} out: {} + INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {} ===================== == set_a == set(v: Int) { @@ -65,5 +65,5 @@ L8: error: INIT: in: {} out: {} sink: - INIT: in: {a=-, field=I?, v=I?} out: {a=-, field=I?, v=I?} USE: in: {} out: {} + INIT: in: {a=I?, field=I, v=I?} out: {a=I?, field=I, v=I?} USE: in: {} out: {} ===================== diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.kt new file mode 100644 index 00000000000..3cb2bd13754 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.kt @@ -0,0 +1,87 @@ +fun println(obj: Any?) = obj + +class Demo0 { + private val some = object { + fun foo() { + println(state) + } + } + + private var state: Boolean = true +} + +class Demo1 { + private val some = object { + fun foo() { + if (state) + state = true + + println(state) + } + } + + private var state: Boolean = true +} + +class Demo1A { + fun foo() { + if (state) + state = true + + println(state) + } + + private var state: Boolean = true +} + +class Demo2 { + private val some = object { + fun foo() { + if (state) + state = true + else + state = false + + println(state) + } + } + + private var state: Boolean = true +} + +class Demo3 { + private val some = run { + if (state) + state = true + + println(state) + } + + private var state: Boolean = true +} + +fun exec(f: () -> T): T = f() + +class Demo4 { + private val some = exec { + if (state) + state = true + + println(state) + } + + private var state: Boolean = true +} + +class Demo5 { + private var state: Boolean = true + + private val some = object { + fun foo() { + if (state) + state = true + + println(state) + } + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.txt new file mode 100644 index 00000000000..d6583ec8289 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.txt @@ -0,0 +1,67 @@ +package + +public fun exec(/*0*/ f: () -> T): T +public fun println(/*0*/ obj: kotlin.Any?): kotlin.Any? + +public final class Demo0 { + public constructor Demo0() + private final val some: Demo0.some. + private final var state: kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Demo1 { + public constructor Demo1() + private final val some: Demo1.some. + private final var state: kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Demo1A { + public constructor Demo1A() + private final var state: kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Demo2 { + public constructor Demo2() + private final val some: Demo2.some. + private final var state: kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Demo3 { + public constructor Demo3() + private final val some: kotlin.Any? + private final var state: kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Demo4 { + public constructor Demo4() + private final val some: kotlin.Any? + private final var state: kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Demo5 { + public constructor Demo5() + private final val some: Demo5.some. + private final var state: kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java index 3bf4736d14b..e614af38659 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/DataFlowTestGenerated.java @@ -130,6 +130,12 @@ public class DataFlowTestGenerated extends AbstractDataFlowTest { doTest(fileName); } + @TestMetadata("localObjectInConstructor.kt") + public void testLocalObjectInConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/localObjectInConstructor.kt"); + doTest(fileName); + } + @TestMetadata("referenceToPropertyInitializer.kt") public void testReferenceToPropertyInitializer() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/referenceToPropertyInitializer.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java index a6b28396d52..1379de24f51 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java @@ -888,6 +888,12 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest { doTest(fileName); } + @TestMetadata("localObjectInConstructor.kt") + public void testLocalObjectInConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/localObjectInConstructor.kt"); + doTest(fileName); + } + @TestMetadata("referenceToPropertyInitializer.kt") public void testReferenceToPropertyInitializer() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg-variables/bugs/referenceToPropertyInitializer.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index ff96518c11f..e38c9e05b1c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3600,6 +3600,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("localObjectInConstructor.kt") + public void testLocalObjectInConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.kt"); + doTest(fileName); + } + @TestMetadata("nestedTryFinally.kt") public void testNestedTryFinally() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/nestedTryFinally.kt");