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