diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DataFlowVariable.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DataFlowVariable.kt deleted file mode 100644 index 226208c3646..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DataFlowVariable.kt +++ /dev/null @@ -1,141 +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 org.jetbrains.kotlin.fir.FirElement -import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.contract - -/* - * isSynthetic = false for variables that represents actual variables in fir - * isSynthetic = true for complex expressions (like when expression) - */ -sealed class DataFlowVariable(private val variableIndexForDebug: Int, val fir: FirElement) { - final override fun toString(): String { - return "d$variableIndexForDebug" - } -} - -open class RealDataFlowVariable(index: Int, fir: FirElement, val isThisReference: Boolean) : DataFlowVariable(index, fir) - -class SyntheticDataFlowVariable(index: Int, fir: FirElement) : DataFlowVariable(index, fir) - -class AliasedDataFlowVariable( - index: Int, - fir: FirElement, - var delegate: RealDataFlowVariable -) : RealDataFlowVariable(index, fir, delegate.isThisReference) - -// ------------------------------------------------------------------------------------------------------------------------- - -@UseExperimental(ExperimentalContracts::class) -fun DataFlowVariable.isSynthetic(): Boolean { - contract { - returns(true) implies (this@isSynthetic is SyntheticDataFlowVariable) - } - return this is SyntheticDataFlowVariable -} - -@UseExperimental(ExperimentalContracts::class) -fun DataFlowVariable.isAliasVariable(): Boolean { - contract { - returns(true) implies (this@isAliasVariable is AliasedDataFlowVariable) - } - return this is AliasedDataFlowVariable -} - -val RealDataFlowVariable.variableUnderAlias: RealDataFlowVariable - get() { - var variable = this - while (variable.isAliasVariable()) { - variable = variable.delegate - } - return variable - } - -// ------------------------------------------------------------------------------------------------------------------------- - -class DataFlowVariableStorage { - private val fir2DfiMap: MutableMap = mutableMapOf() - private var debugIndexCounter: Int = 1 - - fun getOrCreateNewRealVariable(symbol: AbstractFirBasedSymbol<*>): RealDataFlowVariable { - return getOrCreateNewRealVariableImpl(symbol, false) - } - - fun getOrCreateNewThisRealVariable(symbol: AbstractFirBasedSymbol<*>): RealDataFlowVariable { - return getOrCreateNewRealVariableImpl(symbol, true) - } - - private fun getOrCreateNewRealVariableImpl(symbol: AbstractFirBasedSymbol<*>, isThisReference: Boolean): RealDataFlowVariable { - val fir = symbol.fir - get(fir)?.let { return it as RealDataFlowVariable } - return RealDataFlowVariable(debugIndexCounter++, fir, isThisReference).also { storeVariable(it, fir) } - } - - fun getOrCreateNewSyntheticVariable(fir: FirElement): SyntheticDataFlowVariable { - get(fir)?.let { return it as SyntheticDataFlowVariable } - return SyntheticDataFlowVariable(debugIndexCounter++, fir).also { storeVariable(it, fir) } - } - - fun createAliasVariable(symbol: AbstractFirBasedSymbol<*>, variable: RealDataFlowVariable) { - createAliasVariable(symbol.fir, variable) - } - - private fun createAliasVariable(fir: FirElement, variable: RealDataFlowVariable) { - AliasedDataFlowVariable(debugIndexCounter++, fir, variable).also { storeVariable(it, fir) } - } - - fun rebindAliasVariable(aliasVariable: RealDataFlowVariable, newVariable: RealDataFlowVariable) { - val fir = removeVariable(aliasVariable) - requireNotNull(fir) - createAliasVariable(fir, newVariable) - } - - fun removeRealVariable(symbol: AbstractFirBasedSymbol<*>) { - removeSyntheticVariable(symbol.fir) - } - - fun removeSyntheticVariable(fir: FirElement) { - val variable = fir2DfiMap[fir] ?: return - removeVariable(variable) - } - - fun removeVariable(variable: DataFlowVariable): FirElement? { - return variable.fir.also { - fir2DfiMap.remove(it) - } - } - - fun removeVariableIfSynthetic(variable: DataFlowVariable): FirElement? { - if (variable is SyntheticDataFlowVariable) { - return removeVariable(variable) - } - return null - } - - operator fun get(variable: DataFlowVariable): FirElement? { - return variable.fir - } - - operator fun get(firElement: FirElement): DataFlowVariable? { - return fir2DfiMap[firElement] - } - - operator fun get(symbol: AbstractFirBasedSymbol<*>): RealDataFlowVariable? { - return fir2DfiMap[symbol.fir] as RealDataFlowVariable? - } - - fun reset() { - fir2DfiMap.clear() - debugIndexCounter = 1 - } - - private fun storeVariable(variable: DataFlowVariable, fir: FirElement) { - fir2DfiMap[fir] = variable - } -} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DelegatingLogicSystem.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DelegatingLogicSystem.kt deleted file mode 100644 index e05b1cf56f1..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DelegatingLogicSystem.kt +++ /dev/null @@ -1,232 +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.ArrayListMultimap -import com.google.common.collect.Multimap -import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext - -class DelegatingFlow( - val previousFlow: DelegatingFlow?, - val approvedInfos: MutableApprovedInfos = mutableMapOf(), - val conditionalInfos: ConditionalInfos = ArrayListMultimap.create() -) : Flow { - val level: Int = previousFlow?.level?.plus(1) ?: 0 - - override fun getApprovedInfo(variable: RealDataFlowVariable): FirDataFlowInfo? { - val info = MutableFirDataFlowInfo(mutableSetOf(), mutableSetOf()) - collect { flow -> - flow.approvedInfos[variable]?.let { info += it } - true - } - return info.takeIf { it.isNotEmpty } - } - - override fun getConditionalInfos(variable: DataFlowVariable): Collection { - val result = mutableSetOf() - val isReal = !variable.isSynthetic() - collect { - val infos = it.conditionalInfos[variable] - result += infos - /* - * Here we use invariant that if we found some conditional info about synthetic variable - * on some level then there is definitely no another info on other levels of flow - * It's correct because we can add condtional info about synthetic variable only - * when we first met expression corresponding to that variable - */ - isReal || infos.isEmpty() - } - return result - } - - override fun getVariablesInApprovedInfos(): Collection { - val result = mutableSetOf() - collect { - result += approvedInfos.keys - true - } - return result - } - - override fun removeConditionalInfos(variable: DataFlowVariable): Collection { - return conditionalInfos.removeAll(variable) - } - - private inline fun collect(block: (DelegatingFlow) -> Boolean) { - var flow: DelegatingFlow? = this - while (flow != null) { - val shouldContinue = block(flow) - if (!shouldContinue) break - flow = flow.previousFlow - } - } -} - -fun DelegatingFlow.approvedInfosFromTopFlow(): ApprovedInfos { - return approvedInfos -} - -fun DelegatingFlow.conditionalInfosFromTopFlow(): ConditionalInfos { - return ImmutableMultimap(conditionalInfos) -} - -private fun DelegatingFlow.approvedInfosUntilParent(parent: DelegatingFlow): ApprovedInfos { - return when(level - parent.level) { - 0 -> emptyMap() - 1 -> approvedInfosFromTopFlow() - else -> mutableMapOf().apply { - traverse(parent) { - it.approvedInfosFromTopFlow().forEach { (variable, info) -> - compute(variable) { _, existingInfo -> - existingInfo?.plus(info) ?: info - } - } - } - } - } -} - -private inline fun DelegatingFlow.traverse(untilFlow: DelegatingFlow, block: (DelegatingFlow) -> Unit) { - var flow = this - while (flow != untilFlow) { - block(flow) - flow = flow.previousFlow!! - } -} - -private class ImmutableMultimap(private val original: Multimap) : Multimap by original { - override fun put(p0: K?, p1: V?): Boolean { - throw IllegalStateException() - } - - override fun remove(p0: Any?, p1: Any?): Boolean { - throw IllegalStateException() - } - - override fun putAll(p0: Multimap): Boolean { - throw IllegalStateException() - } - - override fun putAll(p0: K?, p1: MutableIterable): Boolean { - throw IllegalStateException() - } - - override fun removeAll(p0: Any?): MutableCollection { - throw IllegalStateException() - } - - override fun replaceValues(p0: K?, p1: MutableIterable): MutableCollection { - throw IllegalStateException() - } -} - -abstract class DelegatingLogicSystem(context: ConeInferenceContext) : 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 { - return DelegatingFlow(null) - } - - override fun forkFlow(flow: Flow): Flow { - require(flow is DelegatingFlow) - return DelegatingFlow(flow) - } - - override fun collectInfoForBooleanOperator( - leftFlow: Flow, - leftVariable: DataFlowVariable, - rightFlow: Flow, - rightVariable: DataFlowVariable - ): InfoForBooleanOperator { - require(leftFlow is DelegatingFlow && rightFlow is DelegatingFlow) - val parent = lowestCommonFlow(leftFlow, rightFlow) - - return InfoForBooleanOperator( - leftFlow.previousFlow!!.conditionalInfosFromTopFlow()[leftVariable], - rightFlow.conditionalInfosFromTopFlow()[rightVariable], - rightFlow.approvedInfosUntilParent(parent) - ) - } - - // ------------------------------- Flow operations ------------------------------- - - override fun joinFlow(flows: Collection): Flow { - if (flows.isEmpty()) return createEmptyFlow() - flows.singleOrNull()?.let { return it } - - @Suppress("UNCHECKED_CAST", "NAME_SHADOWING") - val flows = flows as Collection - val commonFlow = flows.reduce(this::lowestCommonFlow) - - val approvedInfosFromAllFlows = mutableListOf() - flows.forEach { - approvedInfosFromAllFlows += it.collectInfos(commonFlow) - } - - // approved info - val variablesFromApprovedInfos = approvedInfosFromAllFlows.map { it.keys }.intersectSets() - for (variable in variablesFromApprovedInfos) { - val infos = approvedInfosFromAllFlows.mapNotNull { it[variable] } - if (infos.size != flows.size) continue - val intersectedInfo = or(infos) - if (intersectedInfo.isEmpty) continue - val existingInfo = commonFlow.approvedInfos[variable] - if (existingInfo == null) { - commonFlow.approvedInfos[variable] = intersectedInfo - } else { - existingInfo += intersectedInfo - } - } - - updateAllReceivers(commonFlow) - - return commonFlow - } - - // ------------------------------- Util functions ------------------------------- - - private fun lowestCommonFlow(left: DelegatingFlow, right: DelegatingFlow): DelegatingFlow { - val level = minOf(left.level, right.level) - @Suppress("NAME_SHADOWING") - var left = left - while (left.level > level) { - left = left.previousFlow!! - } - @Suppress("NAME_SHADOWING") - var right = right - while (right.level > level) { - right = right.previousFlow!! - } - while (left != right) { - left = left.previousFlow!! - right = right.previousFlow!! - } - return left - } - - private fun DelegatingFlow.collectInfos(untilFlow: DelegatingFlow): ApprovedInfos { - val approvedInfos: MutableApprovedInfos = mutableMapOf() - - // val conditionalInfos: ConditionalInfos = ArrayListMultimap.create() - - var flow = this - while (flow != untilFlow) { - flow.approvedInfos.forEach { (variable, info) -> approvedInfos.addInfo(variable, info) } - /* - * we don't join conditional infos yet - flow.conditionalInfos.asMap().forEach { (variable, infos) -> - conditionalInfos.putAll(variable, infos) - } - */ - flow = flow.previousFlow!! - } - return approvedInfos - } -} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt deleted file mode 100644 index 1ad6a7e33aa..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ /dev/null @@ -1,920 +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 org.jetbrains.kotlin.fir.* -import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference -import org.jetbrains.kotlin.fir.contracts.description.ConeConditionalEffectDeclaration -import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference -import org.jetbrains.kotlin.fir.contracts.description.ConeReturnsEffectDeclaration -import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.expressions.impl.FirThisReceiverExpressionImpl -import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference -import org.jetbrains.kotlin.fir.references.impl.FirExplicitThisReference -import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents -import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStackImpl -import org.jetbrains.kotlin.fir.resolve.ResolutionMode -import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext -import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate -import org.jetbrains.kotlin.fir.resolve.dfa.Condition.* -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* -import org.jetbrains.kotlin.fir.resolve.dfa.contracts.buildContractFir -import org.jetbrains.kotlin.fir.resolve.dfa.contracts.createArgumentsMapping -import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer -import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType -import org.jetbrains.kotlin.fir.resolve.withNullability -import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.CallableId -import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol -import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.fir.visitors.transformSingle -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.utils.addIfNotNull -import org.jetbrains.kotlin.utils.addToStdlib.safeAs - -class FirDataFlowAnalyzer(private val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents) : BodyResolveComponents by components { - companion object { - private val KOTLIN_BOOLEAN_NOT = CallableId(FqName("kotlin"), FqName("Boolean"), Name.identifier("not")) - } - - private val context: ConeInferenceContext get() = inferenceComponents.ctx - private val receiverStack: ImplicitReceiverStackImpl = components.implicitReceiverStack as ImplicitReceiverStackImpl - - private val graphBuilder = ControlFlowGraphBuilder() - private val logicSystem: LogicSystem = LogicSystemImpl(context) - private val variableStorage = DataFlowVariableStorage() - private val flowOnNodes = mutableMapOf, Flow>() - - private val variablesForWhenConditions = mutableMapOf() - - private var contractDescriptionVisitingMode = false - - /* - * If there is no types from smartcasts function returns null - * - * Note that return value based on state of DataFlowAnalyzer (despite of stateless model of old frontend) - */ - fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection? { - /* - * DataFlowAnalyzer holds variables only for declarations that have some smartcast (or can have) - * If there is no useful information there is no data flow variable also - */ - val variable = qualifiedAccessExpression.realVariable?.variableUnderAlias ?: return null - return graphBuilder.lastNode.flow.getApprovedInfo(variable)?.exactType ?: return null - } - - // ----------------------------------- Named function ----------------------------------- - - fun enterFunction(function: FirFunction<*>) { - val (functionEnterNode, previousNode) = graphBuilder.enterFunction(function) - if (previousNode == null) { - functionEnterNode.mergeIncomingFlow() - } else { - assert(functionEnterNode.previousNodes.isEmpty()) - functionEnterNode.flow = logicSystem.forkFlow(previousNode.flow) - } - } - - fun exitFunction(function: FirFunction<*>): ControlFlowGraph? { - val (node, graph) = graphBuilder.exitFunction(function) - if (function.body == null) { - node.mergeIncomingFlow() - } - for (valueParameter in function.valueParameters) { - variableStorage.removeRealVariable(valueParameter.symbol) - } - if (graphBuilder.isTopLevel()) { - flowOnNodes.clear() - variableStorage.reset() - graphBuilder.reset() - } - return graph - } - - // ----------------------------------- Property ----------------------------------- - - fun enterProperty(property: FirProperty) { - graphBuilder.enterProperty(property).mergeIncomingFlow() - } - - fun exitProperty(property: FirProperty): ControlFlowGraph { - val (node, graph) = graphBuilder.exitProperty(property) - node.mergeIncomingFlow() - return graph - } - - // ----------------------------------- Block ----------------------------------- - - fun enterBlock(block: FirBlock) { - graphBuilder.enterBlock(block)?.mergeIncomingFlow() - } - - fun exitBlock(block: FirBlock) { - graphBuilder.exitBlock(block).mergeIncomingFlow() - } - - private fun FirElement.extractReturnType(target: FirFunction<*>?): ConeKotlinType? { - return when (this) { - is FirReturnExpression -> { - if (this.target.labeledElement == target) { - result.extractReturnType(target) - } else { - result.resultType.coneTypeSafe() - } - } - is FirExpression -> { - typeRef.coneTypeSafe() - } - else -> session.builtinTypes.unitType.type - } - } - - fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): List { - return graphBuilder.returnExpressionsOfAnonymousFunction(function) - } - - // ----------------------------------- Operator call ----------------------------------- - - fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) { - val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow() - - if (typeOperatorCall.operation !in FirOperation.TYPES) return - val type = typeOperatorCall.conversionTypeRef.coneTypeSafe() ?: return - val operandVariable = getOrCreateRealVariable(typeOperatorCall.argument) ?: return - - val flow = node.flow - when (typeOperatorCall.operation) { - FirOperation.IS, FirOperation.NOT_IS -> { - val expressionVariable = getOrCreateSyntheticVariable(typeOperatorCall) - - val trueInfo = FirDataFlowInfo(setOf(type), emptySet()) - val falseInfo = FirDataFlowInfo(emptySet(), setOf(type)) - - fun chooseInfo(trueBranch: Boolean) = - if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) trueInfo else falseInfo - - logicSystem.addConditionalInfo( - flow, - expressionVariable, - ConditionalFirDataFlowInfo( - EqTrue, operandVariable, chooseInfo(true) - ) - ) - - logicSystem.addConditionalInfo( - flow, - expressionVariable, - ConditionalFirDataFlowInfo( - EqFalse, operandVariable, chooseInfo(false) - ) - ) - } - - FirOperation.AS -> { - logicSystem.addApprovedInfo(flow, operandVariable, FirDataFlowInfo(setOf(type), emptySet())) - } - - FirOperation.SAFE_AS -> { - val expressionVariable = getOrCreateSyntheticVariable(typeOperatorCall) - logicSystem.addConditionalInfo( - flow, - expressionVariable, - ConditionalFirDataFlowInfo( - NotEqNull, operandVariable, FirDataFlowInfo(setOf(type), emptySet()) - ) - ) - logicSystem.addConditionalInfo( - flow, - expressionVariable, - ConditionalFirDataFlowInfo( - EqNull, operandVariable, FirDataFlowInfo(emptySet(), setOf(type)) - ) - ) - } - - else -> throw IllegalStateException() - } - - node.flow = flow - } - - fun exitOperatorCall(operatorCall: FirOperatorCall) { - val node = graphBuilder.exitOperatorCall(operatorCall).mergeIncomingFlow() - when (val operation = operatorCall.operation) { - FirOperation.EQ, FirOperation.NOT_EQ, FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> { - val leftOperand = operatorCall.arguments[0] - val rightOperand = operatorCall.arguments[1] - - val leftConst = leftOperand as? FirConstExpression<*> - val rightConst = rightOperand as? FirConstExpression<*> - - when { - leftConst?.kind == FirConstKind.Null -> processEqNull(node, rightOperand, operation) - rightConst?.kind == FirConstKind.Null -> processEqNull(node, leftOperand, operation) - leftConst != null -> processEqWithConst(node, rightOperand, leftConst, operation) - rightConst != null -> processEqWithConst(node, leftOperand, rightConst, operation) - operation != FirOperation.EQ && operation != FirOperation.IDENTITY -> return - else -> processEq(node, leftOperand, rightOperand, operation) - } - } - } - } - - private fun processEqWithConst(node: OperatorCallNode, operand: FirExpression, const: FirConstExpression<*>, operation: FirOperation) { - val isEq = when (operation) { - FirOperation.EQ, FirOperation.IDENTITY -> true - FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> false - else -> return - } - - val expressionVariable = getOrCreateVariable(node.fir) - val flow = node.flow - - // not null for comparisons with constants - getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables -> - operandVariables.forEach { operandVariable -> - logicSystem.addConditionalInfo( - flow, - expressionVariable, ConditionalFirDataFlowInfo( - isEq.toEqBoolean(), - operandVariable, - FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet()) - ) - ) - } - } - - // propagating facts for (... == true) and (... == false) - variableStorage[operand]?.let { operandVariable -> - if (const.kind != FirConstKind.Boolean) return@let - - val constValue = (const.value as Boolean) - val shouldInvert = isEq xor constValue - - flow.getConditionalInfos(operandVariable).forEach { info -> - logicSystem.addConditionalInfo(flow, expressionVariable, info.let { if (shouldInvert) it.invert() else it }) - } - } - } - - private fun processEq(node: OperatorCallNode, leftOperand: FirExpression, rightOperand: FirExpression, operation: FirOperation) { - val leftType = leftOperand.typeRef.coneTypeSafe() ?: return - val rightType = rightOperand.typeRef.coneTypeSafe() ?: return - when { - leftType.isMarkedNullable && rightType.isMarkedNullable -> return - leftType.isMarkedNullable -> processEqNull(node, leftOperand, FirOperation.NOT_EQ) - rightType.isMarkedNullable -> processEqNull(node, rightOperand, FirOperation.NOT_EQ) - } - // TODO: process EQUALITY - } - - private fun processEqNull(node: OperatorCallNode, operand: FirExpression, operation: FirOperation) { - val flow = node.flow - val expressionVariable = getOrCreateVariable(node.fir) - - variableStorage[operand]?.let { operandVariable -> - val condition = when (operation) { - FirOperation.EQ, FirOperation.IDENTITY -> EqNull - FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> NotEqNull - else -> throw IllegalArgumentException() - } - val facts = logicSystem.approveFact(operandVariable, condition, flow) - facts.forEach { (variable, info) -> - logicSystem.addConditionalInfo( - flow, - expressionVariable, - ConditionalFirDataFlowInfo( - EqTrue, variable, info - ) - ) - logicSystem.addConditionalInfo( - flow, - expressionVariable, - ConditionalFirDataFlowInfo( - EqFalse, variable, info.invert() - ) - ) - } - node.flow = flow - return - } - - val operandVariables = getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() } ?: return - - val condition = when (operation) { - FirOperation.EQ, FirOperation.IDENTITY -> EqFalse - FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> EqTrue - else -> throw IllegalArgumentException() - } - - operandVariables.forEach { operandVariable -> - logicSystem.addConditionalInfo( - flow, - expressionVariable, ConditionalFirDataFlowInfo( - condition, - operandVariable, - FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet()) - ) - ) - // TODO: design do we need casts to Nothing? - /* - flow = addConditionalInfo( - expressionVariable, UnapprovedFirDataFlowInfo( - eq(conditionValue.invert()!!), - operandVariable, - FirDataFlowInfo(setOf(session.builtinTypes.nullableNothingType.coneTypeUnsafe()), emptySet()) - ) - ) - */ - - } - } - - // ----------------------------------- Jump ----------------------------------- - - fun exitJump(jump: FirJump<*>) { - graphBuilder.exitJump(jump).mergeIncomingFlow() - } - - // ----------------------------------- Check not null call ----------------------------------- - - fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall) { - // Add `Any` to the set of possible types; the intersection type `T? & Any` will be reduced to `T` after smartcast. - val node = graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow() - val operandVariable = getOrCreateRealVariable(checkNotNullCall.argument) ?: return - logicSystem.addApprovedInfo( - node.flow, - operandVariable, - FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet()) - ) - } - - // ----------------------------------- When ----------------------------------- - - fun enterWhenExpression(whenExpression: FirWhenExpression) { - graphBuilder.enterWhenExpression(whenExpression).mergeIncomingFlow() - } - - fun enterWhenBranchCondition(whenBranch: FirWhenBranch) { - val node = graphBuilder.enterWhenBranchCondition(whenBranch).mergeIncomingFlow() - val previousNode = node.previousNodes.single() - if (previousNode is WhenBranchConditionExitNode) { - node.flow = logicSystem.approveFactsInsideFlow( - variablesForWhenConditions.remove(previousNode)!!, - EqFalse, - node.flow, - shouldForkFlow = true, - shouldRemoveSynthetics = true - ) - } - } - - fun exitWhenBranchCondition(whenBranch: FirWhenBranch) { - val (conditionExitNode, branchEnterNode) = graphBuilder.exitWhenBranchCondition(whenBranch) - conditionExitNode.mergeIncomingFlow() - - val conditionVariable = getOrCreateVariable(whenBranch.condition) - variablesForWhenConditions[conditionExitNode] = conditionVariable - branchEnterNode.flow = logicSystem.approveFactsInsideFlow( - conditionVariable, - EqTrue, - conditionExitNode.flow, - shouldForkFlow = true, - shouldRemoveSynthetics = false - ) - } - - fun exitWhenBranchResult(whenBranch: FirWhenBranch) { - graphBuilder.exitWhenBranchResult(whenBranch).mergeIncomingFlow() - } - - fun exitWhenExpression(whenExpression: FirWhenExpression) { - val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression) - if (syntheticElseNode != null) { - syntheticElseNode.mergeIncomingFlow() - val previousConditionExitNode = syntheticElseNode.previousNodes.single() as? WhenBranchConditionExitNode - // previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches - // in that case there will be when enter or subject access node - if (previousConditionExitNode != null) { - syntheticElseNode.flow = logicSystem.approveFactsInsideFlow( - variablesForWhenConditions.remove(previousConditionExitNode)!!, - EqFalse, - syntheticElseNode.flow, - shouldForkFlow = true, - shouldRemoveSynthetics = true - ) - } - - } - val previousFlows = whenExitNode.alivePreviousNodes.map { it.flow } - val flow = logicSystem.joinFlow(previousFlows) - whenExitNode.flow = flow - // TODO - // val subjectSymbol = whenExpression.subjectVariable?.symbol - // if (subjectSymbol != null) { - // variableStorage[subjectSymbol]?.let { flow = flow.removeVariable(it) } - // } - // node.flow = flow - } - - // ----------------------------------- While Loop ----------------------------------- - - fun enterWhileLoop(loop: FirLoop) { - val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop) - loopEnterNode.mergeIncomingFlow() - loopConditionEnterNode.mergeIncomingFlow() - } - - fun exitWhileLoopCondition(loop: FirLoop) { - val (loopConditionExitNode, loopBlockEnterNode) = graphBuilder.exitWhileLoopCondition(loop) - loopConditionExitNode.mergeIncomingFlow() - loopBlockEnterNode.mergeIncomingFlow() - variableStorage[loop.condition]?.let { conditionVariable -> - loopBlockEnterNode.flow = logicSystem.approveFactsInsideFlow( - conditionVariable, - EqTrue, - loopBlockEnterNode.flow, - shouldForkFlow = false, - shouldRemoveSynthetics = true - ) - } - } - - fun exitWhileLoop(loop: FirLoop) { - val (blockExitNode, exitNode) = graphBuilder.exitWhileLoop(loop) - blockExitNode.mergeIncomingFlow() - exitNode.mergeIncomingFlow() - } - - // ----------------------------------- Do while Loop ----------------------------------- - - fun enterDoWhileLoop(loop: FirLoop) { - val (loopEnterNode, loopBlockEnterNode) = graphBuilder.enterDoWhileLoop(loop) - loopEnterNode.mergeIncomingFlow() - loopBlockEnterNode.mergeIncomingFlow() - } - - fun enterDoWhileLoopCondition(loop: FirLoop) { - val (loopBlockExitNode, loopConditionEnterNode) = graphBuilder.enterDoWhileLoopCondition(loop) - loopBlockExitNode.mergeIncomingFlow() - loopConditionEnterNode.mergeIncomingFlow() - } - - fun exitDoWhileLoop(loop: FirLoop) { - val (loopConditionExitNode, loopExitNode) = graphBuilder.exitDoWhileLoop(loop) - loopConditionExitNode.mergeIncomingFlow() - loopExitNode.mergeIncomingFlow() - } - - // ----------------------------------- Try-catch-finally ----------------------------------- - - fun enterTryExpression(tryExpression: FirTryExpression) { - val (tryExpressionEnterNode, tryMainBlockEnterNode) = graphBuilder.enterTryExpression(tryExpression) - tryExpressionEnterNode.mergeIncomingFlow() - tryMainBlockEnterNode.mergeIncomingFlow() - } - - fun exitTryMainBlock(tryExpression: FirTryExpression) { - graphBuilder.exitTryMainBlock(tryExpression).mergeIncomingFlow() - } - - fun enterCatchClause(catch: FirCatch) { - graphBuilder.enterCatchClause(catch).mergeIncomingFlow() - } - - fun exitCatchClause(catch: FirCatch) { - graphBuilder.exitCatchClause(catch).mergeIncomingFlow() - } - - fun enterFinallyBlock(tryExpression: FirTryExpression) { - // TODO - graphBuilder.enterFinallyBlock(tryExpression).mergeIncomingFlow() - } - - fun exitFinallyBlock(tryExpression: FirTryExpression) { - // TODO - graphBuilder.exitFinallyBlock(tryExpression).mergeIncomingFlow() - } - - fun exitTryExpression(tryExpression: FirTryExpression) { - // TODO - graphBuilder.exitTryExpression(tryExpression).mergeIncomingFlow() - } - - // ----------------------------------- Resolvable call ----------------------------------- - - private fun enterSafeCall(qualifiedAccess: FirQualifiedAccess) { - if (!qualifiedAccess.safe) return - val node = graphBuilder.enterSafeCall(qualifiedAccess).mergeIncomingFlow() - val previousNode = node.alivePreviousNodes.first() - val shouldFork: Boolean - var flow= if (previousNode is ExitSafeCallNode) { - shouldFork = false - previousNode.alivePreviousNodes.getOrNull(1)?.flow ?: node.flow - } else { - shouldFork = true - node.flow - } - qualifiedAccess.explicitReceiver?.let { - val type = it.typeRef.coneTypeSafe() - ?.takeIf { it.isMarkedNullable } - ?.withNullability(ConeNullability.NOT_NULL, session.inferenceContext) - ?: return@let - - when (val variable = getOrCreateVariable(it)) { - is RealDataFlowVariable -> { - if (shouldFork) { - flow = logicSystem.forkFlow(flow) - } - logicSystem.addApprovedInfo(flow, variable, FirDataFlowInfo(setOf(type), emptySet())) - } - is SyntheticDataFlowVariable -> { - flow = logicSystem.approveFactsInsideFlow(variable, NotEqNull, flow, shouldFork, true) - } - } - } - - node.flow = flow - } - - private fun exitSafeCall(qualifiedAccess: FirQualifiedAccess) { - if (!qualifiedAccess.safe) return - graphBuilder.exitSafeCall(qualifiedAccess).mergeIncomingFlow() - } - - fun enterQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) { - enterSafeCall(qualifiedAccessExpression) - } - - fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) { - graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).mergeIncomingFlow() - exitSafeCall(qualifiedAccessExpression) - } - - fun exitFunctionCall(functionCall: FirFunctionCall) { - val node = graphBuilder.exitFunctionCall(functionCall).mergeIncomingFlow() - if (functionCall.isBooleanNot()) { - exitBooleanNot(functionCall, node) - } - processConditionalContract(functionCall) - if (functionCall.safe) { - exitSafeCall(functionCall) - } - } - - private fun processConditionalContract(functionCall: FirFunctionCall) { - val contractDescription = (functionCall.resolvedSymbol?.fir as? FirSimpleFunction)?.contractDescription ?: return - val conditionalEffects = contractDescription.effects.filterIsInstance() - if (conditionalEffects.isEmpty()) return - val argumentsMapping = createArgumentsMapping(functionCall) ?: return - contractDescriptionVisitingMode = true - graphBuilder.enterContract(functionCall).mergeIncomingFlow() - val functionCallVariable = getOrCreateVariable(functionCall) - for (conditionalEffect in conditionalEffects) { - val fir = conditionalEffect.buildContractFir(argumentsMapping) ?: continue - val effect = conditionalEffect.effect as? ConeReturnsEffectDeclaration ?: continue - fir.transformSingle(components.transformer, ResolutionMode.ContextDependent) - val argumentVariable = getOrCreateVariable(fir) - val lastNode = graphBuilder.lastNode - when (val value = effect.value) { - ConeConstantReference.WILDCARD -> { - lastNode.flow = logicSystem.approveFactsInsideFlow( - argumentVariable, - EqTrue, - lastNode.flow, - shouldForkFlow = false, - shouldRemoveSynthetics = true - ) - } - - ConeBooleanConstantReference.TRUE, ConeBooleanConstantReference.FALSE -> { - logicSystem.changeVariableForConditionFlow(lastNode.flow, argumentVariable, functionCallVariable) { - it.takeIf { it.condition == if (value == ConeBooleanConstantReference.TRUE) EqTrue else EqFalse } - } - } - - ConeConstantReference.NOT_NULL, ConeConstantReference.NULL -> { - logicSystem.changeVariableForConditionFlow(lastNode.flow, argumentVariable, functionCallVariable) { - it.takeIf { it.condition == EqTrue }?.let { - val condition = if (value == ConeConstantReference.NOT_NULL) Condition.NotEqNull else NotEqNull - ConditionalFirDataFlowInfo(condition, it.variable, it.info) - } - } - } - - else -> throw IllegalArgumentException(value.toString()) - } - } - graphBuilder.exitContract(functionCall).mergeIncomingFlow() - contractDescriptionVisitingMode = true - } - - - private val FirElement.resolvedSymbol: AbstractFirBasedSymbol<*>? - get() { - return when (this) { - is FirResolvable -> resolvedSymbol - is FirSymbolOwner<*> -> symbol - else -> null - } - } - - private val FirResolvable.resolvedSymbol: AbstractFirBasedSymbol<*>? - get() = calleeReference.let { - when (it) { - is FirExplicitThisReference -> it.boundSymbol - is FirResolvedNamedReference -> it.resolvedSymbol - is FirNamedReferenceWithCandidate -> it.candidateSymbol - else -> null - } - } - - private fun FirFunctionCall.isBooleanNot(): Boolean { - val symbol = calleeReference.safeAs()?.resolvedSymbol as? FirNamedFunctionSymbol ?: return false - return symbol.callableId == KOTLIN_BOOLEAN_NOT - } - - fun exitConstExpresion(constExpression: FirConstExpression<*>) { - if (constExpression.resultType is FirResolvedTypeRef && !contractDescriptionVisitingMode) return - graphBuilder.exitConstExpresion(constExpression).mergeIncomingFlow() - } - - fun exitVariableDeclaration(variable: FirProperty) { - val node = graphBuilder.exitVariableDeclaration(variable).mergeIncomingFlow() - val initializer = variable.initializer ?: return - - /* - * That part is needed for cases like that: - * - * val b = x is String - * ... - * if (b) { - * x.length - * } - */ - variableStorage[initializer]?.takeIf { it.isSynthetic() }?.let { initializerVariable -> - val realVariable = getOrCreateRealVariable(variable) - requireNotNull(realVariable) - logicSystem.changeVariableForConditionFlow(node.flow, initializerVariable, realVariable) - } - - - getOrCreateRealVariable(initializer)?.let { rhsVariable -> - variableStorage.createAliasVariable(variable.symbol, rhsVariable) - } - } - - fun exitVariableAssignment(assignment: FirVariableAssignment) { - val node = graphBuilder.exitVariableAssignment(assignment).mergeIncomingFlow() - val lhsSymbol: AbstractFirBasedSymbol<*> = (assignment.lValue as? FirResolvedNamedReference)?.resolvedSymbol ?: return - val lhsVariable = getOrCreateRealVariable(lhsSymbol.fir) ?: return - val rhsSymbol = assignment.rValue.resolvedSymbol - val rhsVariable = rhsSymbol?.let { variableStorage[it]?.takeIf { !it.isSynthetic() } } - if (rhsVariable == null) { - val type = assignment.rValue.typeRef.coneTypeSafe() ?: return - logicSystem.addApprovedInfo( - node.flow, - lhsVariable, - FirDataFlowInfo(setOf(type), emptySet()) - ) - return - } else { - variableStorage.rebindAliasVariable(lhsVariable, rhsVariable) - } - } - - fun exitThrowExceptionNode(throwExpression: FirThrowExpression) { - graphBuilder.exitThrowExceptionNode(throwExpression).mergeIncomingFlow() - } - - // ----------------------------------- Boolean operators ----------------------------------- - - fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) { - graphBuilder.enterBinaryAnd(binaryLogicExpression).mergeIncomingFlow() - } - - fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) { - val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression) - exitLeftArgumentOfBinaryBooleanOperator(leftNode, rightNode, isAnd = true) - } - - fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) { - val node = graphBuilder.exitBinaryAnd(binaryLogicExpression) - exitBinaryBooleanOperator(binaryLogicExpression, node, isAnd = true) - } - - fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) { - graphBuilder.enterBinaryOr(binaryLogicExpression).mergeIncomingFlow() - } - - fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) { - val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression) - exitLeftArgumentOfBinaryBooleanOperator(leftNode, rightNode, isAnd = false) - } - - fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) { - val node = graphBuilder.exitBinaryOr(binaryLogicExpression) - exitBinaryBooleanOperator(binaryLogicExpression, node, isAnd = false) - } - - private fun exitLeftArgumentOfBinaryBooleanOperator(leftNode: CFGNode<*>, rightNode: CFGNode<*>, isAnd: Boolean) { - val parentFlow = leftNode.alivePreviousNodes.first().flow - leftNode.flow = logicSystem.forkFlow(parentFlow) - val leftOperandVariable = getOrCreateVariable(leftNode.previousNodes.first().fir) - rightNode.flow = logicSystem.approveFactsInsideFlow( - leftOperandVariable, - if (isAnd) EqTrue else EqFalse, - parentFlow, - shouldForkFlow = true, - shouldRemoveSynthetics = false - ) - } - - private fun exitBinaryBooleanOperator( - binaryLogicExpression: FirBinaryLogicExpression, - node: AbstractBinaryExitNode<*>, - isAnd: Boolean - ) { - val bothEvaluated = if (isAnd) EqTrue else EqFalse - val onlyLeftEvaluated = bothEvaluated.invert() - - // Naming for all variables was chosen in assumption that we processing && expression - val flowFromLeft = node.leftOperandNode.flow - val flowFromRight = node.rightOperandNode.flow - - val flow = node.mergeIncomingFlow().flow - - val (leftVariable, rightVariable) = binaryLogicExpression.getVariables() - val andVariable = getOrCreateVariable(binaryLogicExpression) - - val (conditionalFromLeft, conditionalFromRight, approvedFromRight) = logicSystem.collectInfoForBooleanOperator( - flowFromLeft, - leftVariable, - flowFromRight, - rightVariable - ) - - // left && right == True - // left || right == False - val approvedIfTrue: MutableApprovedInfos = mutableMapOf() - logicSystem.approveFactTo(approvedIfTrue, bothEvaluated, conditionalFromLeft) - logicSystem.approveFactTo(approvedIfTrue, bothEvaluated, conditionalFromRight) - approvedFromRight.forEach { (variable, info) -> - approvedIfTrue.addInfo(variable, info) - } - approvedIfTrue.forEach { (variable, info) -> - logicSystem.addConditionalInfo(flow, andVariable, info.toConditional(bothEvaluated, variable)) - } - - // left && right == False - // left || right == True - val approvedIfFalse: MutableApprovedInfos = mutableMapOf() - val leftIsFalse = logicSystem.approveFact(onlyLeftEvaluated, conditionalFromLeft) - val rightIsFalse = logicSystem.approveFact(onlyLeftEvaluated, conditionalFromRight) - approvedIfFalse.mergeInfo(logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse)) - approvedIfFalse.forEach { (variable, info) -> - logicSystem.addConditionalInfo(flow, andVariable, info.toConditional(onlyLeftEvaluated, variable)) - } - - node.flow = flow - - variableStorage.removeVariableIfSynthetic(leftVariable) - variableStorage.removeVariableIfSynthetic(rightVariable) - } - - - private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) { - val booleanExpressionVariable = getOrCreateVariable(node.previousNodes.first().fir) - val variable = getOrCreateVariable(functionCall) - logicSystem.changeVariableForConditionFlow(node.flow, booleanExpressionVariable, variable) { it.invert() } - } - - // ----------------------------------- Annotations ----------------------------------- - - fun enterAnnotationCall(annotationCall: FirAnnotationCall) { - graphBuilder.enterAnnotationCall(annotationCall).mergeIncomingFlow() - } - - fun exitAnnotationCall(annotationCall: FirAnnotationCall) { - graphBuilder.exitAnnotationCall(annotationCall).mergeIncomingFlow() - } - - // ----------------------------------- Init block ----------------------------------- - - fun enterInitBlock(initBlock: FirAnonymousInitializer) { - graphBuilder.enterInitBlock(initBlock).mergeIncomingFlow() - } - - fun exitInitBlock(initBlock: FirAnonymousInitializer) { - graphBuilder.exitInitBlock(initBlock).mergeIncomingFlow() - } - - // ------------------------------------------------------------------------------------------------------------------------- - - private fun FirBinaryLogicExpression.getVariables(): Pair = - getOrCreateVariable(leftOperand) to getOrCreateVariable(rightOperand) - - private var CFGNode<*>.flow: Flow - get() = flowOnNodes.getValue(this.origin) - set(value) { - flowOnNodes[this.origin] = value - } - - private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) previousNodes.first() else this - - private fun > T.mergeIncomingFlow(): T = this.also { node -> - val previousFlows = node.alivePreviousNodes.map { it.flow } - node.flow = logicSystem.joinFlow(previousFlows) - } - - // -------------------------------- get or create variable -------------------------------- - - private fun getOrCreateSyntheticVariable(fir: FirElement): SyntheticDataFlowVariable = - variableStorage.getOrCreateNewSyntheticVariable(fir) - - private fun getOrCreateRealVariable(fir: FirElement): RealDataFlowVariable? { - @Suppress("NAME_SHADOWING") - val fir = fir.unwrapWhenSubjectExpression().unwrapSmartcast() - val symbol = fir.resolvedSymbol ?: return null - return when { - fir is FirThisReceiverExpressionImpl -> variableStorage.getOrCreateNewThisRealVariable(symbol) - symbol is FirVariableSymbol<*> -> - // TODO: Fix this for non-local properties. - variableStorage.getOrCreateNewRealVariable(symbol).variableUnderAlias - else -> null - } - } - - private fun getOrCreateVariable(fir: FirElement): DataFlowVariable { - return getOrCreateRealVariable(fir) ?: getOrCreateSyntheticVariable(fir) - } - - // -------------------------------- get variable -------------------------------- - - private val FirElement.realVariable: RealDataFlowVariable? - get() { - val symbol: AbstractFirBasedSymbol<*> = if (this is FirThisReceiverExpression) { - calleeReference.boundSymbol - } else { - resolvedSymbol - } ?: return null - return variableStorage[symbol] - } - - // TODO: Fix this -- see broken test_3 in compiler/fir/resolve/testData/resolve/smartcasts/notBoundSmartcasts.kt - // If we have multiple local variables (could be value parameter) of the same type, there should be separate DataFlowVariables for - // accesses to a property for each distinct local variable, i.e., DataFlowVariables in storage for properties should be keyed by - // the "chain" of variable/property accesses. We also need to check that the property is not mutable and has no custom getter. - private fun getRealVariablesForSafeCallChain(call: FirExpression): Collection { - val result = mutableListOf() - - fun collect(call: FirExpression) { - when (call) { - is FirQualifiedAccess -> { - if (call.safe) { - val explicitReceiver = call.explicitReceiver - require(explicitReceiver != null) - collect(explicitReceiver) - } - result.addIfNotNull(getOrCreateRealVariable(call)) - } - is FirWhenSubjectExpression -> { - // TODO: check - call.whenSubject.whenExpression.subjectVariable?.let { result += getOrCreateRealVariable(it)!! } - call.whenSubject.whenExpression.subject?.let { collect(it) } - } - } - } - - collect(call) - return result - } - - private inner class LogicSystemImpl(context: ConeInferenceContext) : DelegatingLogicSystem(context) { - override fun processUpdatedReceiverVariable(flow: Flow, variable: RealDataFlowVariable) { - val symbol = (variable.fir as? FirSymbolOwner<*>)?.symbol ?: return - - val index = receiverStack.getReceiverIndex(symbol) ?: return - val info = flow.getApprovedInfo(variable) - - if (info == null) { - receiverStack.replaceReceiverType(index, receiverStack.getOriginalType(index)) - } else { - val types = info.exactType.toMutableList().also { - it += receiverStack.getOriginalType(index) - } - receiverStack.replaceReceiverType(index, context.intersectTypesOrNull(types)!!) - } - } - - override fun updateAllReceivers(flow: Flow) { - receiverStack.mapNotNull { variableStorage[it.boundSymbol] }.forEach { processUpdatedReceiverVariable(flow, it) } - } - } -} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowInfo.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowInfo.kt deleted file mode 100644 index 239f72fc700..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowInfo.kt +++ /dev/null @@ -1,89 +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.Multimap -import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.render - -data class ConditionalFirDataFlowInfo( - val condition: Condition, - val variable: RealDataFlowVariable, - val info: FirDataFlowInfo -) { - fun invert(): ConditionalFirDataFlowInfo { - return ConditionalFirDataFlowInfo(condition.invert(), variable, info) - } - - override fun toString(): String { - return "$condition -> $variable: ${info.exactType.render()}, ${info.exactNotType.render()}" - } - - private fun Set.render(): String = joinToString { it.render() } -} - -typealias ApprovedInfos = Map -typealias MutableApprovedInfos = MutableMap -typealias ConditionalInfos = Multimap - -interface FirDataFlowInfo { - companion object { - // TODO: temporary - operator fun invoke(exactType: Set, exactNotType: Set): FirDataFlowInfo = - MutableFirDataFlowInfo(exactType.toMutableSet(), exactNotType.toMutableSet()) - } - - val exactType: Set - val exactNotType: Set - operator fun plus(other: FirDataFlowInfo): FirDataFlowInfo - operator fun minus(other: FirDataFlowInfo): FirDataFlowInfo - val isNotEmpty: Boolean - val isEmpty: Boolean get() = !isNotEmpty - fun invert(): FirDataFlowInfo -} - -data class MutableFirDataFlowInfo( - override val exactType: MutableSet = mutableSetOf(), - override val exactNotType: MutableSet = mutableSetOf() -) : FirDataFlowInfo { - override operator fun plus(other: FirDataFlowInfo): MutableFirDataFlowInfo = MutableFirDataFlowInfo( - HashSet(exactType).apply { addAll(other.exactType) }, - HashSet(exactNotType).apply { addAll(other.exactNotType) } - ) - - override operator fun minus(other: FirDataFlowInfo): MutableFirDataFlowInfo = MutableFirDataFlowInfo( - HashSet(exactType).apply { removeAll(other.exactType) }, - HashSet(exactNotType).apply { removeAll(other.exactNotType) } - ) - - override val isNotEmpty: Boolean get() = exactType.isNotEmpty() || exactNotType.isNotEmpty() - - override fun invert(): FirDataFlowInfo = MutableFirDataFlowInfo(exactNotType, exactType) - - operator fun plusAssign(info: FirDataFlowInfo) { - exactType += info.exactType - exactNotType += info.exactNotType - } - - fun copy(): MutableFirDataFlowInfo = MutableFirDataFlowInfo(exactType.toMutableSet(), exactNotType.toMutableSet()) -} - -operator fun FirDataFlowInfo.plus(other: FirDataFlowInfo?): FirDataFlowInfo = other?.let { this + other } ?: this - -fun FirDataFlowInfo.toConditional(condition: Condition, variable: RealDataFlowVariable): ConditionalFirDataFlowInfo = - ConditionalFirDataFlowInfo(condition, variable, this) - -fun MutableApprovedInfos.addInfo(variable: RealDataFlowVariable, info: FirDataFlowInfo) { - merge(variable, info as MutableFirDataFlowInfo) { existingInfo, newInfo -> - existingInfo.apply { this += newInfo } - } -} - -fun MutableApprovedInfos.mergeInfo(other: Map) { - other.forEach { (variable, info) -> - addInfo(variable, info) - } -} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt deleted file mode 100644 index a9ba333d3f1..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt +++ /dev/null @@ -1,197 +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.ArrayListMultimap -import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext -import org.jetbrains.kotlin.fir.types.ConeKotlinType - -interface Flow { - fun getApprovedInfo(variable: RealDataFlowVariable): FirDataFlowInfo? - fun getConditionalInfos(variable: DataFlowVariable): Collection - - fun getVariablesInApprovedInfos(): Collection - - fun removeConditionalInfos(variable: DataFlowVariable): Collection -} - -abstract class LogicSystem(private val context: ConeInferenceContext) { - - // ------------------------------- Flow operations ------------------------------- - - abstract fun createEmptyFlow(): Flow - abstract fun forkFlow(flow: Flow): Flow - abstract fun joinFlow(flows: Collection): Flow - - open fun addApprovedInfo(flow: Flow, variable: RealDataFlowVariable, info: FirDataFlowInfo) { - 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: - * 1. val b = x is String - * 2. b = x is String - * 3. !b | b.not() for Booleans - */ - open fun changeVariableForConditionFlow( - flow: Flow, - sourceVariable: DataFlowVariable, - newVariable: DataFlowVariable, - transform: ((ConditionalFirDataFlowInfo) -> ConditionalFirDataFlowInfo?)? = null - ) { - var infos = flow.getConditionalInfos(sourceVariable) - if (transform != null) { - infos = infos.mapNotNull(transform) - } - flow.conditionalInfos.putAll(newVariable, infos) - if (sourceVariable.isSynthetic()) { - flow.conditionalInfos.removeAll(sourceVariable) - } - } - - open fun approveFactsInsideFlow( - variable: DataFlowVariable, - condition: Condition, - flow: Flow, - shouldForkFlow: Boolean, - shouldRemoveSynthetics: Boolean - ): Flow { - val notApprovedFacts: Collection = 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() - notApprovedFacts.forEach { - if (it.condition == condition) { - newFacts.put(it.variable, it.info) - } - } - - val updatedReceivers = mutableSetOf() - - 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 ------------------------------- - - abstract fun processUpdatedReceiverVariable(flow: Flow, variable: RealDataFlowVariable) - 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 ------------------------------- - - data class InfoForBooleanOperator( - val conditionalFromLeft: Collection, - val conditionalFromRight: Collection, - val approvedFromRight: ApprovedInfos - ) - - abstract fun collectInfoForBooleanOperator( - leftFlow: Flow, - leftVariable: DataFlowVariable, - rightFlow: Flow, - rightVariable: DataFlowVariable - ): InfoForBooleanOperator - - fun orForVerifiedFacts( - left: ApprovedInfos, - right: ApprovedInfos - ): MutableApprovedInfos { - if (left.isNullOrEmpty() || right.isNullOrEmpty()) return mutableMapOf() - val map = mutableMapOf() - for (variable in left.keys.intersect(right.keys)) { - val leftInfo = left.getValue(variable) - val rightInfo = right.getValue(variable) - map[variable] = or(listOf(leftInfo, rightInfo)) - } - return map - } - - fun approveFactTo(destination: MutableApprovedInfos, variable: DataFlowVariable, condition: Condition, flow: Flow) { - val notApprovedFacts: Collection = flow.getConditionalInfos(variable) - approveFactTo(destination, condition, notApprovedFacts) - } - - fun approveFactTo(destination: MutableApprovedInfos, condition: Condition, notApprovedFacts: Collection) { - if (notApprovedFacts.isEmpty()) return - notApprovedFacts.forEach { - if (it.condition == condition) { - destination.addInfo(it.variable, it.info) - } - } - } - - fun approveFact(condition: Condition, notApprovedFacts: Collection): MutableApprovedInfos { - return mutableMapOf().apply { approveFactTo(this, condition, notApprovedFacts) } - } - - fun approveFact(variable: DataFlowVariable, condition: Condition, flow: Flow): MutableApprovedInfos { - return mutableMapOf().apply { approveFactTo(this, variable, condition, flow) } - } - - // ------------------------------- Util functions ------------------------------- - - // TODO - protected fun Collection>.intersectSets(): Set { - if (isEmpty()) return emptySet() - val iterator = iterator() - val result = HashSet(iterator.next()) - while (iterator.hasNext()) { - result.retainAll(iterator.next()) - } - return result - } - - protected fun or(infos: Collection): MutableFirDataFlowInfo { - infos.singleOrNull()?.let { return it as MutableFirDataFlowInfo } - val exactType = orTypes(infos.map { it.exactType }) - val exactNotType = orTypes(infos.map { it.exactNotType }) - return MutableFirDataFlowInfo(exactType, exactNotType) - } - - private fun orTypes(types: Collection>): MutableSet { - if (types.any { it.isEmpty() }) return mutableSetOf() - 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 - } -} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt deleted file mode 100644 index 9fc8b4e4db4..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt +++ /dev/null @@ -1,266 +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 kotlinx.collections.immutable.* -//import kotlinx.collections.immutable.PersistentMap -//import kotlinx.collections.immutable.PersistentSet -//import org.jetbrains.kotlin.fir.types.ConeKotlinType -// -// -//private data class PersistentFirDataFlowInfo( -// override val exactType: PersistentSet, -// override val exactNotType: PersistentSet -//) : FirDataFlowInfo { -// -// override operator fun plus(other: FirDataFlowInfo): PersistentFirDataFlowInfo { -// return PersistentFirDataFlowInfo( -// exactType + other.exactType, -// exactNotType + other.exactNotType -// ) -// } -// -// override fun minus(other: FirDataFlowInfo): FirDataFlowInfo { -// // TODO -// throw IllegalStateException() -// } -// -// override val isNotEmpty: Boolean -// get() = exactType.isNotEmpty() || exactNotType.isNotEmpty() -// -// override fun invert(): PersistentFirDataFlowInfo { -// return PersistentFirDataFlowInfo(exactNotType, exactType) -// } -// -//// override fun toMutableInfo(): MutableFirDataFlowInfo { -//// return MutableFirDataFlowInfo(exactType.toMutableSet(), exactNotType.toMutableSet()) -//// } -//} -// -//private typealias PersistentApprovedInfos = PersistentMap -//private typealias PersistentConditionalInfos = PersistentMap> -// -//private fun FirDataFlowInfo.toPersistent(): PersistentFirDataFlowInfo = PersistentFirDataFlowInfo( -// exactType.toPersistentSet(), -// exactNotType.toPersistentSet() -//) -// -//private fun PersistentApprovedInfos.addNewInfo(variable: RealDataFlowVariable, info: FirDataFlowInfo): PersistentApprovedInfos { -// val existingInfo = this[variable] -// return if (existingInfo == null) { -// val persistentInfo = if (info is PersistentFirDataFlowInfo) info else info.toPersistent() -// put(variable, persistentInfo) -// } else { -// put(variable, existingInfo + info) -// } -//} -// -//private class PersistentFlow : Flow { -// val previousFlow: PersistentFlow? -// var approvedInfos: PersistentApprovedInfos -// var conditionalInfos: PersistentConditionalInfos -// -// constructor(previousFlow: PersistentFlow) { -// this.previousFlow = previousFlow -// approvedInfos = previousFlow.approvedInfos -// conditionalInfos = previousFlow.conditionalInfos -// level = previousFlow.level + 1 -// } -// -// constructor() { -// previousFlow = null -// approvedInfos = persistentHashMapOf() -// conditionalInfos = persistentHashMapOf() -// level = 1 -// } -// -// val level: Int -// var approvedInfosDiff: PersistentApprovedInfos = persistentHashMapOf() -// -// override fun getApprovedInfo(variable: RealDataFlowVariable): FirDataFlowInfo? { -// return approvedInfos[variable] -// } -// -// override fun getConditionalInfos(variable: DataFlowVariable): Collection { -// return conditionalInfos[variable] ?: emptyList() -// } -// -// override fun getVariablesInApprovedInfos(): Collection { -// return approvedInfos.keys -// } -// -// override fun removeConditionalInfos(variable: DataFlowVariable): Collection { -// val result = getConditionalInfos(variable) -// if (result.isNotEmpty()) { -// conditionalInfos = conditionalInfos.remove(variable) -// } -// return result -// } -//} -// -//abstract class PersistentLogicSystem(context: DataFlowInferenceContext) : LogicSystem(context) { -// override fun createEmptyFlow(): Flow { -// return PersistentFlow() -// } -// -// override fun forkFlow(flow: Flow): Flow { -// require(flow is PersistentFlow) -// return PersistentFlow(flow) -// } -// -// override fun joinFlow(flows: Collection): Flow { -// if (flows.isEmpty()) return createEmptyFlow() -// flows.singleOrNull()?.let { return it } -// -// @Suppress("UNCHECKED_CAST", "NAME_SHADOWING") -// val flows = flows as Collection -// val commonFlow = flows.reduce(this::lowestCommonFlow) -// -// val commonVariables = flows.map { it.diffVariablesIterable(commonFlow).toList() } -// .intersectSets() -// .takeIf { it.isNotEmpty() } -// ?: return commonFlow -// -// for (variable in commonVariables) { -// val info = or(flows.map { it.getApprovedDiff(variable, commonFlow) }) -// if (info.isEmpty) continue -// commonFlow.approvedInfos = commonFlow.approvedInfos.addNewInfo(variable, info) -// commonFlow.approvedInfosDiff = commonFlow.approvedInfosDiff.addNewInfo(variable, info) -// } -// -// updateAllReceivers(commonFlow) -// -// return commonFlow -// } -// -// private fun PersistentFlow.diffVariablesIterable(parentFlow: PersistentFlow): Iterable = -// object : DiffIterable(parentFlow, this) { -// override fun extractIterator(flow: PersistentFlow): Iterator { -// return flow.approvedInfosDiff.keys.iterator() -// } -// } -// -// private abstract class DiffIterable(private val parentFlow: PersistentFlow, private var currentFlow: PersistentFlow) : Iterable { -// private var currentIterator = extractIterator(currentFlow) -// -// abstract fun extractIterator(flow: PersistentFlow): Iterator -// -// override fun iterator(): Iterator { -// return object : Iterator { -// override fun hasNext(): Boolean { -// if (currentIterator.hasNext()) return true -// while (currentFlow != parentFlow) { -// currentFlow = currentFlow.previousFlow!! -// currentIterator = extractIterator(currentFlow) -// if (currentIterator.hasNext()) return true -// } -// return false -// } -// -// override fun next(): T { -// if (!hasNext()) { -// throw NoSuchElementException() -// } -// return currentIterator.next() -// } -// } -// } -// } -// -// private fun PersistentFlow.getApprovedDiff(variable: RealDataFlowVariable, parentFlow: PersistentFlow): MutableFirDataFlowInfo { -// var flow = this -// val result = MutableFirDataFlowInfo() -// while (flow != parentFlow) { -// flow.approvedInfosDiff[variable]?.let { -// result += it -// } -// flow = flow.previousFlow!! -// } -// return result -// } -// -// override fun collectInfoForBooleanOperator( -// leftFlow: Flow, -// leftVariable: DataFlowVariable, -// rightFlow: Flow, -// rightVariable: DataFlowVariable -// ): InfoForBooleanOperator { -// require(leftFlow is PersistentFlow && rightFlow is PersistentFlow) -// return InfoForBooleanOperator( -// leftFlow.conditionalInfos[leftVariable] ?: emptyList(), -// rightFlow.conditionalInfos[rightVariable] ?: emptyList(), -// rightFlow.approvedInfosDiff -// ) -// } -// -// override fun changeVariableForConditionFlow( -// flow: Flow, -// sourceVariable: DataFlowVariable, -// newVariable: DataFlowVariable, -// transform: ((ConditionalFirDataFlowInfo) -> ConditionalFirDataFlowInfo?)? -// ) { -// require(flow is PersistentFlow) -// with(flow) { -// val existingInfo = conditionalInfos[sourceVariable]?.takeIf { it.isNotEmpty() } ?: return -// val transformedInfo = if (transform == null) { -// existingInfo -// } else { -// existingInfo.map(transform).toPersistentList() -// } -// conditionalInfos = conditionalInfos.remove(sourceVariable).put(newVariable, transformedInfo) -// } -// } -// -// override fun addApprovedInfo(flow: Flow, variable: RealDataFlowVariable, info: FirDataFlowInfo) { -// require(flow is PersistentFlow) -// with(flow) { -// approvedInfos = approvedInfos.addNewInfo(variable, info) -// if (previousFlow != null) { -// approvedInfosDiff = approvedInfosDiff.addNewInfo(variable, info) -// } -// if (variable.isThisReference) { -// processUpdatedReceiverVariable(flow, variable) -// } -// } -// } -// -// override fun addConditionalInfo(flow: Flow, variable: DataFlowVariable, info: ConditionalFirDataFlowInfo) { -// require(flow is PersistentFlow) -// with(flow) { -// val existingInfo = conditionalInfos[variable] -// conditionalInfos = if (existingInfo == null) { -// conditionalInfos.put(variable, persistentListOf(info)) -// } else { -// conditionalInfos.put(variable, existingInfo + info) -// } -// } -// } -// -// override val Flow.approvedInfos: MutableApprovedInfos -// get() = throw IllegalStateException() -// -// override val Flow.conditionalInfos: ConditionalInfos -// get() = throw IllegalStateException() -// -// private fun lowestCommonFlow(left: PersistentFlow, right: PersistentFlow): PersistentFlow { -// val level = minOf(left.level, right.level) -// @Suppress("NAME_SHADOWING") -// var left = left -// while (left.level > level) { -// left = left.previousFlow!! -// } -// @Suppress("NAME_SHADOWING") -// var right = right -// while (right.level > level) { -// right = right.previousFlow!! -// } -// while (left != right) { -// left = left.previousFlow!! -// right = right.previousFlow!! -// } -// return left -// } -//} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/SimpleLogicSystem.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/SimpleLogicSystem.kt deleted file mode 100644 index 65f1551f462..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/SimpleLogicSystem.kt +++ /dev/null @@ -1,106 +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.ArrayListMultimap -import com.google.common.collect.HashMultimap -import com.google.common.collect.Multimap -import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext - -private class SimpleFlow( - val approvedInfos: MutableApprovedInfos = mutableMapOf(), - val conditionalInfos: ConditionalInfos = HashMultimap.create() -) : Flow { - override fun getApprovedInfo(variable: RealDataFlowVariable): FirDataFlowInfo? { - return approvedInfos[variable] - } - - override fun getConditionalInfos(variable: DataFlowVariable): Collection { - return conditionalInfos[variable] - } - - override fun getVariablesInApprovedInfos(): Collection { - return approvedInfos.keys - } - - override fun removeConditionalInfos(variable: DataFlowVariable): Collection { - return conditionalInfos.removeAll(variable) - } -} - -abstract class SimpleLogicSystem(context: ConeInferenceContext) : 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 { - @Suppress("UNCHECKED_CAST", "NAME_SHADOWING") - val flows = flows as Collection - flows.singleOrNull()?.let { return it } - val approvedFacts: MutableApprovedInfos = mutableMapOf() - flows.map { it.approvedInfos.keys } - .intersectSets() - .forEach { variable -> - val infos = flows.map { it.approvedInfos[variable]!! } - if (infos.isNotEmpty()) { - approvedFacts[variable] = or(infos) - } - } - - val notApprovedFacts: ConditionalInfos = ArrayListMultimap.create() - flows.map { it.conditionalInfos.keySet() } - .intersectSets() - .forEach { variable -> - val infos = flows.map { it.conditionalInfos[variable] }.intersectSets() - if (infos.isNotEmpty()) { - notApprovedFacts.putAll(variable, infos) - } - } - - val result = SimpleFlow(approvedFacts, notApprovedFacts) - updateAllReceivers(result) - return result - } - - override fun collectInfoForBooleanOperator( - leftFlow: Flow, - leftVariable: DataFlowVariable, - rightFlow: Flow, - rightVariable: DataFlowVariable - ): InfoForBooleanOperator { - require(leftFlow is SimpleFlow && rightFlow is SimpleFlow) - return InfoForBooleanOperator( - leftFlow.conditionalInfos[leftVariable], - rightFlow.conditionalInfos[rightVariable], - rightFlow.approvedInfos - leftFlow.approvedInfos - ) - } - - private operator fun ApprovedInfos.minus(other: ApprovedInfos): ApprovedInfos { - val result: MutableApprovedInfos = mutableMapOf() - forEach { (variable, info) -> - require(info is MutableFirDataFlowInfo) - result[variable] = other[variable]?.let { info - it } ?: info - } - return result - } -} - -private fun Multimap.copy(): Multimap = ArrayListMultimap.create(this)