Implicit exhaustive when check for definite variable initialization (KT-8700)

This commit is contained in:
Mikhail Glukhikh
2015-12-25 14:25:38 +03:00
committed by Mikhail Glukhikh
parent 52c3fb03a2
commit 011a9f23b9
10 changed files with 47 additions and 16 deletions
@@ -38,6 +38,19 @@ open class ControlFlowInfo<D> internal constructor(protected val map: MutableMap
class InitControlFlowInfo(map: MutableMap<VariableDescriptor, VariableControlFlowState> = hashMapOf()) : class InitControlFlowInfo(map: MutableMap<VariableDescriptor, VariableControlFlowState> = hashMapOf()) :
ControlFlowInfo<VariableControlFlowState>(map) { ControlFlowInfo<VariableControlFlowState>(map) {
override fun copy() = InitControlFlowInfo(HashMap(map)) override fun copy() = InitControlFlowInfo(HashMap(map))
// this = output of EXHAUSTIVE_WHEN_ELSE instruction
// merge = input of MergeInstruction
// returns true if definite initialization in when happens here
fun checkDefiniteInitializationInWhen(merge: InitControlFlowInfo): Boolean {
for (entry in entries) {
if (entry.value.initState == InitState.INITIALIZED_EXHAUSTIVELY &&
merge[entry.key]?.initState == InitState.INITIALIZED) {
return true
}
}
return false
}
} }
class UseControlFlowInfo(map: MutableMap<VariableDescriptor, VariableUseState> = hashMapOf()) : class UseControlFlowInfo(map: MutableMap<VariableDescriptor, VariableUseState> = hashMapOf()) :
@@ -713,10 +713,28 @@ public class ControlFlowInformationProvider {
} }
public void markWhenWithoutElse() { public void markWhenWithoutElse() {
final Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
PseudocodeTraverserKt.traverse( PseudocodeTraverserKt.traverse(
pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() { pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
@Override @Override
public void execute(@NotNull Instruction instruction) { public void execute(@NotNull Instruction instruction) {
if (instruction instanceof MagicInstruction) {
MagicInstruction magicInstruction = (MagicInstruction) instruction;
if (magicInstruction.getKind() == MagicKind.EXHAUSTIVE_WHEN_ELSE) {
Instruction next = magicInstruction.getNext();
if (next instanceof MergeInstruction) {
MergeInstruction mergeInstruction = (MergeInstruction) next;
if (initializers.containsKey(mergeInstruction) && initializers.containsKey(magicInstruction)) {
InitControlFlowInfo mergeInfo = initializers.get(mergeInstruction).getIncoming();
InitControlFlowInfo magicInfo = initializers.get(magicInstruction).getOutgoing();
if (mergeInstruction.getElement() instanceof KtWhenExpression &&
magicInfo.checkDefiniteInitializationInWhen(mergeInfo)) {
trace.record(IMPLICIT_EXHAUSTIVE_WHEN, (KtWhenExpression) mergeInstruction.getElement());
}
}
}
}
}
PseudoValue value = instruction instanceof InstructionWithValue PseudoValue value = instruction instanceof InstructionWithValue
? ((InstructionWithValue) instruction).getOutputValue() ? ((InstructionWithValue) instruction).getOutputValue()
: null; : null;
@@ -3,10 +3,10 @@ enum class My { A, B }
fun test(a: My): String { fun test(a: My): String {
val q: String? val q: String?
when (a) { <!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (a) {
My.A -> q = "1" My.A -> q = "1"
My.B -> q = "2" My.B -> q = "2"
} }<!>
// When is exhaustive // When is exhaustive
return <!DEBUG_INFO_SMARTCAST!>q<!> return <!DEBUG_INFO_SMARTCAST!>q<!>
} }
@@ -5,11 +5,11 @@ enum class Direction {
fun foo(dir: Direction): Int { fun foo(dir: Direction): Int {
val res: Int val res: Int
// See KT-6046: res is always initialized // See KT-6046: res is always initialized
when (dir) { <!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (dir) {
Direction.NORTH -> res = 1 Direction.NORTH -> res = 1
Direction.SOUTH -> res = 2 Direction.SOUTH -> res = 2
Direction.WEST -> res = 3 Direction.WEST -> res = 3
Direction.EAST -> res = 4 Direction.EAST -> res = 4
} }<!>
return res return res
} }
@@ -1,10 +1,10 @@
fun foo(b: Boolean): Int { fun foo(b: Boolean): Int {
val x: Int val x: Int
val y: Int val y: Int
when (b) { <!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (b) {
true -> y = 1 true -> y = 1
false -> y = 0 false -> y = 0
} }<!>
// x is initialized here // x is initialized here
x = 3 x = 3
return x + y return x + y
@@ -5,10 +5,10 @@ enum class MyEnum {
fun foo(x: MyEnum?): Int { fun foo(x: MyEnum?): Int {
val y: Int val y: Int
// See KT-6046: y is always initialized // See KT-6046: y is always initialized
when (x) { <!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (x) {
MyEnum.A -> y = 1 MyEnum.A -> y = 1
MyEnum.B -> y = 2 MyEnum.B -> y = 2
null -> y = 0 null -> y = 0
} }<!>
return y return y
} }
@@ -3,10 +3,10 @@ fun foo(a: Boolean, b: Boolean): Int {
if (a) { if (a) {
x = 1 x = 1
} }
when (b) { <!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (b) {
true -> <!VAL_REASSIGNMENT!>x<!> = 2 true -> <!VAL_REASSIGNMENT!>x<!> = 2
false -> x = 3 false -> x = 3
} }<!>
return x return x
} }
@@ -3,10 +3,10 @@ fun foo(a: Boolean, b: Boolean): Int {
if (a) { if (a) {
x = 1 x = 1
} }
when (b) { <!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (b) {
true -> x = 2 true -> x = 2
false -> x = 3 false -> x = 3
} }<!>
return x return x
} }
@@ -3,11 +3,11 @@ enum class X { A, B, C, D }
fun foo(arg: X): String { fun foo(arg: X): String {
val res: String val res: String
when (arg) { <!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when (arg) {
X.A -> res = "A" X.A -> res = "A"
X.B -> res = "B" X.B -> res = "B"
X.C -> res = "C" X.C -> res = "C"
X.D -> res = "D" X.D -> res = "D"
} }<!>
return res return res
} }
@@ -7,10 +7,10 @@ enum class E {
class Outer(e: E) { class Outer(e: E) {
private val prop: Int private val prop: Int
init { init {
when(e ) { <!DEBUG_INFO_IMPLICIT_EXHAUSTIVE!>when(e ) {
// When is exhaustive, property is always initialized // When is exhaustive, property is always initialized
E.A -> prop = 1 E.A -> prop = 1
E.B -> prop = 2 E.B -> prop = 2
} }<!>
} }
} }