[FIR] Make SimpleFlow (old implementation) implement abstract Flow

This commit is contained in:
Dmitriy Novozhilov
2019-09-10 10:56:11 +03:00
parent 60343c721c
commit 43a873a2ec
6 changed files with 178 additions and 298 deletions
@@ -50,6 +50,10 @@ class DelegatingFlow(
return result return result
} }
override fun removeConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo> {
return conditionalInfos.removeAll(variable)
}
private inline fun collect(block: (DelegatingFlow) -> Boolean) { private inline fun collect(block: (DelegatingFlow) -> Boolean) {
var flow: DelegatingFlow? = this var flow: DelegatingFlow? = this
while (flow != null) { while (flow != null) {
@@ -95,6 +99,12 @@ private class ImmutableMultimap<K, V>(private val original: Multimap<K, V>) : Mu
} }
abstract class DelegatingLogicSystem(context: DataFlowInferenceContext) : LogicSystem(context) { abstract class DelegatingLogicSystem(context: DataFlowInferenceContext) : LogicSystem(context) {
override val Flow.approvedInfos: MutableApprovedInfos
get() = (this as DelegatingFlow).approvedInfos
override val Flow.conditionalInfos: ConditionalInfos
get() = (this as DelegatingFlow).conditionalInfos
override fun createEmptyFlow(): Flow { override fun createEmptyFlow(): Flow {
return DelegatingFlow(null) return DelegatingFlow(null)
} }
@@ -104,11 +114,16 @@ abstract class DelegatingLogicSystem(context: DataFlowInferenceContext) : LogicS
return DelegatingFlow(flow) return DelegatingFlow(flow)
} }
override fun collectInfoForBooleanOperator(leftFlow: Flow, rightFlow: Flow): InfoForBooleanOperator { override fun collectInfoForBooleanOperator(
leftFlow: Flow,
leftVariable: DataFlowVariable,
rightFlow: Flow,
rightVariable: DataFlowVariable
): InfoForBooleanOperator {
require(leftFlow is DelegatingFlow && rightFlow is DelegatingFlow) require(leftFlow is DelegatingFlow && rightFlow is DelegatingFlow)
return InfoForBooleanOperator( return InfoForBooleanOperator(
leftFlow.previousFlow!!.conditionalInfosFromTopFlow(), leftFlow.previousFlow!!.conditionalInfosFromTopFlow()[leftVariable],
rightFlow.conditionalInfosFromTopFlow(), rightFlow.conditionalInfosFromTopFlow()[rightVariable],
rightFlow.approvedInfosFromTopFlow() rightFlow.approvedInfosFromTopFlow()
) )
} }
@@ -148,83 +163,6 @@ abstract class DelegatingLogicSystem(context: DataFlowInferenceContext) : LogicS
return commonFlow return commonFlow
} }
override fun changeVariableForConditionFlow(
flow: Flow,
sourceVariable: DataFlowVariable,
newVariable: DataFlowVariable,
transform: ((ConditionalFirDataFlowInfo) -> ConditionalFirDataFlowInfo)?
) {
require(flow is DelegatingFlow)
var infos = flow.getConditionalInfos(sourceVariable)
if (transform != null) {
infos = infos.map(transform)
}
flow.conditionalInfos.putAll(newVariable, infos)
if (sourceVariable.isSynthetic()) {
flow.conditionalInfos.removeAll(sourceVariable)
}
}
override fun approveFactsInsideFlow(
variable: DataFlowVariable,
condition: Condition,
flow: Flow,
shouldForkFlow: Boolean,
shouldRemoveSynthetics: Boolean
): Flow {
require(flow is DelegatingFlow)
val notApprovedFacts: Collection<ConditionalFirDataFlowInfo> = if (shouldRemoveSynthetics && variable.isSynthetic()) {
flow.conditionalInfos.removeAll(variable)
} else {
flow.getConditionalInfos(variable)
}
val resultFlow = if (shouldForkFlow) forkFlow(flow) else flow
if (notApprovedFacts.isEmpty()) {
return resultFlow
}
val newFacts = ArrayListMultimap.create<RealDataFlowVariable, FirDataFlowInfo>()
notApprovedFacts.forEach {
if (it.condition == condition) {
newFacts.put(it.variable, it.info)
}
}
val updatedReceivers = mutableSetOf<RealDataFlowVariable>()
newFacts.asMap().forEach { (variable, infos) ->
@Suppress("NAME_SHADOWING")
val info = MutableFirDataFlowInfo()
infos.forEach {
info += it
}
if (variable.isThisReference) {
updatedReceivers += variable
}
addApprovedInfo(resultFlow, variable, info)
}
updatedReceivers.forEach {
processUpdatedReceiverVariable(resultFlow, it)
}
return resultFlow
}
override fun addApprovedInfo(flow: Flow, variable: RealDataFlowVariable, info: FirDataFlowInfo) {
assert(info is MutableFirDataFlowInfo)
require(flow is DelegatingFlow)
flow.approvedInfos.addInfo(variable, info)
if (variable.isThisReference) {
processUpdatedReceiverVariable(flow, variable)
}
}
override fun addConditionalInfo(flow: Flow, variable: DataFlowVariable, info: ConditionalFirDataFlowInfo) {
require(flow is DelegatingFlow)
flow.conditionalInfos.put(variable, info)
}
// ------------------------------- Util functions ------------------------------- // ------------------------------- Util functions -------------------------------
private fun lowestCommonFlow(left: DelegatingFlow, right: DelegatingFlow): DelegatingFlow { private fun lowestCommonFlow(left: DelegatingFlow, right: DelegatingFlow): DelegatingFlow {
@@ -569,17 +569,16 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
val (conditionalFromLeft, conditionalFromRight, approvedFromRight) = logicSystem.collectInfoForBooleanOperator( val (conditionalFromLeft, conditionalFromRight, approvedFromRight) = logicSystem.collectInfoForBooleanOperator(
flowFromLeft, flowFromLeft,
flowFromRight leftVariable,
flowFromRight,
rightVariable
) )
val conditionalFromLeftArgument = conditionalFromLeft[leftVariable]
val conditionalFromRightArgument = conditionalFromRight[rightVariable]
// left && right == True // left && right == True
// left || right == False // left || right == False
val approvedIfTrue: MutableApprovedInfos = mutableMapOf() val approvedIfTrue: MutableApprovedInfos = mutableMapOf()
logicSystem.approveFactTo(approvedIfTrue, bothEvaluated, conditionalFromLeftArgument) logicSystem.approveFactTo(approvedIfTrue, bothEvaluated, conditionalFromLeft)
logicSystem.approveFactTo(approvedIfTrue, bothEvaluated, conditionalFromRightArgument) logicSystem.approveFactTo(approvedIfTrue, bothEvaluated, conditionalFromRight)
approvedFromRight.forEach { (variable, info) -> approvedFromRight.forEach { (variable, info) ->
approvedIfTrue.addInfo(variable, info) approvedIfTrue.addInfo(variable, info)
} }
@@ -590,8 +589,8 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
// left && right == False // left && right == False
// left || right == True // left || right == True
val approvedIfFalse: MutableApprovedInfos = mutableMapOf() val approvedIfFalse: MutableApprovedInfos = mutableMapOf()
val leftIsFalse = logicSystem.approveFact(onlyLeftEvaluated, conditionalFromLeftArgument) val leftIsFalse = logicSystem.approveFact(onlyLeftEvaluated, conditionalFromLeft)
val rightIsFalse = logicSystem.approveFact(onlyLeftEvaluated, conditionalFromRightArgument) val rightIsFalse = logicSystem.approveFact(onlyLeftEvaluated, conditionalFromRight)
approvedIfFalse.mergeInfo(logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse)) approvedIfFalse.mergeInfo(logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse))
approvedIfFalse.forEach { (variable, info) -> approvedIfFalse.forEach { (variable, info) ->
logicSystem.addConditionalInfo(flow, andVariable, info.toConditional(onlyLeftEvaluated, variable)) logicSystem.addConditionalInfo(flow, andVariable, info.toConditional(onlyLeftEvaluated, variable))
@@ -68,6 +68,7 @@ data class MutableFirDataFlowInfo(
exactNotType += info.exactNotType exactNotType += info.exactNotType
} }
fun copy(): MutableFirDataFlowInfo = MutableFirDataFlowInfo(exactType.toMutableSet(), exactNotType.toMutableSet())
} }
operator fun FirDataFlowInfo.plus(other: FirDataFlowInfo?): FirDataFlowInfo = other?.let { this + other } ?: this operator fun FirDataFlowInfo.plus(other: FirDataFlowInfo?): FirDataFlowInfo = other?.let { this + other } ?: this
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa package org.jetbrains.kotlin.fir.resolve.dfa
import com.google.common.collect.ArrayListMultimap
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
interface Flow { interface Flow {
@@ -12,17 +13,29 @@ interface Flow {
fun getConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo> fun getConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo>
fun getVariablesInApprovedInfos(): Collection<RealDataFlowVariable> fun getVariablesInApprovedInfos(): Collection<RealDataFlowVariable>
fun removeConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo>
} }
abstract class LogicSystem(private val context: DataFlowInferenceContext) { abstract class LogicSystem(private val context: DataFlowInferenceContext) {
// ------------------------------- Flow operations ------------------------------- // ------------------------------- Flow operations -------------------------------
abstract fun createEmptyFlow(): Flow abstract fun createEmptyFlow(): Flow
abstract fun forkFlow(flow: Flow): Flow abstract fun forkFlow(flow: Flow): Flow
abstract fun joinFlow(flows: Collection<Flow>): Flow abstract fun joinFlow(flows: Collection<Flow>): Flow
abstract fun addApprovedInfo(flow: Flow, variable: RealDataFlowVariable, info: FirDataFlowInfo) open fun addApprovedInfo(flow: Flow, variable: RealDataFlowVariable, info: FirDataFlowInfo) {
abstract fun addConditionalInfo(flow: Flow, variable: DataFlowVariable, info: ConditionalFirDataFlowInfo) assert(info is MutableFirDataFlowInfo)
flow.approvedInfos.addInfo(variable, info)
if (variable.isThisReference) {
processUpdatedReceiverVariable(flow, variable)
}
}
open fun addConditionalInfo(flow: Flow, variable: DataFlowVariable, info: ConditionalFirDataFlowInfo) {
flow.conditionalInfos.put(variable, info)
}
/* /*
* used for: * used for:
@@ -30,35 +43,90 @@ abstract class LogicSystem(private val context: DataFlowInferenceContext) {
* 2. b = x is String * 2. b = x is String
* 3. !b | b.not() for Booleans * 3. !b | b.not() for Booleans
*/ */
abstract fun changeVariableForConditionFlow( open fun changeVariableForConditionFlow(
flow: Flow, flow: Flow,
sourceVariable: DataFlowVariable, sourceVariable: DataFlowVariable,
newVariable: DataFlowVariable, newVariable: DataFlowVariable,
transform: ((ConditionalFirDataFlowInfo) -> ConditionalFirDataFlowInfo)? = null transform: ((ConditionalFirDataFlowInfo) -> ConditionalFirDataFlowInfo)? = null
) ) {
var infos = flow.getConditionalInfos(sourceVariable)
if (transform != null) {
infos = infos.map(transform)
}
flow.conditionalInfos.putAll(newVariable, infos)
if (sourceVariable.isSynthetic()) {
flow.conditionalInfos.removeAll(sourceVariable)
}
}
abstract fun approveFactsInsideFlow( open fun approveFactsInsideFlow(
variable: DataFlowVariable, variable: DataFlowVariable,
condition: Condition, condition: Condition,
flow: Flow, flow: Flow,
shouldForkFlow: Boolean, shouldForkFlow: Boolean,
shouldRemoveSynthetics: Boolean shouldRemoveSynthetics: Boolean
): Flow ): Flow {
val notApprovedFacts: Collection<ConditionalFirDataFlowInfo> = if (shouldRemoveSynthetics && variable.isSynthetic()) {
flow.removeConditionalInfos(variable)
} else {
flow.getConditionalInfos(variable)
}
val resultFlow = if (shouldForkFlow) forkFlow(flow) else flow
if (notApprovedFacts.isEmpty()) {
return resultFlow
}
val newFacts = ArrayListMultimap.create<RealDataFlowVariable, FirDataFlowInfo>()
notApprovedFacts.forEach {
if (it.condition == condition) {
newFacts.put(it.variable, it.info)
}
}
val updatedReceivers = mutableSetOf<RealDataFlowVariable>()
newFacts.asMap().forEach { (variable, infos) ->
@Suppress("NAME_SHADOWING")
val info = MutableFirDataFlowInfo()
infos.forEach {
info += it
}
if (variable.isThisReference) {
updatedReceivers += variable
}
addApprovedInfo(resultFlow, variable, info)
}
updatedReceivers.forEach {
processUpdatedReceiverVariable(resultFlow, it)
}
return resultFlow
}
// ------------------------------- Callbacks for updating implicit receiver stack ------------------------------- // ------------------------------- Callbacks for updating implicit receiver stack -------------------------------
abstract fun processUpdatedReceiverVariable(flow: Flow, variable: RealDataFlowVariable) abstract fun processUpdatedReceiverVariable(flow: Flow, variable: RealDataFlowVariable)
abstract fun updateAllReceivers(flow: Flow) abstract fun updateAllReceivers(flow: Flow)
// ------------------------------- Accessors to flow implementation -------------------------------
protected abstract val Flow.approvedInfos: MutableApprovedInfos
protected abstract val Flow.conditionalInfos: ConditionalInfos
// ------------------------------- Public DataFlowInfo util functions ------------------------------- // ------------------------------- Public DataFlowInfo util functions -------------------------------
data class InfoForBooleanOperator( data class InfoForBooleanOperator(
val conditionalFromLeft: ConditionalInfos, val conditionalFromLeft: Collection<ConditionalFirDataFlowInfo>,
val conditionalFromRight: ConditionalInfos, val conditionalFromRight: Collection<ConditionalFirDataFlowInfo>,
val approvedFromRight: ApprovedInfos val approvedFromRight: ApprovedInfos
) )
abstract fun collectInfoForBooleanOperator(leftFlow: Flow, rightFlow: Flow): InfoForBooleanOperator abstract fun collectInfoForBooleanOperator(
leftFlow: Flow,
leftVariable: DataFlowVariable,
rightFlow: Flow,
rightVariable: DataFlowVariable
): InfoForBooleanOperator
fun orForVerifiedFacts( fun orForVerifiedFacts(
left: ApprovedInfos, left: ApprovedInfos,
@@ -1,93 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve.dfa
import com.google.common.collect.HashMultimap
class SimpleFlow private constructor(
val approvedInfos: MutableMap<RealDataFlowVariable, FirDataFlowInfo>,
val conditionalInfos: HashMultimap<DataFlowVariable, ConditionalFirDataFlowInfo>,
private var state: State = State.Building
) {
constructor(
approvedInfos: MutableMap<RealDataFlowVariable, FirDataFlowInfo> = mutableMapOf(),
conditionalInfos: HashMultimap<DataFlowVariable, ConditionalFirDataFlowInfo> = HashMultimap.create()
) : this(approvedInfos, conditionalInfos, State.Building)
companion object {
val EMPTY = SimpleFlow(mutableMapOf(), HashMultimap.create(), State.Frozen)
}
private val isFrozen: Boolean get() = state == State.Frozen
fun freeze() {
state = State.Frozen
}
fun addApprovedFact(variable: RealDataFlowVariable, info: FirDataFlowInfo): SimpleFlow {
if (isFrozen) return copyForBuilding().addApprovedFact(variable, info)
approvedInfos.compute(variable) { _, existingInfo ->
if (existingInfo == null) info
else existingInfo + info
}
return this
}
fun addNotApprovedFact(variable: DataFlowVariable, info: ConditionalFirDataFlowInfo): SimpleFlow {
if (isFrozen) return copyForBuilding().addNotApprovedFact(variable, info)
conditionalInfos.put(variable, info)
return this
}
fun copyNotApprovedFacts(
from: DataFlowVariable,
to: DataFlowVariable,
transform: ((ConditionalFirDataFlowInfo) -> ConditionalFirDataFlowInfo)? = null
): SimpleFlow {
if (isFrozen) {
return copyForBuilding().copyNotApprovedFacts(from, to, transform)
}
var facts = if (from.isSynthetic()) {
conditionalInfos.removeAll(from)
} else {
conditionalInfos[from]
}
if (transform != null) {
facts = facts.mapTo(mutableSetOf(), transform)
}
conditionalInfos.putAll(to, facts)
return this
}
fun approvedFacts(variable: RealDataFlowVariable): FirDataFlowInfo? {
return approvedInfos[variable]
}
fun removeVariableFromFlow(variable: DataFlowVariable): SimpleFlow {
if (isFrozen) return copyForBuilding().removeVariableFromFlow(variable)
conditionalInfos.removeAll(variable)
approvedInfos.remove(variable)
return this
}
private enum class State {
Building, Frozen
}
fun copy(): SimpleFlow {
return when (state) {
State.Frozen -> this
State.Building -> copyForBuilding()
}
}
fun copyForBuilding(): SimpleFlow {
return SimpleFlow(approvedInfos.toMutableMap(), conditionalInfos.copy(), State.Building)
}
}
private fun <K, V> HashMultimap<K, V>.copy(): HashMultimap<K, V> = HashMultimap.create(this)
@@ -5,134 +5,101 @@
package org.jetbrains.kotlin.fir.resolve.dfa package org.jetbrains.kotlin.fir.resolve.dfa
import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.HashMultimap import com.google.common.collect.HashMultimap
import org.jetbrains.kotlin.fir.types.ConeKotlinType import com.google.common.collect.Multimap
class SimpleLogicSystem(private val context: DataFlowInferenceContext) { private class SimpleFlow(
private fun <E> List<Set<E>>.intersectSets(): Set<E> = takeIf { isNotEmpty() }?.reduce { x, y -> x.intersect(y) } ?: emptySet() val approvedInfos: MutableApprovedInfos = mutableMapOf(),
val conditionalInfos: ConditionalInfos = HashMultimap.create()
) : Flow {
override fun getApprovedInfo(variable: RealDataFlowVariable): FirDataFlowInfo? {
return approvedInfos[variable]
}
fun or(storages: Collection<SimpleFlow>): SimpleFlow { override fun getConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo> {
storages.singleOrNull()?.let { return conditionalInfos[variable]
return it }
}
val approvedFacts = mutableMapOf<RealDataFlowVariable, FirDataFlowInfo>() override fun getVariablesInApprovedInfos(): Collection<RealDataFlowVariable> {
storages.map { it.approvedInfos.keys } return approvedInfos.keys
}
override fun removeConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo> {
return conditionalInfos.removeAll(variable)
}
}
abstract class SimpleLogicSystem(context: DataFlowInferenceContext) : LogicSystem(context) {
override val Flow.approvedInfos: MutableApprovedInfos
get() = (this as SimpleFlow).approvedInfos
override val Flow.conditionalInfos: ConditionalInfos
get() = (this as SimpleFlow).conditionalInfos
override fun createEmptyFlow(): Flow {
return SimpleFlow()
}
override fun forkFlow(flow: Flow): Flow {
require(flow is SimpleFlow)
return SimpleFlow(
flow.approvedInfos.mapValuesTo(mutableMapOf()) { (_, info) -> info.copy() },
flow.conditionalInfos.copy()
)
}
override fun joinFlow(flows: Collection<Flow>): Flow {
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
val flows = flows as Collection<SimpleFlow>
flows.singleOrNull()?.let { return it }
val approvedFacts: MutableApprovedInfos = mutableMapOf()
flows.map { it.approvedInfos.keys }
.intersectSets() .intersectSets()
.forEach { variable -> .forEach { variable ->
val infos = storages.map { it.approvedInfos[variable]!! } val infos = flows.map { it.approvedInfos[variable]!! }
if (infos.isNotEmpty()) { if (infos.isNotEmpty()) {
approvedFacts[variable] = or(infos) approvedFacts[variable] = or(infos)
} }
} }
val notApprovedFacts: ConditionalInfos = ArrayListMultimap.create()
val notApprovedFacts = HashMultimap.create<DataFlowVariable, ConditionalFirDataFlowInfo>() flows.map { it.conditionalInfos.keySet() }
storages.map { it.conditionalInfos.keySet() }
.intersectSets() .intersectSets()
.forEach { variable -> .forEach { variable ->
val infos = storages.map { it.conditionalInfos[variable] }.intersectSets() val infos = flows.map { it.conditionalInfos[variable] }.intersectSets()
if (infos.isNotEmpty()) { if (infos.isNotEmpty()) {
notApprovedFacts.putAll(variable, infos) notApprovedFacts.putAll(variable, infos)
} }
} }
return SimpleFlow(approvedFacts, notApprovedFacts) val result = SimpleFlow(approvedFacts, notApprovedFacts)
updateAllReceivers(result)
return result
} }
fun andForVerifiedFacts( override fun collectInfoForBooleanOperator(
left: Map<RealDataFlowVariable, FirDataFlowInfo>?, leftFlow: Flow,
right: Map<RealDataFlowVariable, FirDataFlowInfo>? leftVariable: DataFlowVariable,
): Map<RealDataFlowVariable, FirDataFlowInfo>? { rightFlow: Flow,
if (left.isNullOrEmpty()) return right rightVariable: DataFlowVariable
if (right.isNullOrEmpty()) return left ): InfoForBooleanOperator {
require(leftFlow is SimpleFlow && rightFlow is SimpleFlow)
val map = mutableMapOf<RealDataFlowVariable, FirDataFlowInfo>() return InfoForBooleanOperator(
for (variable in left.keys.union(right.keys)) { leftFlow.conditionalInfos[leftVariable],
val leftInfo = left[variable] rightFlow.conditionalInfos[rightVariable],
val rightInfo = right[variable] rightFlow.approvedInfos - leftFlow.approvedInfos
map[variable] = and(listOfNotNull(leftInfo, rightInfo)) )
}
return map
} }
fun orForVerifiedFacts( private operator fun ApprovedInfos.minus(other: ApprovedInfos): ApprovedInfos {
left: Map<RealDataFlowVariable, FirDataFlowInfo>?, val result: MutableApprovedInfos = mutableMapOf()
right: Map<RealDataFlowVariable, FirDataFlowInfo>? forEach { (variable, info) ->
): Map<RealDataFlowVariable, FirDataFlowInfo>? { require(info is MutableFirDataFlowInfo)
if (left.isNullOrEmpty() || right.isNullOrEmpty()) return null result[variable] = other[variable]?.let { info - it } ?: info
val map = mutableMapOf<RealDataFlowVariable, FirDataFlowInfo>()
for (variable in left.keys.intersect(right.keys)) {
val leftInfo = left[variable]!!
val rightInfo = right[variable]!!
map[variable] = or(listOf(leftInfo, rightInfo))
} }
return map return result
}
fun approveFactsInsideFlow(variable: DataFlowVariable, condition: Condition, flow: SimpleFlow): Pair<SimpleFlow, Collection<RealDataFlowVariable>> {
val notApprovedFacts: Set<ConditionalFirDataFlowInfo> = flow.conditionalInfos[variable]
if (notApprovedFacts.isEmpty()) {
return flow to emptyList()
}
@Suppress("NAME_SHADOWING")
val flow = flow.copyForBuilding()
val newFacts = HashMultimap.create<RealDataFlowVariable, FirDataFlowInfo>()
notApprovedFacts.forEach {
if (it.condition == condition) {
newFacts.put(it.variable, it.info)
}
}
val updatedReceivers = mutableSetOf<RealDataFlowVariable>()
newFacts.asMap().forEach { (variable, infos) ->
@Suppress("NAME_SHADOWING")
val infos = ArrayList(infos)
flow.approvedInfos[variable]?.let {
infos.add(it)
}
flow.approvedInfos[variable] = and(infos)
if (variable.isThisReference) {
updatedReceivers += variable
}
}
return flow to updatedReceivers
}
fun approveFact(variable: DataFlowVariable, condition: Condition, flow: SimpleFlow): MutableMap<RealDataFlowVariable, FirDataFlowInfo> {
val notApprovedFacts: Set<ConditionalFirDataFlowInfo> = flow.conditionalInfos[variable]
if (notApprovedFacts.isEmpty()) {
return mutableMapOf()
}
val newFacts = HashMultimap.create<RealDataFlowVariable, FirDataFlowInfo>()
notApprovedFacts.forEach {
if (it.condition == condition) {
newFacts.put(it.variable, it.info)
}
}
return newFacts.asMap().mapValuesTo(mutableMapOf()) { (_, infos) -> and(infos) }
}
private fun or(infos: Collection<FirDataFlowInfo>): FirDataFlowInfo {
infos.singleOrNull()?.let { return it }
val exactType = orTypes(infos.map { it.exactType })
val exactNotType = orTypes(infos.map { it.exactNotType })
return FirDataFlowInfo(exactType, exactNotType)
}
private fun orTypes(types: Collection<Set<ConeKotlinType>>): Set<ConeKotlinType> {
if (types.any { it.isEmpty() }) return emptySet()
val allTypes = types.flatMapTo(mutableSetOf()) { it }
val commonTypes = allTypes.toMutableSet()
types.forEach { commonTypes.retainAll(it) }
val differentTypes = allTypes - commonTypes
context.commonSuperTypeOrNull(differentTypes.toList())?.let { commonTypes += it }
return commonTypes
}
private fun and(infos: Collection<FirDataFlowInfo>): FirDataFlowInfo {
infos.singleOrNull()?.let { return it }
val exactType = infos.flatMapTo(mutableSetOf()) { it.exactType }
val exactNotType = infos.flatMapTo(mutableSetOf()) { it.exactNotType }
return FirDataFlowInfo(exactType, exactNotType)
} }
} }
private fun <K, V> Multimap<K, V>.copy(): Multimap<K, V> = ArrayListMultimap.create(this)