[FIR] Some minor fixes in DFA after review

This commit is contained in:
Dmitriy Novozhilov
2019-09-04 18:07:26 +03:00
parent 6b7aa21b8f
commit f9d45d2f2a
10 changed files with 168 additions and 183 deletions
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
interface DataFlowInferenceContext : TypeSystemCommonSuperTypesContext, ConeInferenceContext {
fun myCommonSuperType(types: List<ConeKotlinType>): ConeKotlinType? {
fun commonSuperTypeOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) {
0 -> null
1 -> types.first()
@@ -22,7 +22,7 @@ interface DataFlowInferenceContext : TypeSystemCommonSuperTypesContext, ConeInfe
}
}
fun myIntersectTypes(types: List<ConeKotlinType>): ConeKotlinType? {
fun intersectTypesOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) {
0 -> null
1 -> types.first()
@@ -43,7 +43,7 @@ interface DataFlowInferenceContext : TypeSystemCommonSuperTypesContext, ConeInfe
val commonTypes = allTypes.toMutableSet()
types.forEach { commonTypes.retainAll(it) }
val differentTypes = allTypes - commonTypes
myCommonSuperType(differentTypes.toList())?.let { commonTypes += it }
commonSuperTypeOrNull(differentTypes.toList())?.let { commonTypes += it }
return commonTypes
}
@@ -6,43 +6,32 @@
package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirThisReference
import org.jetbrains.kotlin.fir.expressions.impl.FirThisReceiverExpressionImpl
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
/*
* isSynthetic = false for variables that represents actual variables in fir
* isSynthetic = true for complex expressions (like when expression)
*/
sealed class DataFlowVariable(val index: Int, val fir: FirElement) {
sealed class DataFlowVariable(val variableIndexForDebug: Int, val fir: FirElement) {
abstract val isSynthetic: Boolean
abstract val real: DataFlowVariable
abstract val aliasedVariable: DataFlowVariable
abstract val isThisReference: Boolean
final override fun hashCode(): Int {
return index
}
final override fun equals(other: Any?): Boolean {
if (other !is DataFlowVariable) return false
return index == other.index
}
final override fun toString(): String {
return "d$index"
return "d$variableIndexForDebug"
}
}
private class RealDataFlowVariable(index: Int, fir: FirElement, override val isThisReference: Boolean) : DataFlowVariable(index, fir) {
override val isSynthetic: Boolean get() = false
override val real: DataFlowVariable get() = this
override val aliasedVariable: DataFlowVariable get() = this
}
private class SyntheticDataFlowVariable(index: Int, fir: FirElement) : DataFlowVariable(index, fir) {
override val isSynthetic: Boolean get() = true
override val real: DataFlowVariable get() = this
override val aliasedVariable: DataFlowVariable get() = this
override val isThisReference: Boolean get() = false
}
@@ -50,7 +39,7 @@ private class SyntheticDataFlowVariable(index: Int, fir: FirElement) : DataFlowV
private class AliasedDataFlowVariable(index: Int, fir: FirElement, var delegate: DataFlowVariable) : DataFlowVariable(index, fir) {
override val isSynthetic: Boolean get() = delegate.isSynthetic
override val real: DataFlowVariable get() = delegate.real
override val aliasedVariable: DataFlowVariable get() = delegate.aliasedVariable
override val isThisReference: Boolean get() = false
}
@@ -58,7 +47,7 @@ private class AliasedDataFlowVariable(index: Int, fir: FirElement, var delegate:
class DataFlowVariableStorage {
private val fir2DfiMap: MutableMap<FirElement, DataFlowVariable> = mutableMapOf()
private var counter: Int = 1
private var debugIndexCounter: Int = 1
fun getOrCreateNewRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable {
return getOrCreateNewRealVariableImpl(symbol, false)
@@ -71,12 +60,12 @@ class DataFlowVariableStorage {
private fun getOrCreateNewRealVariableImpl(symbol: FirBasedSymbol<*>, isThisReference: Boolean): DataFlowVariable {
val fir = symbol.fir
get(fir)?.let { return it }
return RealDataFlowVariable(counter++, fir, isThisReference).also { storeVariable(it, fir) }
return RealDataFlowVariable(debugIndexCounter++, fir, isThisReference).also { storeVariable(it, fir) }
}
fun getOrCreateNewSyntheticVariable(fir: FirElement): DataFlowVariable {
get(fir)?.let { return it }
return SyntheticDataFlowVariable(counter++, fir).also { storeVariable(it, fir) }
return SyntheticDataFlowVariable(debugIndexCounter++, fir).also { storeVariable(it, fir) }
}
fun createAliasVariable(symbol: FirBasedSymbol<*>, variable: DataFlowVariable) {
@@ -84,7 +73,7 @@ class DataFlowVariableStorage {
}
private fun createAliasVariable(fir: FirElement, variable: DataFlowVariable) {
AliasedDataFlowVariable(counter++, fir, variable).also { storeVariable(it, fir) }
AliasedDataFlowVariable(debugIndexCounter++, fir, variable).also { storeVariable(it, fir) }
}
fun rebindAliasVariable(aliasVariable: DataFlowVariable, newVariable: DataFlowVariable) {
@@ -122,7 +111,7 @@ class DataFlowVariableStorage {
fun reset() {
fir2DfiMap.clear()
counter = 1
debugIndexCounter = 1
}
private fun storeVariable(variable: DataFlowVariable, fir: FirElement) {
@@ -40,30 +40,36 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
private val graphBuilder = ControlFlowGraphBuilder()
private val logicSystem = LogicSystem(context)
private val variableStorage = DataFlowVariableStorage()
private val edges = mutableMapOf<CFGNode<*>, Flow>()
private val flowOnNodes = mutableMapOf<CFGNode<*>, Flow>()
/*
* 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<ConeKotlinType>? {
val variable = qualifiedAccessExpression.variable?.real ?: return null
/*
* 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.variable?.aliasedVariable ?: return null
return graphBuilder.lastNode.flow.approvedFacts(variable)?.exactType ?: return null
}
// ----------------------------------- Named function -----------------------------------
fun enterFunction(function: FirFunction<*>) {
graphBuilder.enterFunction(function).passFlow()
graphBuilder.enterFunction(function).mergeIncomingFlow()
}
fun exitFunction(function: FirFunction<*>): ControlFlowGraph? {
val (node, graph) = graphBuilder.exitFunction(function)
node.passFlow()
node.mergeIncomingFlow()
for (valueParameter in function.valueParameters) {
variableStorage.removeRealVariable(valueParameter.symbol)
}
if (graphBuilder.isTopLevel()) {
edges.clear()
flowOnNodes.clear()
variableStorage.reset()
}
return graph
@@ -72,38 +78,38 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
// ----------------------------------- Property -----------------------------------
fun enterProperty(property: FirProperty) {
graphBuilder.enterProperty(property).passFlow()
graphBuilder.enterProperty(property).mergeIncomingFlow()
}
fun exitProperty(property: FirProperty): ControlFlowGraph {
val (node, graph) = graphBuilder.exitProperty(property)
node.passFlow()
node.mergeIncomingFlow()
return graph
}
// ----------------------------------- Block -----------------------------------
fun enterBlock(block: FirBlock) {
val node = graphBuilder.enterBlock(block).passFlow(false)
val node = graphBuilder.enterBlock(block).mergeIncomingFlow()
val previousNode = node.usefulPreviousNodes.singleOrNull() as? WhenBranchConditionExitNode
val previousNode = node.alivePreviousNodes.singleOrNull() as? WhenBranchConditionExitNode
if (previousNode != null) {
node.flow = approveFactsAndUpdateImplicitReceivers(previousNode.variable, EqTrue, node.flow)
}
}
fun exitBlock(block: FirBlock) {
graphBuilder.exitBlock(block).passFlow()
graphBuilder.exitBlock(block).mergeIncomingFlow()
}
// ----------------------------------- Operator call -----------------------------------
fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).passFlow(false)
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow()
if (typeOperatorCall.operation !in FirOperation.TYPES) return
val type = typeOperatorCall.conversionTypeRef.coneTypeSafe<ConeKotlinType>() ?: return
val varVariable = getOrCreateRealVariable(typeOperatorCall.argument)?.real ?: return
val operandVariable = getOrCreateRealVariable(typeOperatorCall.argument)?.aliasedVariable ?: return
var flow = node.flow
when (typeOperatorCall.operation) {
@@ -118,34 +124,34 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqTrue, varVariable, chooseInfo(true)
ConditionalFirDataFlowInfo(
EqTrue, operandVariable, chooseInfo(true)
)
)
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqFalse, varVariable, chooseInfo(false)
ConditionalFirDataFlowInfo(
EqFalse, operandVariable, chooseInfo(false)
)
)
}
FirOperation.AS -> {
flow = addApprovedFact(flow, varVariable, FirDataFlowInfo(setOf(type), emptySet()))
flow = addApprovedFact(flow, operandVariable, FirDataFlowInfo(setOf(type), emptySet()))
}
FirOperation.SAFE_AS -> {
val expressionVariable = getOrCreateSyntheticVariable(typeOperatorCall)
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
NotEqNull, varVariable, FirDataFlowInfo(setOf(type), emptySet())
ConditionalFirDataFlowInfo(
NotEqNull, operandVariable, FirDataFlowInfo(setOf(type), emptySet())
)
).addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqNull, varVariable, FirDataFlowInfo(emptySet(), setOf(type))
ConditionalFirDataFlowInfo(
EqNull, operandVariable, FirDataFlowInfo(emptySet(), setOf(type))
)
)
}
@@ -157,7 +163,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
}
fun exitOperatorCall(operatorCall: FirOperatorCall) {
val node = graphBuilder.exitOperatorCall(operatorCall).passFlow(false)
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]
@@ -192,7 +198,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables ->
operandVariables.forEach { operandVariable ->
flow = flow.addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo(
expressionVariable, ConditionalFirDataFlowInfo(
isEq.toEqBoolean(),
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
@@ -208,7 +214,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
val constValue = (const.value as Boolean)
val shouldInvert = isEq xor constValue
flow.notApprovedFacts[operandVariable].forEach { info ->
flow.conditionalInfos[operandVariable].forEach { info ->
flow = flow.addNotApprovedFact(expressionVariable, info.let { if (shouldInvert) it.invert() else it })
}
}
@@ -241,12 +247,12 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
facts.forEach { (variable, info) ->
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
ConditionalFirDataFlowInfo(
EqTrue, variable, info
)
).addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
ConditionalFirDataFlowInfo(
EqFalse, variable, info.invert()
)
)
@@ -265,7 +271,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
operandVariables.forEach { operandVariable ->
flow = flow.addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo(
expressionVariable, ConditionalFirDataFlowInfo(
condition,
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
@@ -289,17 +295,17 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
// ----------------------------------- Jump -----------------------------------
fun exitJump(jump: FirJump<*>) {
graphBuilder.exitJump(jump).passFlow()
graphBuilder.exitJump(jump).mergeIncomingFlow()
}
// ----------------------------------- When -----------------------------------
fun enterWhenExpression(whenExpression: FirWhenExpression) {
graphBuilder.enterWhenExpression(whenExpression).passFlow()
graphBuilder.enterWhenExpression(whenExpression).mergeIncomingFlow()
}
fun enterWhenBranchCondition(whenBranch: FirWhenBranch) {
val node = graphBuilder.enterWhenBranchCondition(whenBranch).passFlow(false)
val node = graphBuilder.enterWhenBranchCondition(whenBranch).mergeIncomingFlow()
val previousNode = node.previousNodes.single()
if (previousNode is WhenBranchConditionExitNode) {
node.flow = approveFactsAndUpdateImplicitReceivers(previousNode.variable, EqFalse, node.flow)
@@ -308,20 +314,20 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
}
fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {
val node = graphBuilder.exitWhenBranchCondition(whenBranch).passFlow(true)
val node = graphBuilder.exitWhenBranchCondition(whenBranch).mergeIncomingFlow()
node.flow.freeze()
val conditionVariable = getOrCreateVariable(whenBranch.condition)
node.variable = conditionVariable
}
fun exitWhenBranchResult(whenBranch: FirWhenBranch) {
val node = graphBuilder.exitWhenBranchResult(whenBranch).passFlow(false)
val node = graphBuilder.exitWhenBranchResult(whenBranch).mergeIncomingFlow()
val conditionVariable = getOrCreateVariable(whenBranch.condition)
node.flow = node.flow.removeSyntheticVariable(conditionVariable).apply { freeze() }
}
fun exitWhenExpression(whenExpression: FirWhenExpression) {
val node = graphBuilder.exitWhenExpression(whenExpression).passFlow()
val node = graphBuilder.exitWhenExpression(whenExpression).mergeIncomingFlow()
var flow = node.flow
val subjectSymbol = whenExpression.subjectVariable?.symbol
if (subjectSymbol != null) {
@@ -334,74 +340,74 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
fun enterWhileLoop(loop: FirLoop) {
val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop)
loopEnterNode.passFlow()
loopConditionEnterNode.passFlow()
loopEnterNode.mergeIncomingFlow()
loopConditionEnterNode.mergeIncomingFlow()
}
fun exitWhileLoopCondition(loop: FirLoop) {
graphBuilder.exitWhileLoopCondition(loop).passFlow()
graphBuilder.exitWhileLoopCondition(loop).mergeIncomingFlow()
}
fun exitWhileLoop(loop: FirLoop) {
graphBuilder.exitWhileLoop(loop).also { (blockExitNode, exitNode) ->
blockExitNode.passFlow()
exitNode.passFlow()
blockExitNode.mergeIncomingFlow()
exitNode.mergeIncomingFlow()
}
}
// ----------------------------------- Do while Loop -----------------------------------
fun enterDoWhileLoop(loop: FirLoop) {
graphBuilder.enterDoWhileLoop(loop).passFlow()
graphBuilder.enterDoWhileLoop(loop).mergeIncomingFlow()
}
fun enterDoWhileLoopCondition(loop: FirLoop) {
val (loopBlockExitNode, loopConditionEnterNode) = graphBuilder.enterDoWhileLoopCondition(loop)
loopBlockExitNode.passFlow()
loopConditionEnterNode.passFlow()
loopBlockExitNode.mergeIncomingFlow()
loopConditionEnterNode.mergeIncomingFlow()
}
fun exitDoWhileLoop(loop: FirLoop) {
graphBuilder.exitDoWhileLoop(loop).passFlow()
graphBuilder.exitDoWhileLoop(loop).mergeIncomingFlow()
}
// ----------------------------------- Try-catch-finally -----------------------------------
fun enterTryExpression(tryExpression: FirTryExpression) {
graphBuilder.enterTryExpression(tryExpression).passFlow()
graphBuilder.enterTryExpression(tryExpression).mergeIncomingFlow()
}
fun exitTryMainBlock(tryExpression: FirTryExpression) {
graphBuilder.exitTryMainBlock(tryExpression).passFlow()
graphBuilder.exitTryMainBlock(tryExpression).mergeIncomingFlow()
}
fun enterCatchClause(catch: FirCatch) {
graphBuilder.enterCatchClause(catch).passFlow()
graphBuilder.enterCatchClause(catch).mergeIncomingFlow()
}
fun exitCatchClause(catch: FirCatch) {
graphBuilder.exitCatchClause(catch).passFlow()
graphBuilder.exitCatchClause(catch).mergeIncomingFlow()
}
fun enterFinallyBlock(tryExpression: FirTryExpression) {
// TODO
graphBuilder.enterFinallyBlock(tryExpression).passFlow()
graphBuilder.enterFinallyBlock(tryExpression).mergeIncomingFlow()
}
fun exitFinallyBlock(tryExpression: FirTryExpression) {
// TODO
graphBuilder.exitFinallyBlock(tryExpression).passFlow()
graphBuilder.exitFinallyBlock(tryExpression).mergeIncomingFlow()
}
fun exitTryExpression(tryExpression: FirTryExpression) {
// TODO
graphBuilder.exitTryExpression(tryExpression).passFlow()
graphBuilder.exitTryExpression(tryExpression).mergeIncomingFlow()
}
// ----------------------------------- Resolvable call -----------------------------------
fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) {
graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).passFlow()
graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).mergeIncomingFlow()
}
fun enterFunctionCall(functionCall: FirFunctionCall) {
@@ -409,7 +415,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
}
fun exitFunctionCall(functionCall: FirFunctionCall) {
val node = graphBuilder.exitFunctionCall(functionCall).passFlow(false)
val node = graphBuilder.exitFunctionCall(functionCall).mergeIncomingFlow()
if (functionCall.isBooleanNot()) {
exitBooleanNot(functionCall, node)
return
@@ -434,11 +440,11 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
}
fun exitConstExpresion(constExpression: FirConstExpression<*>) {
graphBuilder.exitConstExpresion(constExpression).passFlow()
graphBuilder.exitConstExpresion(constExpression).mergeIncomingFlow()
}
fun exitVariableDeclaration(variable: FirVariable<*>) {
val node = graphBuilder.exitVariableDeclaration(variable).passFlow(false)
val node = graphBuilder.exitVariableDeclaration(variable).mergeIncomingFlow()
val initializer = variable.initializer ?: return
/*
@@ -463,33 +469,34 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
}
fun exitVariableAssignment(assignment: FirVariableAssignment) {
graphBuilder.exitVariableAssignment(assignment).passFlow()
graphBuilder.exitVariableAssignment(assignment).mergeIncomingFlow()
val lhsVariable = variableStorage[assignment.resolvedSymbol ?: return] ?: return
val rhsVariable = variableStorage[assignment.rValue.resolvedSymbol ?: return]?.takeIf { !it.isSynthetic } ?: return
variableStorage.rebindAliasVariable(lhsVariable, rhsVariable)
}
fun exitThrowExceptionNode(throwExpression: FirThrowExpression) {
graphBuilder.exitThrowExceptionNode(throwExpression).passFlow()
graphBuilder.exitThrowExceptionNode(throwExpression).mergeIncomingFlow()
}
// ----------------------------------- Boolean operators -----------------------------------
fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
graphBuilder.enterBinaryAnd(binaryLogicExpression).passFlow()
graphBuilder.enterBinaryAnd(binaryLogicExpression).mergeIncomingFlow()
}
fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression)
leftNode.passFlow(true)
rightNode.passFlow(false)
leftNode.mergeIncomingFlow()
leftNode.flow.freeze()
rightNode.mergeIncomingFlow()
val leftOperandVariable = getOrCreateVariable(leftNode.previousNodes.first().fir)
val flow = approveFactsAndUpdateImplicitReceivers(leftOperandVariable, EqTrue, rightNode.flow)
rightNode.flow = flow.also { it.freeze() }
}
fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
val node = graphBuilder.exitBinaryAnd(binaryLogicExpression).passFlow(false)
val node = graphBuilder.exitBinaryAnd(binaryLogicExpression).mergeIncomingFlow()
val (leftVariable, rightVariable) = binaryLogicExpression.getVariables()
val flowFromLeft = node.leftOperandNode.flow
@@ -503,19 +510,19 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
val rightIsTrue = approveFact(rightVariable, EqTrue, flowFromRight) ?: mutableMapOf()
val rightIsFalse = approveFact(rightVariable, EqFalse, flowFromRight)
flowFromRight.approvedFacts.forEach { (variable, info) ->
val actualInfo = flowFromLeft.approvedFacts[variable]?.let { info - it } ?: info
flowFromRight.approvedInfos.forEach { (variable, info) ->
val actualInfo = flowFromLeft.approvedInfos[variable]?.let { info - it } ?: info
if (actualInfo.isNotEmpty) rightIsTrue.compute(variable) { _, existingInfo -> info + existingInfo }
}
logicSystem.andForVerifiedFacts(leftIsTrue, rightIsTrue)?.let {
for ((variable, info) in it) {
flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqTrue, variable, info))
flow.addNotApprovedFact(andVariable, ConditionalFirDataFlowInfo(EqTrue, variable, info))
}
}
logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse)?.let {
for ((variable, info) in it) {
flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info))
flow.addNotApprovedFact(andVariable, ConditionalFirDataFlowInfo(EqFalse, variable, info))
}
}
node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable)
@@ -527,15 +534,16 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression)
leftNode.passFlow(true)
rightNode.passFlow(false)
leftNode.mergeIncomingFlow()
leftNode.flow.freeze()
rightNode.mergeIncomingFlow()
val leftOperandVariable = getOrCreateVariable(leftNode.previousNodes.first().fir)
val flow = approveFactsAndUpdateImplicitReceivers(leftOperandVariable, EqFalse, rightNode.flow)
rightNode.flow = flow.also { it.freeze() }
}
fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
val node = graphBuilder.exitBinaryOr(binaryLogicExpression).passFlow(false)
val node = graphBuilder.exitBinaryOr(binaryLogicExpression).mergeIncomingFlow()
val (leftVariable, rightVariable) = binaryLogicExpression.getVariables()
val flowFromLeft = node.leftOperandNode.flow
@@ -551,12 +559,12 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
logicSystem.orForVerifiedFacts(leftIsTrue, rightIsTrue)?.let {
for ((variable, info) in it) {
flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqTrue, variable, info))
flow.addNotApprovedFact(orVariable, ConditionalFirDataFlowInfo(EqTrue, variable, info))
}
}
logicSystem.andForVerifiedFacts(leftIsFalse, rightIsFalse)?.let {
for ((variable, info) in it) {
flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info))
flow.addNotApprovedFact(orVariable, ConditionalFirDataFlowInfo(EqFalse, variable, info))
}
}
node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable)
@@ -571,11 +579,11 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
// ----------------------------------- Annotations -----------------------------------
fun enterAnnotationCall(annotationCall: FirAnnotationCall) {
graphBuilder.enterAnnotationCall(annotationCall).passFlow()
graphBuilder.enterAnnotationCall(annotationCall).mergeIncomingFlow()
}
fun exitAnnotationCall(annotationCall: FirAnnotationCall) {
graphBuilder.exitAnnotationCall(annotationCall).passFlow()
graphBuilder.exitAnnotationCall(annotationCall).mergeIncomingFlow()
}
// ----------------------------------- Init block -----------------------------------
@@ -585,7 +593,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
}
fun exitInitBlock(initBlock: FirAnonymousInitializer) {
graphBuilder.exitInitBlock(initBlock).passFlow()
graphBuilder.exitInitBlock(initBlock).mergeIncomingFlow()
}
// -------------------------------------------------------------------------------------------------------------------------
@@ -597,16 +605,14 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
getOrCreateVariable(leftOperand) to getOrCreateVariable(rightOperand)
private var CFGNode<*>.flow: Flow
get() = edges[this] ?: Flow.EMPTY
get() = flowOnNodes[this] ?: Flow.EMPTY
set(value) {
edges[this] = value
flowOnNodes[this] = value
}
private fun <T : CFGNode<*>> T.passFlow(shouldFreeze: Boolean = false): T = this.also { node ->
val previousFlows = node.usefulPreviousNodes.map { it.flow }
val flow = logicSystem.or(previousFlows).also {
if (shouldFreeze) it.freeze()
}
private fun <T : CFGNode<*>> T.mergeIncomingFlow(): T = this.also { node ->
val previousFlows = node.alivePreviousNodes.map { it.flow }
val flow = logicSystem.or(previousFlows)
if (previousFlows.size > 1) {
receiverStack.forEachIndexed { index, receiver ->
val variable = variableStorage[receiver.boundSymbol] ?: return@forEachIndexed
@@ -633,7 +639,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
}
private fun getOrCreateRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable =
variableStorage.getOrCreateNewRealVariable(symbol).real
variableStorage.getOrCreateNewRealVariable(symbol).aliasedVariable
private fun getOrCreateVariable(fir: FirElement): DataFlowVariable {
val symbol = fir.resolvedSymbol
@@ -695,7 +701,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
private fun updateReceiverType(flow: Flow, variable: DataFlowVariable) {
val symbol = (variable.fir as? FirSymbolOwner<*>)?.symbol ?: return
val index = receiverStack.getReceiverIndex(symbol) ?: return
val info = flow.approvedFacts[variable] ?: return
val info = flow.approvedInfos[variable] ?: return
updateReceiverType(index, info)
}
@@ -703,7 +709,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
val types = info.exactType.toMutableList().also {
it += receiverStack.getOriginalType(index)
}
receiverStack.replaceReceiverType(index, context.myIntersectTypes(types)!!)
receiverStack.replaceReceiverType(index, context.intersectTypesOrNull(types)!!)
}
@@ -8,13 +8,13 @@ package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.render
data class UnapprovedFirDataFlowInfo(
data class ConditionalFirDataFlowInfo(
val condition: Condition,
val variable: DataFlowVariable,
val info: FirDataFlowInfo
) {
fun invert(): UnapprovedFirDataFlowInfo {
return UnapprovedFirDataFlowInfo(condition.invert(), variable, info)
fun invert(): ConditionalFirDataFlowInfo {
return ConditionalFirDataFlowInfo(condition.invert(), variable, info)
}
override fun toString(): String {
@@ -8,8 +8,8 @@ package org.jetbrains.kotlin.fir.resolve.dfa
import com.google.common.collect.HashMultimap
class Flow(
val approvedFacts: MutableMap<DataFlowVariable, FirDataFlowInfo> = mutableMapOf(),
val notApprovedFacts: HashMultimap<DataFlowVariable, UnapprovedFirDataFlowInfo> = HashMultimap.create(),
val approvedInfos: MutableMap<DataFlowVariable, FirDataFlowInfo> = mutableMapOf(),
val conditionalInfos: HashMultimap<DataFlowVariable, ConditionalFirDataFlowInfo> = HashMultimap.create(),
private var state: State = State.Building
) {
private val isFrozen: Boolean get() = state == State.Frozen
@@ -20,46 +20,46 @@ class Flow(
fun addApprovedFact(variable: DataFlowVariable, info: FirDataFlowInfo): Flow {
if (isFrozen) return copyForBuilding().addApprovedFact(variable, info)
approvedFacts.compute(variable) { _, existingInfo ->
approvedInfos.compute(variable) { _, existingInfo ->
if (existingInfo == null) info
else existingInfo + info
}
return this
}
fun addNotApprovedFact(variable: DataFlowVariable, info: UnapprovedFirDataFlowInfo): Flow {
fun addNotApprovedFact(variable: DataFlowVariable, info: ConditionalFirDataFlowInfo): Flow {
if (isFrozen) return copyForBuilding().addNotApprovedFact(variable, info)
notApprovedFacts.put(variable, info)
conditionalInfos.put(variable, info)
return this
}
fun copyNotApprovedFacts(
from: DataFlowVariable,
to: DataFlowVariable,
transform: ((UnapprovedFirDataFlowInfo) -> UnapprovedFirDataFlowInfo)? = null
transform: ((ConditionalFirDataFlowInfo) -> ConditionalFirDataFlowInfo)? = null
): Flow {
if (isFrozen)
return copyForBuilding().copyNotApprovedFacts(from, to, transform)
var facts = if (from.isSynthetic) {
notApprovedFacts.removeAll(from)
conditionalInfos.removeAll(from)
} else {
notApprovedFacts[from]
conditionalInfos[from]
}
if (transform != null) {
facts = facts.mapTo(mutableSetOf(), transform)
}
notApprovedFacts.putAll(to, facts)
conditionalInfos.putAll(to, facts)
return this
}
fun approvedFacts(variable: DataFlowVariable): FirDataFlowInfo? {
return approvedFacts[variable]
return approvedInfos[variable]
}
fun removeVariableFromFlow(variable: DataFlowVariable): Flow {
if (isFrozen) return copyForBuilding().removeVariableFromFlow(variable)
notApprovedFacts.removeAll(variable)
approvedFacts.remove(variable)
conditionalInfos.removeAll(variable)
approvedInfos.remove(variable)
return this
}
@@ -79,7 +79,7 @@ class Flow(
}
fun copyForBuilding(): Flow {
return Flow(approvedFacts.toMutableMap(), notApprovedFacts.copy(), State.Building)
return Flow(approvedInfos.toMutableMap(), conditionalInfos.copy(), State.Building)
}
}
@@ -14,28 +14,27 @@ class LogicSystem(private val context: DataFlowInferenceContext) {
storages.singleOrNull()?.let {
return it
}
val approvedFacts = mutableMapOf<DataFlowVariable, FirDataFlowInfo>().apply {
storages.map { it.approvedFacts.keys }
.intersectSets()
.forEach { variable ->
val infos = storages.map { it.approvedFacts[variable]!! }
if (infos.isNotEmpty()) {
this[variable] = context.or(infos)
}
val approvedFacts = mutableMapOf<DataFlowVariable, FirDataFlowInfo>()
storages.map { it.approvedInfos.keys }
.intersectSets()
.forEach { variable ->
val infos = storages.map { it.approvedInfos[variable]!! }
if (infos.isNotEmpty()) {
approvedFacts[variable] = context.or(infos)
}
}
val notApprovedFacts = HashMultimap.create<DataFlowVariable, UnapprovedFirDataFlowInfo>()
.apply {
storages.map { it.notApprovedFacts.keySet() }
.intersectSets()
.forEach { variable ->
val infos = storages.map { it.notApprovedFacts[variable] }.intersectSets()
if (infos.isNotEmpty()) {
this.putAll(variable, infos)
}
}
}
val notApprovedFacts = HashMultimap.create<DataFlowVariable, ConditionalFirDataFlowInfo>()
storages.map { it.conditionalInfos.keySet() }
.intersectSets()
.forEach { variable ->
val infos = storages.map { it.conditionalInfos[variable] }.intersectSets()
if (infos.isNotEmpty()) {
notApprovedFacts.putAll(variable, infos)
}
}
return Flow(approvedFacts, notApprovedFacts)
}
@@ -70,7 +69,7 @@ class LogicSystem(private val context: DataFlowInferenceContext) {
}
fun approveFactsInsideFlow(variable: DataFlowVariable, condition: Condition, flow: Flow): Pair<Flow, Collection<DataFlowVariable>> {
val notApprovedFacts: Set<UnapprovedFirDataFlowInfo> = flow.notApprovedFacts[variable]
val notApprovedFacts: Set<ConditionalFirDataFlowInfo> = flow.conditionalInfos[variable]
if (notApprovedFacts.isEmpty()) {
return flow to emptyList()
}
@@ -87,10 +86,10 @@ class LogicSystem(private val context: DataFlowInferenceContext) {
newFacts.asMap().forEach { (variable, infos) ->
@Suppress("NAME_SHADOWING")
val infos = ArrayList(infos)
flow.approvedFacts[variable]?.let {
flow.approvedInfos[variable]?.let {
infos.add(it)
}
flow.approvedFacts[variable] = context.and(infos)
flow.approvedInfos[variable] = context.and(infos)
if (variable.isThisReference) {
updatedReceivers += variable
}
@@ -99,7 +98,7 @@ class LogicSystem(private val context: DataFlowInferenceContext) {
}
fun approveFact(variable: DataFlowVariable, condition: Condition, flow: Flow): MutableMap<DataFlowVariable, FirDataFlowInfo> {
val notApprovedFacts: Set<UnapprovedFirDataFlowInfo> = flow.notApprovedFacts[variable]
val notApprovedFacts: Set<ConditionalFirDataFlowInfo> = flow.conditionalInfos[variable]
if (notApprovedFacts.isEmpty()) {
return mutableMapOf()
}
@@ -31,12 +31,8 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
var isDead: Boolean = false
}
val CFGNode<*>.usefulFollowingNodes: List<CFGNode<*>> get() = if (isDead) followingNodes else followingNodes.filterNot { it.isDead }
val CFGNode<*>.usefulPreviousNodes: List<CFGNode<*>> get() = if (isDead) previousNodes else previousNodes.filterNot { it.isDead }
interface ReturnableNothingNode {
val returnsNothing: Boolean
}
val CFGNode<*>.aliveFollowingNodes: List<CFGNode<*>> get() = if (isDead) followingNodes else followingNodes.filterNot { it.isDead }
val CFGNode<*>.alivePreviousNodes: List<CFGNode<*>> get() = if (isDead) previousNodes else previousNodes.filterNot { it.isDead }
interface EnterNode
interface ExitNode
@@ -48,8 +44,8 @@ class FunctionExitNode(owner: ControlFlowGraph, override val fir: FirFunction<*>
// ----------------------------------- Property -----------------------------------
class PropertyEnterNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int) : CFGNode<FirProperty>(owner, level), EnterNode
class PropertyExitNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int) : CFGNode<FirProperty>(owner, level), ExitNode
class PropertyInitializerEnterNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int) : CFGNode<FirProperty>(owner, level), EnterNode
class PropertyInitializerExitNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int) : CFGNode<FirProperty>(owner, level), ExitNode
// ----------------------------------- Init -----------------------------------
@@ -125,24 +121,20 @@ class ConstExpressionNode(owner: ControlFlowGraph, override val fir: FirConstExp
class QualifiedAccessNode(
owner: ControlFlowGraph,
override val fir: FirQualifiedAccessExpression,
override val returnsNothing: Boolean,
level: Int
) : CFGNode<FirQualifiedAccessExpression>(owner, level), ReturnableNothingNode
) : CFGNode<FirQualifiedAccessExpression>(owner, level)
class FunctionCallNode(
owner: ControlFlowGraph,
override val fir: FirFunctionCall,
override val returnsNothing: Boolean,
level: Int
) : CFGNode<FirFunctionCall>(owner, level), ReturnableNothingNode
) : CFGNode<FirFunctionCall>(owner, level)
class ThrowExceptionNode(
owner: ControlFlowGraph,
override val fir: FirThrowExpression,
level: Int
) : CFGNode<FirThrowExpression>(owner, level), ReturnableNothingNode {
override val returnsNothing: Boolean get() = true
}
) : CFGNode<FirThrowExpression>(owner, level)
class StubNode(owner: ControlFlowGraph, level: Int) : CFGNode<FirStub>(owner, level) {
init {
@@ -39,7 +39,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
private val binaryAndExitNodes: Stack<BinaryAndExitNode> = stackOf()
private val binaryOrExitNodes: Stack<BinaryOrExitNode> = stackOf()
private val topLevelVariableExitNodes: Stack<PropertyExitNode> = stackWithCallbacks(
private val topLevelVariableInitializerExitNodes: Stack<PropertyInitializerExitNode> = stackWithCallbacks(
pushCallback = { exitNodes.push(it) },
popCallback = { exitNodes.pop() }
)
@@ -127,11 +127,11 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
// ----------------------------------- Property -----------------------------------
fun enterProperty(property: FirProperty): PropertyEnterNode {
fun enterProperty(property: FirProperty): PropertyInitializerEnterNode {
graphs.push(ControlFlowGraph("val ${property.name}"))
val enterNode = createPropertyEnterNode(property)
val exitNode = createPropertyExitNode(property)
topLevelVariableExitNodes.push(exitNode)
val enterNode = createPropertyInitializerEnterNode(property)
val exitNode = createPropertyInitializerExitNode(property)
topLevelVariableInitializerExitNodes.push(exitNode)
lexicalScopes.push(stackOf(enterNode))
graph.enterNode = enterNode
graph.exitNode = exitNode
@@ -139,8 +139,8 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
return enterNode
}
fun exitProperty(property: FirProperty): Pair<PropertyExitNode, ControlFlowGraph> {
val topLevelVariableExitNode = topLevelVariableExitNodes.pop().also {
fun exitProperty(property: FirProperty): Pair<PropertyInitializerExitNode, ControlFlowGraph> {
val topLevelVariableExitNode = topLevelVariableInitializerExitNodes.pop().also {
addNewSimpleNode(it)
it.markAsDeadIfNecessary()
}
@@ -169,7 +169,6 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
is FirBreakExpression -> loopExitNodes[jump.target.labeledElement]
else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}")
}
addNodeWithJump(node, nextNode)
return node
}
@@ -456,7 +455,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression): QualifiedAccessNode {
val returnsNothing = qualifiedAccessExpression.resultType.isNothing
val node = createQualifiedAccessNode(qualifiedAccessExpression, returnsNothing)
val node = createQualifiedAccessNode(qualifiedAccessExpression)
if (returnsNothing) {
addNodeThatReturnsNothing(node)
} else {
@@ -467,7 +466,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
fun exitFunctionCall(functionCall: FirFunctionCall): FunctionCallNode {
val returnsNothing = functionCall.resultType.isNothing
val node = createFunctionCallNode(functionCall, returnsNothing)
val node = createFunctionCallNode(functionCall)
if (returnsNothing) {
addNodeThatReturnsNothing(node)
} else {
@@ -41,18 +41,18 @@ abstract class ControlFlowGraphNodeBuilder {
protected fun createJumpNode(fir: FirJump<*>): JumpNode =
JumpNode(graph, fir, levelCounter)
protected fun createQualifiedAccessNode(
fir: FirQualifiedAccessExpression,
returnsNothing: Boolean
): QualifiedAccessNode = QualifiedAccessNode(graph, fir, returnsNothing, levelCounter)
protected fun createQualifiedAccessNode(fir: FirQualifiedAccessExpression): QualifiedAccessNode =
QualifiedAccessNode(graph, fir, levelCounter)
protected fun createBlockEnterNode(fir: FirBlock): BlockEnterNode = BlockEnterNode(graph, fir, levelCounter)
protected fun createBlockExitNode(fir: FirBlock): BlockExitNode = BlockExitNode(graph, fir, levelCounter)
protected fun createPropertyExitNode(fir: FirProperty): PropertyExitNode = PropertyExitNode(graph, fir, levelCounter)
protected fun createPropertyInitializerExitNode(fir: FirProperty): PropertyInitializerExitNode =
PropertyInitializerExitNode(graph, fir, levelCounter)
protected fun createPropertyEnterNode(fir: FirProperty): PropertyEnterNode = PropertyEnterNode(graph, fir, levelCounter)
protected fun createPropertyInitializerEnterNode(fir: FirProperty): PropertyInitializerEnterNode =
PropertyInitializerEnterNode(graph, fir, levelCounter)
protected fun createFunctionEnterNode(fir: FirFunction<*>, isInPlace: Boolean): FunctionEnterNode =
FunctionEnterNode(graph, fir, levelCounter).also {
@@ -66,7 +66,7 @@ abstract class ControlFlowGraphNodeBuilder {
if (!isInPlace) {
graph.exitNode = it
}
}
}
protected fun createBinaryOrEnterNode(fir: FirBinaryLogicExpression): BinaryOrEnterNode =
BinaryOrEnterNode(graph, fir, levelCounter)
@@ -107,8 +107,8 @@ abstract class ControlFlowGraphNodeBuilder {
protected fun createLoopBlockExitNode(fir: FirLoop): LoopBlockExitNode =
LoopBlockExitNode(graph, fir, levelCounter)
protected fun createFunctionCallNode(fir: FirFunctionCall, returnsNothing: Boolean): FunctionCallNode =
FunctionCallNode(graph, fir, returnsNothing, levelCounter)
protected fun createFunctionCallNode(fir: FirFunctionCall): FunctionCallNode =
FunctionCallNode(graph, fir, levelCounter)
protected fun createVariableAssignmentNode(fir: FirVariableAssignment): VariableAssignmentNode =
VariableAssignmentNode(graph, fir, levelCounter)
@@ -141,8 +141,8 @@ fun CFGNode<*>.render(): String =
is BinaryOrEnterRightOperandNode -> "Enter right part of ||"
is BinaryOrExitNode -> "Exit ||"
is PropertyEnterNode -> "Enter property"
is PropertyExitNode -> "Exit property"
is PropertyInitializerEnterNode -> "Enter property"
is PropertyInitializerExitNode -> "Exit property"
is InitBlockEnterNode -> "Enter init block"
is InitBlockExitNode -> "Exit init block"
is AnnotationEnterNode -> "Enter annotation"