|
| 1 | +// TARGET_BACKEND: JVM_IR |
| 2 | +// IGNORE_BACKEND: JVM_IR |
| 3 | + |
| 4 | +// WITH_STDLIB |
| 5 | + |
| 6 | +import kotlin.Self |
| 7 | + |
| 8 | +interface ReadOnlyControlFlowInfo<K : Any, D : Any> { |
| 9 | + fun getOrNull(key: K): D? |
| 10 | + |
| 11 | + // Only used in tests |
| 12 | + fun asMap(): Map<K, D> |
| 13 | +} |
| 14 | + |
| 15 | +@Self |
| 16 | +abstract class ControlFlowInfo<K : Any, D : Any> |
| 17 | +internal constructor( |
| 18 | + protected val map: Map<K, D> = mapOf() |
| 19 | +) : Map<K, D> by map, ReadOnlyControlFlowInfo<K, D> { |
| 20 | + protected abstract fun copy(newMap: Map<K, D>): Self |
| 21 | + |
| 22 | + fun put(key: K, value: D): Self = |
| 23 | + put(key, value, this[key] ?: null as D?) |
| 24 | + |
| 25 | + /** |
| 26 | + * This overload exists just for sake of optimizations: in some cases we've just retrieved the old value, |
| 27 | + * so we don't need to scan through the persistent hashmap again |
| 28 | + */ |
| 29 | + fun put(key: K, value: D, oldValue: D?): Self { |
| 30 | + // Avoid a copy instance creation if new value is the same |
| 31 | + if (value == oldValue) return this as Self |
| 32 | + val newMap = map + (key to value) |
| 33 | + return copy(newMap) |
| 34 | + } |
| 35 | + |
| 36 | + override fun getOrNull(key: K): D? = this[key] ?: null as D? |
| 37 | + override fun asMap() = this |
| 38 | + |
| 39 | + fun retainAll(predicate: (K) -> Boolean): Self { |
| 40 | + val newMap = map.filter { predicate(it.key) } |
| 41 | + return copy(newMap) |
| 42 | + } |
| 43 | + |
| 44 | + override fun equals(other: Any?) = map == (other as? ControlFlowInfo<*, *, *>)?.map |
| 45 | + |
| 46 | + override fun hashCode() = map.hashCode() |
| 47 | + |
| 48 | + override fun toString() = map.toString() |
| 49 | +} |
| 50 | + |
| 51 | +// ALIASES BEGIN |
| 52 | + |
| 53 | +typealias VariableDescriptor = String |
| 54 | +typealias VariableUsageControlFlowInfo<S, D> = ControlFlowInfo<VariableDescriptor, D, S> |
| 55 | +typealias VariableUsageReadOnlyControlInfo = ReadOnlyControlFlowInfo<VariableDescriptor, VariableUseState> |
| 56 | + |
| 57 | +// ALIASES END |
| 58 | + |
| 59 | + |
| 60 | +// IMPLEMENTATIONS OF CONTROL FLOW INFOS BEGIN |
| 61 | + |
| 62 | +class UsageVariableControlFlowInfo(map: Map<VariableDescriptor, VariableUseState> = mapOf()) : |
| 63 | + VariableUsageControlFlowInfo<UsageVariableControlFlowInfo, VariableUseState>(map), |
| 64 | + VariableUsageReadOnlyControlInfo { |
| 65 | + override fun copy(newMap: Map<VariableDescriptor, VariableUseState>): UsageVariableControlFlowInfo = |
| 66 | + UsageVariableControlFlowInfo(newMap) |
| 67 | +} |
| 68 | + |
| 69 | +interface VariableInitReadOnlyControlFlowInfo : |
| 70 | + ReadOnlyControlFlowInfo<VariableDescriptor, VariableControlFlowState> { |
| 71 | + fun checkDefiniteInitializationInWhen(merge: VariableInitReadOnlyControlFlowInfo): Boolean |
| 72 | +} |
| 73 | + |
| 74 | +class VariableInitControlFlowInfo(map: Map<VariableDescriptor, VariableControlFlowState> = mapOf()) : |
| 75 | + VariableUsageControlFlowInfo<VariableInitControlFlowInfo, VariableControlFlowState>(map), |
| 76 | + VariableInitReadOnlyControlFlowInfo { |
| 77 | + override fun copy(newMap: Map<VariableDescriptor, VariableControlFlowState>): VariableInitControlFlowInfo = |
| 78 | + VariableInitControlFlowInfo(newMap) |
| 79 | + |
| 80 | + // this = output of EXHAUSTIVE_WHEN_ELSE instruction |
| 81 | + // merge = input of MergeInstruction |
| 82 | + // returns true if definite initialization in when happens here |
| 83 | + override fun checkDefiniteInitializationInWhen(merge: VariableInitReadOnlyControlFlowInfo): Boolean { |
| 84 | + for ((key, value) in iterator()) { |
| 85 | + if (value.initState == InitState.INITIALIZED_EXHAUSTIVELY && |
| 86 | + merge.getOrNull(key)?.initState == InitState.INITIALIZED |
| 87 | + ) { |
| 88 | + return true |
| 89 | + } |
| 90 | + } |
| 91 | + return false |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +// IMPLEMENTATIONS OF CONTROL FLOW INFOS END |
| 96 | + |
| 97 | +// STATES BEGIN |
| 98 | + |
| 99 | +class VariableControlFlowState private constructor(val initState: InitState, val isDeclared: Boolean) { |
| 100 | + |
| 101 | + fun definitelyInitialized(): Boolean = initState == InitState.INITIALIZED |
| 102 | + |
| 103 | + fun mayBeInitialized(): Boolean = initState != InitState.NOT_INITIALIZED |
| 104 | + |
| 105 | + override fun toString(): String { |
| 106 | + if (initState == InitState.NOT_INITIALIZED && !isDeclared) return "-" |
| 107 | + return "$initState${if (isDeclared) "D" else ""}" |
| 108 | + } |
| 109 | + |
| 110 | + companion object { |
| 111 | + |
| 112 | + private val VS_IT = VariableControlFlowState(InitState.INITIALIZED, true) |
| 113 | + private val VS_IF = VariableControlFlowState(InitState.INITIALIZED, false) |
| 114 | + private val VS_ET = VariableControlFlowState(InitState.INITIALIZED_EXHAUSTIVELY, true) |
| 115 | + private val VS_EF = VariableControlFlowState(InitState.INITIALIZED_EXHAUSTIVELY, false) |
| 116 | + private val VS_UT = VariableControlFlowState(InitState.UNKNOWN, true) |
| 117 | + private val VS_UF = VariableControlFlowState(InitState.UNKNOWN, false) |
| 118 | + private val VS_NT = VariableControlFlowState(InitState.NOT_INITIALIZED, true) |
| 119 | + private val VS_NF = VariableControlFlowState(InitState.NOT_INITIALIZED, false) |
| 120 | + |
| 121 | + fun create(initState: InitState, isDeclared: Boolean): VariableControlFlowState = |
| 122 | + when (initState) { |
| 123 | + InitState.INITIALIZED -> if (isDeclared) VS_IT else VS_IF |
| 124 | + InitState.INITIALIZED_EXHAUSTIVELY -> if (isDeclared) VS_ET else VS_EF |
| 125 | + InitState.UNKNOWN -> if (isDeclared) VS_UT else VS_UF |
| 126 | + InitState.NOT_INITIALIZED -> if (isDeclared) VS_NT else VS_NF |
| 127 | + } |
| 128 | + |
| 129 | + fun createInitializedExhaustively(isDeclared: Boolean): VariableControlFlowState = |
| 130 | + create(InitState.INITIALIZED_EXHAUSTIVELY, isDeclared) |
| 131 | + |
| 132 | + fun create(isInitialized: Boolean, isDeclared: Boolean = false): VariableControlFlowState = |
| 133 | + create(if (isInitialized) InitState.INITIALIZED else InitState.NOT_INITIALIZED, isDeclared) |
| 134 | + |
| 135 | + fun create(isDeclaredHere: Boolean, mergedEdgesData: VariableControlFlowState?): VariableControlFlowState = |
| 136 | + create(true, isDeclaredHere || mergedEdgesData != null && mergedEdgesData.isDeclared) |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +enum class VariableUseState(private val priority: Int) { |
| 141 | + READ(3), |
| 142 | + WRITTEN_AFTER_READ(2), |
| 143 | + ONLY_WRITTEN_NEVER_READ(1), |
| 144 | + UNUSED(0); |
| 145 | + |
| 146 | + fun merge(variableUseState: VariableUseState?): VariableUseState { |
| 147 | + if (variableUseState == null || priority > variableUseState.priority) return this |
| 148 | + return variableUseState |
| 149 | + } |
| 150 | + |
| 151 | + companion object { |
| 152 | + |
| 153 | + @JvmStatic |
| 154 | + fun isUsed(variableUseState: VariableUseState?): Boolean = |
| 155 | + variableUseState != null && variableUseState != UNUSED |
| 156 | + } |
| 157 | +} |
| 158 | + |
| 159 | +enum class InitState(private val s: String) { |
| 160 | + // Definitely initialized |
| 161 | + INITIALIZED("I"), |
| 162 | + // Fake initializer in else branch of "exhaustive when without else", see MagicKind.EXHAUSTIVE_WHEN_ELSE |
| 163 | + INITIALIZED_EXHAUSTIVELY("IE"), |
| 164 | + // Initialized in some branches, not initialized in other branches |
| 165 | + UNKNOWN("I?"), |
| 166 | + // Definitely not initialized |
| 167 | + NOT_INITIALIZED(""); |
| 168 | + |
| 169 | + fun merge(other: InitState): InitState { |
| 170 | + // X merge X = X |
| 171 | + // X merge IE = IE merge X = X |
| 172 | + // else X merge Y = I? |
| 173 | + if (this == other || other == INITIALIZED_EXHAUSTIVELY) return this |
| 174 | + if (this == INITIALIZED_EXHAUSTIVELY) return other |
| 175 | + return UNKNOWN |
| 176 | + } |
| 177 | + |
| 178 | + override fun toString() = s |
| 179 | +} |
| 180 | + |
| 181 | +// STATES END |
| 182 | + |
| 183 | +fun box(): String { |
| 184 | + val usageVariableControlFlowInfo: UsageVariableControlFlowInfo = UsageVariableControlFlowInfo( |
| 185 | + mapOf( |
| 186 | + "unused" to VariableUseState.UNUSED, |
| 187 | + "read" to VariableUseState.READ |
| 188 | + ) |
| 189 | + ) |
| 190 | + |
| 191 | + val usageVariableControlFlowInfoUpdated: UsageVariableControlFlowInfo = |
| 192 | + usageVariableControlFlowInfo.put("second unused", VariableUseState.UNUSED) |
| 193 | + |
| 194 | + |
| 195 | + val variableInitControlFlowInfo: VariableInitControlFlowInfo = VariableInitControlFlowInfo( |
| 196 | + mapOf( |
| 197 | + "VS_IT" to VariableControlFlowState.create(InitState.INITIALIZED, isDeclared = true), |
| 198 | + "VS_IF" to VariableControlFlowState.create(InitState.INITIALIZED, isDeclared = false) |
| 199 | + ) |
| 200 | + ) |
| 201 | + |
| 202 | + val updatedVariableInitControlFlowInfo = variableInitControlFlowInfo.put( |
| 203 | + "VS_ET", VariableControlFlowState.create(InitState.INITIALIZED_EXHAUSTIVELY, isDeclared = true) |
| 204 | + ) |
| 205 | + |
| 206 | + val predicate = usageVariableControlFlowInfoUpdated.containsKey("second unused") && updatedVariableInitControlFlowInfo.containsKey("VS_ET") |
| 207 | + |
| 208 | + return if (predicate) "OK" else "ERROR" |
| 209 | +} |
0 commit comments