FIR: Introduce DataFlowAnalyzerContext

This commit is contained in:
Denis Zharkov
2020-03-20 14:36:29 +03:00
parent dd628bc64a
commit f119839891
3 changed files with 65 additions and 39 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
import org.jetbrains.kotlin.fir.contracts.description.ConeConditionalEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference
@@ -29,55 +30,69 @@ import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
class DataFlowAnalyzerContext<FLOW : Flow>(
val graphBuilder: ControlFlowGraphBuilder,
val variableStorage: VariableStorage,
val flowOnNodes: MutableMap<CFGNode<*>, FLOW>,
val variablesForWhenConditions: MutableMap<WhenBranchConditionExitNode, DataFlowVariable>
) {
companion object {
fun <FLOW : Flow> empty(session: FirSession) =
DataFlowAnalyzerContext<FLOW>(
ControlFlowGraphBuilder(), VariableStorage(session),
mutableMapOf(), mutableMapOf()
)
}
}
@OptIn(DfaInternals::class)
abstract class FirDataFlowAnalyzer<FLOW : Flow>(
protected val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents
protected val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents,
private val context: DataFlowAnalyzerContext<FLOW>
) {
companion object {
internal val KOTLIN_BOOLEAN_NOT = CallableId(FqName("kotlin"), FqName("Boolean"), Name.identifier("not"))
fun createFirDataFlowAnalyzer(
components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents
): FirDataFlowAnalyzer<*> = object : FirDataFlowAnalyzer<PersistentFlow>(components) {
private val receiverStack: ImplicitReceiverStackImpl
get() = components.implicitReceiverStack as ImplicitReceiverStackImpl
components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents,
dataFlowAnalyzerContext: DataFlowAnalyzerContext<PersistentFlow>
): FirDataFlowAnalyzer<*> =
object : FirDataFlowAnalyzer<PersistentFlow>(components, dataFlowAnalyzerContext) {
private val receiverStack: ImplicitReceiverStackImpl
get() = components.implicitReceiverStack as ImplicitReceiverStackImpl
override val logicSystem: PersistentLogicSystem = object : PersistentLogicSystem(components.inferenceComponents.ctx) {
override fun processUpdatedReceiverVariable(flow: PersistentFlow, variable: RealVariable) {
val symbol = variable.identifier.symbol
override val logicSystem: PersistentLogicSystem = object : PersistentLogicSystem(components.inferenceComponents.ctx) {
override fun processUpdatedReceiverVariable(flow: PersistentFlow, variable: RealVariable) {
val symbol = variable.identifier.symbol
val index = receiverStack.getReceiverIndex(symbol) ?: return
val info = flow.getTypeStatement(variable)
val index = receiverStack.getReceiverIndex(symbol) ?: return
val info = flow.getTypeStatement(variable)
if (info == null) {
receiverStack.replaceReceiverType(index, receiverStack.getOriginalType(index))
} else {
val types = info.exactType.toMutableList().also {
it += receiverStack.getOriginalType(index)
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)!!)
}
receiverStack.replaceReceiverType(index, context.intersectTypesOrNull(types)!!)
}
}
override fun updateAllReceivers(flow: PersistentFlow) {
receiverStack.forEach {
variableStorage.getRealVariable(it.boundSymbol, it.receiverExpression, flow)?.let { variable ->
processUpdatedReceiverVariable(flow, variable)
override fun updateAllReceivers(flow: PersistentFlow) {
receiverStack.forEach {
variableStorage.getRealVariable(it.boundSymbol, it.receiverExpression, flow)?.let { variable ->
processUpdatedReceiverVariable(flow, variable)
}
}
}
}
}
}
}
protected abstract val logicSystem: LogicSystem<FLOW>
private val context: ConeInferenceContext = components.inferenceComponents.ctx
private val graphBuilder = ControlFlowGraphBuilder()
protected val variableStorage: VariableStorage = VariableStorage(components.session)
private val flowOnNodes = mutableMapOf<CFGNode<*>, FLOW>()
private val variablesForWhenConditions = mutableMapOf<WhenBranchConditionExitNode, DataFlowVariable>()
private val graphBuilder get() = context.graphBuilder
protected val variableStorage get() = context.variableStorage
private var contractDescriptionVisitingMode = false
@@ -129,7 +144,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
}
}
if (graphBuilder.isTopLevel()) {
flowOnNodes.clear()
context.flowOnNodes.clear()
variableStorage.reset()
graphBuilder.reset()
}
@@ -388,7 +403,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val node = graphBuilder.enterWhenBranchCondition(whenBranch).mergeIncomingFlow(updateReceivers = true)
val previousNode = node.previousNodes.single()
if (previousNode is WhenBranchConditionExitNode) {
val conditionVariable = variablesForWhenConditions.remove(previousNode)!!
val conditionVariable = context.variablesForWhenConditions.remove(previousNode)!!
node.flow = logicSystem.approveStatementsInsideFlow(
node.flow,
conditionVariable eq false,
@@ -404,7 +419,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val conditionExitFlow = conditionExitNode.flow
val conditionVariable = variableStorage.getOrCreateVariable(conditionExitFlow, whenBranch.condition)
variablesForWhenConditions[conditionExitNode] = conditionVariable
context.variablesForWhenConditions[conditionExitNode] = conditionVariable
branchEnterNode.flow = logicSystem.approveStatementsInsideFlow(
conditionExitFlow,
conditionVariable eq true,
@@ -424,7 +439,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
// 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) {
val conditionVariable = variablesForWhenConditions.remove(previousConditionExitNode)!!
val conditionVariable = context.variablesForWhenConditions.remove(previousConditionExitNode)!!
syntheticElseNode.flow = logicSystem.approveStatementsInsideFlow(
previousConditionExitNode.flow,
conditionVariable eq false,
@@ -713,7 +728,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
exitVariableInitialization(node, assignment.rValue, property, assignment)
}
private fun exitVariableInitialization(node: CFGNode<*>, initializer: FirExpression, property: FirProperty, assignment: FirVariableAssignment?) {
private fun exitVariableInitialization(
node: CFGNode<*>,
initializer: FirExpression,
property: FirProperty,
assignment: FirVariableAssignment?
) {
val flow = node.flow
val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAlias(flow, property.symbol, assignment ?: property)
val isAssignment = assignment != null
@@ -905,9 +925,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
// ------------------------------------------------------ Utils ------------------------------------------------------
private var CFGNode<*>.flow: FLOW
get() = flowOnNodes.getValue(this.origin)
get() = context.flowOnNodes.getValue(this.origin)
set(value) {
flowOnNodes[this.origin] = value
context.flowOnNodes[this.origin] = value
}
private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) firstPreviousNode else this
@@ -941,7 +961,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
return logicSystem.forkFlow(this)
}
private val CFGNode<*>.previousFlow : FLOW
private val CFGNode<*>.previousFlow: FLOW
get() = firstPreviousNode.flow
}
@@ -10,7 +10,9 @@ import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionStageRunner
import org.jetbrains.kotlin.fir.resolve.dfa.DataFlowAnalyzerContext
import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
import org.jetbrains.kotlin.fir.resolve.dfa.PersistentFlow
import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter
import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
import org.jetbrains.kotlin.fir.resolve.transformers.*
@@ -101,6 +103,7 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
class BodyResolveContext(
val returnTypeCalculator: ReturnTypeCalculator,
val dataFlowAnalyzerContext: DataFlowAnalyzerContext<PersistentFlow>,
val targetedLocalClasses: Set<FirClass<*>> = emptySet()
) {
val fileImportsScope: MutableList<FirScope> = mutableListOf()
@@ -197,7 +200,7 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
fun createSnapshotForLocalClasses(
returnTypeCalculator: ReturnTypeCalculator,
targetedLocalClasses: Set<FirClass<*>>
) = BodyResolveContext(returnTypeCalculator, targetedLocalClasses).apply {
) = BodyResolveContext(returnTypeCalculator, dataFlowAnalyzerContext, targetedLocalClasses).apply {
fileImportsScope.addAll(this@BodyResolveContext.fileImportsScope)
typeParametersScopes = this@BodyResolveContext.typeParametersScopes
localScopes = this@BodyResolveContext.localScopes
@@ -239,7 +242,8 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
FirTypeResolveScopeForBodyResolve(this), session
)
override val callCompleter: FirCallCompleter = FirCallCompleter(transformer, this)
override val dataFlowAnalyzer: FirDataFlowAnalyzer<*> = FirDataFlowAnalyzer.createFirDataFlowAnalyzer(this)
override val dataFlowAnalyzer: FirDataFlowAnalyzer<*> =
FirDataFlowAnalyzer.createFirDataFlowAnalyzer(this, context.dataFlowAnalyzerContext)
override val syntheticCallGenerator: FirSyntheticCallGenerator = FirSyntheticCallGenerator(this)
override val integerLiteralTypeApproximator: IntegerLiteralTypeApproximationTransformer =
IntegerLiteralTypeApproximationTransformer(symbolProvider, inferenceComponents.ctx)
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.dfa.DataFlowAnalyzerContext
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorForFullBodyResolve
import org.jetbrains.kotlin.fir.scopes.addImportingScopes
@@ -31,7 +32,8 @@ open class FirBodyResolveTransformer(
) : FirAbstractBodyResolveTransformer(phase) {
private var packageFqName = FqName.ROOT
final override val context: BodyResolveContext = outerBodyResolveContext ?: BodyResolveContext(returnTypeCalculator)
final override val context: BodyResolveContext =
outerBodyResolveContext ?: BodyResolveContext(returnTypeCalculator, DataFlowAnalyzerContext.empty(session))
final override val components: BodyResolveTransformerComponents =
BodyResolveTransformerComponents(session, scopeSession, this, context)