[FIR] Cleanup DFA code and rename some classes for better understanding

This commit is contained in:
Dmitriy Novozhilov
2020-01-21 12:16:07 +03:00
parent 16a9ca5912
commit 19e0b8039b
14 changed files with 449 additions and 513 deletions
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.calls.InferenceComponents import org.jetbrains.kotlin.fir.resolve.calls.InferenceComponents
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionStageRunner import org.jetbrains.kotlin.fir.resolve.calls.ResolutionStageRunner
import org.jetbrains.kotlin.fir.resolve.dfa.new.FirDataFlowAnalyzer import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
import org.jetbrains.kotlin.fir.resolve.transformers.* import org.jetbrains.kotlin.fir.resolve.transformers.*
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef
@@ -5,10 +5,10 @@
package org.jetbrains.kotlin.fir.resolve.dfa package org.jetbrains.kotlin.fir.resolve.dfa
enum class Condition { enum class Operation {
EqTrue, EqFalse, EqNull, NotEqNull; EqTrue, EqFalse, EqNull, NotEqNull;
fun invert(): Condition = when (this) { fun invert(): Operation = when (this) {
EqTrue -> EqFalse EqTrue -> EqFalse
EqFalse -> EqTrue EqFalse -> EqTrue
EqNull -> NotEqNull EqNull -> NotEqNull
@@ -21,6 +21,4 @@ enum class Condition {
EqNull -> "== Null" EqNull -> "== Null"
NotEqNull -> "!= Null" NotEqNull -> "!= Null"
} }
} }
fun Boolean.toEqBoolean(): Condition = if (this) Condition.EqTrue else Condition.EqFalse
@@ -1,30 +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.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeIntersector
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
fun ConeInferenceContext.commonSuperTypeOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) {
0 -> null
1 -> types.first()
else -> with(NewCommonSuperTypeCalculator) {
commonSuperType(types) as ConeKotlinType
}
}
}
fun ConeInferenceContext.intersectTypesOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) {
0 -> null
1 -> types.first()
else -> ConeTypeIntersector.intersectTypes(this, types)
}
}
@@ -0,0 +1,9 @@
/*
* Copyright 2010-2020 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
@Experimental
annotation class DfaInternals
@@ -1,9 +1,9 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * 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.new package org.jetbrains.kotlin.fir.resolve.dfa
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
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStackImpl import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStackImpl
import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.resolve.dfa.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* 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.buildContractFir
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.createArgumentsMapping import org.jetbrains.kotlin.fir.resolve.dfa.contracts.createArgumentsMapping
@@ -29,10 +28,9 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.transformSingle 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
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import kotlin.IllegalArgumentException
@UseExperimental(DfaInternals::class)
abstract class FirDataFlowAnalyzer<FLOW : Flow>( abstract class FirDataFlowAnalyzer<FLOW : Flow>(
protected val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents protected val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents
) { ) {
@@ -44,12 +42,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
): FirDataFlowAnalyzer<*> = object : FirDataFlowAnalyzer<PersistentFlow>(components) { ): FirDataFlowAnalyzer<*> = object : FirDataFlowAnalyzer<PersistentFlow>(components) {
private val receiverStack: ImplicitReceiverStackImpl = components.implicitReceiverStack as ImplicitReceiverStackImpl private val receiverStack: ImplicitReceiverStackImpl = components.implicitReceiverStack as ImplicitReceiverStackImpl
override val logicSystem: PersistentLogicSystem = object : PersistentLogicSystem(any, 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.getKnownInfo(variable) val info = flow.getTypeStatement(variable)
if (info == null) { if (info == null) {
receiverStack.replaceReceiverType(index, receiverStack.getOriginalType(index)) receiverStack.replaceReceiverType(index, receiverStack.getOriginalType(index))
@@ -91,7 +89,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
*/ */
val symbol: AbstractFirBasedSymbol<*> = qualifiedAccessExpression.symbol ?: return null val symbol: AbstractFirBasedSymbol<*> = qualifiedAccessExpression.symbol ?: return null
val variable = variableStorage[symbol, qualifiedAccessExpression] ?: return null val variable = variableStorage[symbol, qualifiedAccessExpression] ?: return null
return graphBuilder.lastNode.flow.getKnownInfo(variable)?.exactType return graphBuilder.lastNode.flow.getTypeStatement(variable)?.exactType
} }
fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): List<FirStatement> { fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): List<FirStatement> {
@@ -165,31 +163,31 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall) val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall)
val isNotNullCheck = operation == FirOperation.IS && type.nullability == ConeNullability.NOT_NULL val isNotNullCheck = operation == FirOperation.IS && type.nullability == ConeNullability.NOT_NULL
if (operandVariable.isReal()) { if (operandVariable.isReal()) {
val hasTypeInfo = operandVariable has type val hasTypeInfo = operandVariable typeEq type
val hasNotTypeInfo = operandVariable hasNot type val hasNotTypeInfo = operandVariable typeNotEq type
fun chooseInfo(trueBranch: Boolean) = fun chooseInfo(trueBranch: Boolean) =
if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) hasTypeInfo else hasNotTypeInfo if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) hasTypeInfo else hasNotTypeInfo
flow.addLogicStatement((expressionVariable eq true) implies chooseInfo(true)) flow.addImplication((expressionVariable eq true) implies chooseInfo(true))
flow.addLogicStatement((expressionVariable eq false) implies chooseInfo(false)) flow.addImplication((expressionVariable eq false) implies chooseInfo(false))
if (operation == FirOperation.NOT_IS && type == nullableNothing) { if (operation == FirOperation.NOT_IS && type == nullableNothing) {
flow.addKnownInfo(operandVariable has any) flow.addTypeStatement(operandVariable typeEq any)
} }
if (isNotNullCheck) { if (isNotNullCheck) {
flow.addLogicStatement((expressionVariable eq true) implies (operandVariable has any)) } flow.addImplication((expressionVariable eq true) implies (operandVariable typeEq any)) }
} else { } else {
if (isNotNullCheck) { if (isNotNullCheck) {
flow.addLogicStatement((expressionVariable eq true) implies (operandVariable notEq null)) flow.addImplication((expressionVariable eq true) implies (operandVariable notEq null))
} }
} }
} }
FirOperation.AS -> { FirOperation.AS -> {
if (operandVariable.isReal()) { if (operandVariable.isReal()) {
flow.addKnownInfo(operandVariable has type) flow.addTypeStatement(operandVariable typeEq type)
} else { } else {
logicSystem.approveStatementsInsideFlow( logicSystem.approveStatementsInsideFlow(
flow, flow,
@@ -203,11 +201,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
FirOperation.SAFE_AS -> { FirOperation.SAFE_AS -> {
val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall) val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall)
if (operandVariable.isReal()) { if (operandVariable.isReal()) {
flow.addLogicStatement((expressionVariable notEq null) implies (operandVariable has type)) flow.addImplication((expressionVariable notEq null) implies (operandVariable typeEq type))
flow.addLogicStatement((expressionVariable eq null) implies (operandVariable hasNot type)) flow.addImplication((expressionVariable eq null) implies (operandVariable typeNotEq type))
} else { } else {
if (type.nullability == ConeNullability.NOT_NULL) { if (type.nullability == ConeNullability.NOT_NULL) {
flow.addLogicStatement((expressionVariable notEq null) implies (operandVariable notEq null)) flow.addImplication((expressionVariable notEq null) implies (operandVariable notEq null))
} }
} }
} }
@@ -246,9 +244,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val flow = node.flow val flow = node.flow
val operandVariable = variableStorage.getOrCreateVariable(operand) val operandVariable = variableStorage.getOrCreateVariable(operand)
// expression == const -> expression != null // expression == const -> expression != null
flow.addLogicStatement((expressionVariable eq isEq) implies (operandVariable notEq null)) flow.addImplication((expressionVariable eq isEq) implies (operandVariable notEq null))
if (operandVariable is RealVariable) { if (operandVariable is RealVariable) {
flow.addLogicStatement((expressionVariable eq isEq) implies (operandVariable has any)) flow.addImplication((expressionVariable eq isEq) implies (operandVariable typeEq any))
} }
// propagating facts for (... == true) and (... == false) // propagating facts for (... == true) and (... == false)
@@ -256,7 +254,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val constValue = const.value as Boolean val constValue = const.value as Boolean
val shouldInvert = isEq xor constValue val shouldInvert = isEq xor constValue
logicSystem.translateConditionalVariableInStatements( logicSystem.translateVariableFromConditionInStatements(
flow, flow,
operandVariable, operandVariable,
expressionVariable, expressionVariable,
@@ -291,21 +289,21 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
false -> operandVariable notEq null false -> operandVariable notEq null
} }
logicSystem.approvePredicate(flow, predicate).forEach { effect -> logicSystem.approveOperationStatement(flow, predicate).forEach { effect ->
flow.addLogicStatement((expressionVariable eq true) implies effect) flow.addImplication((expressionVariable eq true) implies effect)
flow.addLogicStatement((expressionVariable eq false) implies effect.invert()) flow.addImplication((expressionVariable eq false) implies effect.invert())
} }
flow.addLogicStatement((expressionVariable eq isEq) implies (operandVariable eq null)) flow.addImplication((expressionVariable eq isEq) implies (operandVariable eq null))
flow.addLogicStatement((expressionVariable notEq isEq) implies (operandVariable notEq null)) flow.addImplication((expressionVariable notEq isEq) implies (operandVariable notEq null))
if (operandVariable is RealVariable) { if (operandVariable is RealVariable) {
flow.addLogicStatement((expressionVariable eq isEq) implies (operandVariable hasNot any)) flow.addImplication((expressionVariable eq isEq) implies (operandVariable typeNotEq any))
flow.addLogicStatement((expressionVariable notEq isEq) implies (operandVariable has any)) flow.addImplication((expressionVariable notEq isEq) implies (operandVariable typeEq any))
// TODO: design do we need casts to Nothing? // TODO: design do we need casts to Nothing?
// flow.addLogicStatement((expressionVariable eq !isEq) implies (operandVariable has nullableNothing)) // flow.addImplication((expressionVariable eq !isEq) implies (operandVariable typeEq nullableNothing))
// flow.addLogicStatement((expressionVariable notEq !isEq) implies (operandVariable hasNot nullableNothing)) // flow.addImplication((expressionVariable notEq !isEq) implies (operandVariable typeNotEq nullableNothing))
} }
node.flow = flow node.flow = flow
} }
@@ -323,7 +321,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val node = graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow() val node = graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow()
val argument = checkNotNullCall.argument val argument = checkNotNullCall.argument
val operandVariable = variableStorage.getOrCreateRealVariable(argument.symbol, argument) ?: return val operandVariable = variableStorage.getOrCreateRealVariable(argument.symbol, argument) ?: return
node.flow.addKnownInfo(operandVariable has any) node.flow.addTypeStatement(operandVariable typeEq any)
} }
// ----------------------------------- When ----------------------------------- // ----------------------------------- When -----------------------------------
@@ -504,7 +502,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
if (shouldFork) { if (shouldFork) {
flow = logicSystem.forkFlow(flow) flow = logicSystem.forkFlow(flow)
} }
flow.addKnownInfo(variable has type) flow.addTypeStatement(variable typeEq type)
} }
is SyntheticVariable -> { is SyntheticVariable -> {
flow = logicSystem.approveStatementsInsideFlow( flow = logicSystem.approveStatementsInsideFlow(
@@ -544,9 +542,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
is RealVariable -> variable.explicitReceiverVariable!! is RealVariable -> variable.explicitReceiverVariable!!
is SyntheticVariable -> variableStorage.getOrCreateVariable(qualifiedAccess.explicitReceiver!!) is SyntheticVariable -> variableStorage.getOrCreateVariable(qualifiedAccess.explicitReceiver!!)
} }
logicSystem.addLogicStatement(node.flow, (variable notEq null) implies (receiverVariable notEq null)) logicSystem.addImplication(node.flow, (variable notEq null) implies (receiverVariable notEq null))
if (receiverVariable.isReal()) { if (receiverVariable.isReal()) {
logicSystem.addLogicStatement(node.flow, (variable notEq null) implies (receiverVariable has any)) logicSystem.addImplication(node.flow, (variable notEq null) implies (receiverVariable typeEq any))
} }
} }
@@ -575,21 +573,21 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
} }
is ConeBooleanConstantReference -> { is ConeBooleanConstantReference -> {
logicSystem.replaceConditionalVariableInStatements( logicSystem.replaceVariableFromConditionInStatements(
lastNode.flow, lastNode.flow,
argumentVariable, argumentVariable,
functionCallVariable, functionCallVariable,
filter = { it.condition.condition == value.toCondition() } filter = { it.condition.operation == value.toOperation() }
) )
} }
ConeConstantReference.NOT_NULL, ConeConstantReference.NULL -> { ConeConstantReference.NOT_NULL, ConeConstantReference.NULL -> {
logicSystem.replaceConditionalVariableInStatements( logicSystem.replaceVariableFromConditionInStatements(
lastNode.flow, lastNode.flow,
argumentVariable, argumentVariable,
functionCallVariable, functionCallVariable,
filter = { it.condition.condition == Condition.EqTrue }, filter = { it.condition.operation == Operation.EqTrue },
transform = { Predicate(it.condition.variable, value.toCondition()) implies it.effect } transform = { OperationStatement(it.condition.variable, value.toOperation()) implies it.effect }
) )
} }
@@ -628,7 +626,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
* x.length * x.length
* } * }
*/ */
logicSystem.replaceConditionalVariableInStatements(node.flow, initializerVariable, propertyVariable) logicSystem.replaceVariableFromConditionInStatements(node.flow, initializerVariable, propertyVariable)
return return
} }
@@ -636,12 +634,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
if (initializerVariable.isStable) { if (initializerVariable.isStable) {
variableStorage.attachSymbolToVariable(variable.symbol, initializerVariable) variableStorage.attachSymbolToVariable(variable.symbol, initializerVariable)
} else { } else {
node.flow.addLogicStatement((propertyVariable notEq null) implies (initializerVariable notEq null)) node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null))
} }
} }
if (!isVariableDeclaration) { if (!isVariableDeclaration) {
node.flow.addKnownInfo(propertyVariable has initializer.typeRef.coneTypeUnsafe<ConeKotlinType>()) node.flow.addTypeStatement(propertyVariable typeEq initializer.typeRef.coneTypeUnsafe<ConeKotlinType>())
} }
} }
@@ -725,24 +723,24 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
// left && right == True // left && right == True
// left || right == False // left || right == False
val approvedIfTrue: MutableKnownFacts = mutableMapOf() val approvedIfTrue: MutableTypeStatements = mutableMapOf()
logicSystem.approvePredicateTo(approvedIfTrue, flowFromRight, leftVariable eq bothEvaluated, conditionalFromLeft) logicSystem.approveStatementsTo(approvedIfTrue, flowFromRight, leftVariable eq bothEvaluated, conditionalFromLeft)
logicSystem.approvePredicateTo(approvedIfTrue, flowFromRight, rightVariable eq bothEvaluated, conditionalFromRight) logicSystem.approveStatementsTo(approvedIfTrue, flowFromRight, rightVariable eq bothEvaluated, conditionalFromRight)
approvedFromRight.forEach { (variable, info) -> approvedFromRight.forEach { (variable, info) ->
approvedIfTrue.addInfo(variable, info) approvedIfTrue.addStatement(variable, info)
} }
approvedIfTrue.values.forEach { info -> approvedIfTrue.values.forEach { info ->
flow.addLogicStatement((operatorVariable eq bothEvaluated) implies info) flow.addImplication((operatorVariable eq bothEvaluated) implies info)
} }
// left && right == False // left && right == False
// left || right == True // left || right == True
val approvedIfFalse: MutableKnownFacts = mutableMapOf() val approvedIfFalse: MutableTypeStatements = mutableMapOf()
val leftIsFalse = logicSystem.approvePredicate(flowFromLeft, leftVariable eq onlyLeftEvaluated, conditionalFromLeft) val leftIsFalse = logicSystem.approveOperationStatement(flowFromLeft, leftVariable eq onlyLeftEvaluated, conditionalFromLeft)
val rightIsFalse = logicSystem.approvePredicate(flowFromRight, rightVariable eq onlyLeftEvaluated, conditionalFromRight) val rightIsFalse = logicSystem.approveOperationStatement(flowFromRight, rightVariable eq onlyLeftEvaluated, conditionalFromRight)
approvedIfFalse.mergeInfo(logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse)) approvedIfFalse.mergeTypeStatements(logicSystem.orForTypeStatements(leftIsFalse, rightIsFalse))
approvedIfFalse.values.forEach { info -> approvedIfFalse.values.forEach { info ->
flow.addLogicStatement((operatorVariable eq onlyLeftEvaluated) implies info) flow.addImplication((operatorVariable eq onlyLeftEvaluated) implies info)
} }
node.flow = flow node.flow = flow
@@ -755,7 +753,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) { private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
val booleanExpressionVariable = variableStorage.getOrCreateVariable(node.previousNodes.first().fir) val booleanExpressionVariable = variableStorage.getOrCreateVariable(node.previousNodes.first().fir)
val variable = variableStorage.getOrCreateVariable(functionCall) val variable = variableStorage.getOrCreateVariable(functionCall)
logicSystem.replaceConditionalVariableInStatements( logicSystem.replaceVariableFromConditionInStatements(
node.flow, node.flow,
booleanExpressionVariable, booleanExpressionVariable,
variable, variable,
@@ -798,16 +796,16 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
node.flow = logicSystem.joinFlow(previousFlows) node.flow = logicSystem.joinFlow(previousFlows)
} }
private fun FLOW.addLogicStatement(statement: LogicStatement) { private fun FLOW.addImplication(statement: Implication) {
logicSystem.addLogicStatement(this, statement) logicSystem.addImplication(this, statement)
} }
private fun FLOW.addKnownInfo(info: DataFlowInfo) { private fun FLOW.addTypeStatement(info: TypeStatement) {
logicSystem.addKnownInfo(this, info) logicSystem.addTypeStatement(this, info)
} }
private fun FLOW.removeAllAboutVariable(variable: RealVariable?) { private fun FLOW.removeAllAboutVariable(variable: RealVariable?) {
if (variable == null) return if (variable == null) return
logicSystem.removeAllAboutVariable(this, variable) logicSystem.removeAllAboutVariable(this, variable)
} }
} }
@@ -0,0 +1,166 @@
/*
* Copyright 2010-2020 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.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.commonSuperTypeOrNull
abstract class Flow {
abstract fun getTypeStatement(variable: RealVariable): TypeStatement?
abstract fun getImplications(variable: DataFlowVariable): Collection<Implication>
abstract fun getVariablesInTypeStatements(): Collection<RealVariable>
abstract fun removeOperations(variable: DataFlowVariable): Collection<Implication>
}
abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceContext) {
// ------------------------------- Flow operations -------------------------------
abstract fun createEmptyFlow(): FLOW
abstract fun forkFlow(flow: FLOW): FLOW
abstract fun joinFlow(flows: Collection<FLOW>): FLOW
abstract fun addTypeStatement(flow: FLOW, statement: TypeStatement)
abstract fun addImplication(flow: FLOW, implication: Implication)
abstract fun removeAllAboutVariable(flow: FLOW, variable: RealVariable)
abstract fun translateVariableFromConditionInStatements(
flow: FLOW,
originalVariable: DataFlowVariable,
newVariable: DataFlowVariable,
shouldRemoveOriginalStatements: Boolean,
filter: (Implication) -> Boolean = { true },
transform: (Implication) -> Implication = { it },
)
abstract fun approveStatementsInsideFlow(
flow: FLOW,
approvedStatement: OperationStatement,
shouldForkFlow: Boolean,
shouldRemoveSynthetics: Boolean,
): FLOW
protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication>
// ------------------------------- Callbacks for updating implicit receiver stack -------------------------------
abstract fun processUpdatedReceiverVariable(flow: FLOW, variable: RealVariable)
abstract fun updateAllReceivers(flow: FLOW)
// ------------------------------- Public TypeStatement util functions -------------------------------
data class InfoForBooleanOperator(
val conditionalFromLeft: Collection<Implication>,
val conditionalFromRight: Collection<Implication>,
val knownFromRight: TypeStatements,
)
abstract fun collectInfoForBooleanOperator(
leftFlow: FLOW,
leftVariable: DataFlowVariable,
rightFlow: FLOW,
rightVariable: DataFlowVariable,
): InfoForBooleanOperator
abstract fun approveStatementsTo(
destination: MutableTypeStatements,
flow: FLOW,
approvedStatement: OperationStatement,
statements: Collection<Implication>,
)
/**
* Recursively collects all TypeStatements approved by [approvedStatement] and all predicates
* that has been implied by it
* TODO: or not recursively?
*/
fun approveOperationStatement(flow: FLOW, approvedStatement: OperationStatement): Collection<TypeStatement> {
val statements = getImplicationsWithVariable(flow, approvedStatement.variable)
return approveOperationStatement(flow, approvedStatement, statements).values
}
fun orForTypeStatements(
left: TypeStatements,
right: TypeStatements,
): MutableTypeStatements {
if (left.isNullOrEmpty() || right.isNullOrEmpty()) return mutableMapOf()
val map = mutableMapOf<RealVariable, MutableTypeStatement>()
for (variable in left.keys.intersect(right.keys)) {
val leftStatement = left.getValue(variable)
val rightStatement = right.getValue(variable)
map[variable] = or(listOf(leftStatement, rightStatement))
}
return map
}
// ------------------------------- Util functions -------------------------------
// TODO
protected fun <E> Collection<Collection<E>>.intersectSets(): Set<E> {
if (isEmpty()) return emptySet()
val iterator = iterator()
val result = HashSet<E>(iterator.next())
while (iterator.hasNext()) {
result.retainAll(iterator.next())
}
return result
}
protected fun or(statements: Collection<TypeStatement>): MutableTypeStatement {
require(statements.isNotEmpty())
statements.singleOrNull()?.let { return it as MutableTypeStatement }
val variable = statements.first().variable
assert(statements.all { it.variable == variable })
val exactType = orForTypes(statements.map { it.exactType })
val exactNotType = orForTypes(statements.map { it.exactNotType })
return MutableTypeStatement(variable, exactType, exactNotType)
}
private fun orForTypes(types: Collection<Set<ConeKotlinType>>): MutableSet<ConeKotlinType> {
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
}
}
fun <FLOW : Flow> LogicSystem<FLOW>.approveOperationStatement(
flow: FLOW,
approvedStatement: OperationStatement,
statements: Collection<Implication>,
): MutableTypeStatements {
return mutableMapOf<RealVariable, MutableTypeStatement>().apply {
approveStatementsTo(this, flow, approvedStatement, statements)
}
}
/*
* used for:
* 1. val b = x is String
* 2. b = x is String
* 3. !b | b.not() for Booleans
*/
fun <F : Flow> LogicSystem<F>.replaceVariableFromConditionInStatements(
flow: F,
originalVariable: DataFlowVariable,
newVariable: DataFlowVariable,
filter: (Implication) -> Boolean = { true },
transform: (Implication) -> Implication = { it },
) {
translateVariableFromConditionInStatements(
flow,
originalVariable,
newVariable,
shouldRemoveOriginalStatements = true,
filter,
transform,
)
}
@@ -1,9 +1,9 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * 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.new package org.jetbrains.kotlin.fir.resolve.dfa
import com.google.common.collect.ArrayListMultimap import com.google.common.collect.ArrayListMultimap
import kotlinx.collections.immutable.* import kotlinx.collections.immutable.*
@@ -11,15 +11,14 @@ import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import java.util.* import java.util.*
import kotlin.NoSuchElementException import kotlin.NoSuchElementException
import kotlin.collections.HashSet
data class PersistentDataFlowInfo( data class PersistentTypeStatement(
override val variable: RealVariable, override val variable: RealVariable,
override val exactType: PersistentSet<ConeKotlinType>, override val exactType: PersistentSet<ConeKotlinType>,
override val exactNotType: PersistentSet<ConeKotlinType> override val exactNotType: PersistentSet<ConeKotlinType>
) : DataFlowInfo() { ) : TypeStatement() {
override operator fun plus(other: DataFlowInfo): PersistentDataFlowInfo { override operator fun plus(other: TypeStatement): PersistentTypeStatement {
return PersistentDataFlowInfo( return PersistentTypeStatement(
variable, variable,
exactType + other.exactType, exactType + other.exactType,
exactNotType + other.exactNotType exactNotType + other.exactNotType
@@ -29,49 +28,49 @@ data class PersistentDataFlowInfo(
override val isEmpty: Boolean override val isEmpty: Boolean
get() = exactType.isEmpty() && exactNotType.isEmpty() get() = exactType.isEmpty() && exactNotType.isEmpty()
override fun invert(): PersistentDataFlowInfo { override fun invert(): PersistentTypeStatement {
return PersistentDataFlowInfo(variable, exactNotType, exactType) return PersistentTypeStatement(variable, exactNotType, exactType)
} }
} }
typealias PersistentKnownFacts = PersistentMap<RealVariable, PersistentDataFlowInfo> typealias PersistentApprovedTypeStatements = PersistentMap<RealVariable, PersistentTypeStatement>
typealias PersistentLogicStatements = PersistentMap<DataFlowVariable, PersistentList<LogicStatement>> typealias PersistentImplications = PersistentMap<DataFlowVariable, PersistentList<Implication>>
class PersistentFlow : Flow { class PersistentFlow : Flow {
val previousFlow: PersistentFlow? val previousFlow: PersistentFlow?
var knownFacts: PersistentKnownFacts var approvedTypeStatements: PersistentApprovedTypeStatements
var logicStatements: PersistentLogicStatements var logicStatements: PersistentImplications
val level: Int val level: Int
var knownFactsDiff: PersistentKnownFacts = persistentHashMapOf() var approvedTypeStatementsDiff: PersistentApprovedTypeStatements = persistentHashMapOf()
constructor(previousFlow: PersistentFlow) { constructor(previousFlow: PersistentFlow) {
this.previousFlow = previousFlow this.previousFlow = previousFlow
knownFacts = previousFlow.knownFacts approvedTypeStatements = previousFlow.approvedTypeStatements
logicStatements = previousFlow.logicStatements logicStatements = previousFlow.logicStatements
level = previousFlow.level + 1 level = previousFlow.level + 1
} }
constructor() { constructor() {
previousFlow = null previousFlow = null
knownFacts = persistentHashMapOf() approvedTypeStatements = persistentHashMapOf()
logicStatements = persistentHashMapOf() logicStatements = persistentHashMapOf()
level = 1 level = 1
} }
override fun getKnownInfo(variable: RealVariable): DataFlowInfo? { override fun getTypeStatement(variable: RealVariable): TypeStatement? {
return knownFacts[variable] return approvedTypeStatements[variable]
} }
override fun getLogicStatements(variable: DataFlowVariable): Collection<LogicStatement> { override fun getImplications(variable: DataFlowVariable): Collection<Implication> {
return logicStatements[variable] ?: emptyList() return logicStatements[variable] ?: emptyList()
} }
override fun getVariablesInKnownInfos(): Collection<RealVariable> { override fun getVariablesInTypeStatements(): Collection<RealVariable> {
return knownFacts.keys return approvedTypeStatements.keys
} }
override fun removeConditions(variable: DataFlowVariable): Collection<LogicStatement> { override fun removeOperations(variable: DataFlowVariable): Collection<Implication> {
return getLogicStatements(variable).also { return getImplications(variable).also {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
logicStatements -= variable logicStatements -= variable
} }
@@ -79,8 +78,7 @@ class PersistentFlow : Flow {
} }
} }
abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, context: ConeInferenceContext) : abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSystem<PersistentFlow>(context) {
LogicSystem<PersistentFlow>(context) {
override fun createEmptyFlow(): PersistentFlow { override fun createEmptyFlow(): PersistentFlow {
return PersistentFlow() return PersistentFlow()
} }
@@ -99,11 +97,11 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
?: return commonFlow ?: return commonFlow
for (variable in commonVariables) { for (variable in commonVariables) {
val info = or(flows.map { it.getKnownFactsDiff(variable, commonFlow) }) val info = or(flows.map { it.getApprovedTypeStatementsDiff(variable, commonFlow) })
if (info.isEmpty) continue if (info.isEmpty) continue
commonFlow.knownFacts = commonFlow.knownFacts.addNewInfo(info) commonFlow.approvedTypeStatements = commonFlow.approvedTypeStatements.addTypeStatement(info)
if (commonFlow.previousFlow != null) { if (commonFlow.previousFlow != null) {
commonFlow.knownFactsDiff = commonFlow.knownFactsDiff.addNewInfo(info) commonFlow.approvedTypeStatementsDiff = commonFlow.approvedTypeStatementsDiff.addTypeStatement(info)
} }
} }
@@ -112,11 +110,11 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
return commonFlow return commonFlow
} }
private fun PersistentFlow.getKnownFactsDiff(variable: RealVariable, parentFlow: PersistentFlow): MutableDataFlowInfo { private fun PersistentFlow.getApprovedTypeStatementsDiff(variable: RealVariable, parentFlow: PersistentFlow): MutableTypeStatement {
var flow = this var flow = this
val result = MutableDataFlowInfo(variable) val result = MutableTypeStatement(variable)
while (flow != parentFlow) { while (flow != parentFlow) {
flow.knownFactsDiff[variable]?.let { flow.approvedTypeStatementsDiff[variable]?.let {
result += it result += it
} }
flow = flow.previousFlow!! flow = flow.previousFlow!!
@@ -131,7 +129,7 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
private fun PersistentFlow.diffVariablesIterable(parentFlow: PersistentFlow): Iterable<RealVariable> = private fun PersistentFlow.diffVariablesIterable(parentFlow: PersistentFlow): Iterable<RealVariable> =
object : DiffIterable<RealVariable>(parentFlow, this) { object : DiffIterable<RealVariable>(parentFlow, this) {
override fun extractIterator(flow: PersistentFlow): Iterator<RealVariable> { override fun extractIterator(flow: PersistentFlow): Iterator<RealVariable> {
return flow.knownFactsDiff.keys.iterator() return flow.approvedTypeStatementsDiff.keys.iterator()
} }
} }
@@ -162,49 +160,49 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
} }
} }
override fun addKnownInfo(flow: PersistentFlow, info: DataFlowInfo) { override fun addTypeStatement(flow: PersistentFlow, statement: TypeStatement) {
with(flow) { with(flow) {
knownFacts = knownFacts.addNewInfo(info) approvedTypeStatements = approvedTypeStatements.addTypeStatement(statement)
if (previousFlow != null) { if (previousFlow != null) {
knownFactsDiff = knownFactsDiff.addNewInfo(info) approvedTypeStatementsDiff = approvedTypeStatementsDiff.addTypeStatement(statement)
} }
if (info.variable.isThisReference) { if (statement.variable.isThisReference) {
processUpdatedReceiverVariable(flow, info.variable) processUpdatedReceiverVariable(flow, statement.variable)
} }
} }
} }
override fun addLogicStatement(flow: PersistentFlow, statement: LogicStatement) { override fun addImplication(flow: PersistentFlow, implication: Implication) {
if (statement.condition == statement.effect) return if (implication.condition == implication.effect) return
with(flow) { with(flow) {
val variable = statement.condition.variable val variable = implication.condition.variable
val existingFacts = logicStatements[variable] val existingImplications = logicStatements[variable]
logicStatements = if (existingFacts == null) { logicStatements = if (existingImplications == null) {
logicStatements.put(variable, persistentListOf(statement)) logicStatements.put(variable, persistentListOf(implication))
} else { } else {
logicStatements.put(variable, existingFacts + statement) logicStatements.put(variable, existingImplications + implication)
} }
} }
} }
override fun removeAllAboutVariable(flow: PersistentFlow, variable: RealVariable) { override fun removeAllAboutVariable(flow: PersistentFlow, variable: RealVariable) {
flow.knownFacts -= variable flow.approvedTypeStatements -= variable
flow.knownFactsDiff -= variable flow.approvedTypeStatementsDiff -= variable
// TODO: should we search variable in all logic statements? // TODO: should we search variable in all logic statements?
} }
override fun translateConditionalVariableInStatements( override fun translateVariableFromConditionInStatements(
flow: PersistentFlow, flow: PersistentFlow,
originalVariable: DataFlowVariable, originalVariable: DataFlowVariable,
newVariable: DataFlowVariable, newVariable: DataFlowVariable,
shouldRemoveOriginalStatements: Boolean, shouldRemoveOriginalStatements: Boolean,
filter: (LogicStatement) -> Boolean, filter: (Implication) -> Boolean,
transform: (LogicStatement) -> LogicStatement transform: (Implication) -> Implication
) { ) {
with(flow) { with(flow) {
val statements = logicStatements[originalVariable]?.takeIf { it.isNotEmpty() } ?: return val statements = logicStatements[originalVariable]?.takeIf { it.isNotEmpty() } ?: return
val newStatements = statements.filter(filter).map { val newStatements = statements.filter(filter).map {
val newStatement = Predicate(newVariable, it.condition.condition) implies it.effect val newStatement = OperationStatement(newVariable, it.condition.operation) implies it.effect
transform(newStatement) transform(newStatement)
}.toPersistentList() }.toPersistentList()
if (shouldRemoveOriginalStatements) { if (shouldRemoveOriginalStatements) {
@@ -216,13 +214,13 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
override fun approveStatementsInsideFlow( override fun approveStatementsInsideFlow(
flow: PersistentFlow, flow: PersistentFlow,
predicate: Predicate, approvedStatement: OperationStatement,
shouldForkFlow: Boolean, shouldForkFlow: Boolean,
shouldRemoveSynthetics: Boolean shouldRemoveSynthetics: Boolean
): PersistentFlow { ): PersistentFlow {
val approvedFacts = approvePredicatesInternal( val approvedFacts = approveOperationStatementsInternal(
flow, flow,
predicate, approvedStatement,
initialStatements = null, initialStatements = null,
shouldRemoveSynthetics shouldRemoveSynthetics
) )
@@ -232,14 +230,14 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
val updatedReceivers = mutableSetOf<RealVariable>() val updatedReceivers = mutableSetOf<RealVariable>()
approvedFacts.asMap().forEach { (variable, infos) -> approvedFacts.asMap().forEach { (variable, infos) ->
var resultInfo = PersistentDataFlowInfo(variable, persistentSetOf(), persistentSetOf()) var resultInfo = PersistentTypeStatement(variable, persistentSetOf(), persistentSetOf())
for (info in infos) { for (info in infos) {
resultInfo += info resultInfo += info
} }
if (variable.isThisReference) { if (variable.isThisReference) {
updatedReceivers += variable updatedReceivers += variable
} }
addKnownInfo(resultFlow, resultInfo) addTypeStatement(resultFlow, resultInfo)
} }
updatedReceivers.forEach { updatedReceivers.forEach {
@@ -249,86 +247,63 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
return resultFlow return resultFlow
} }
private fun approvePredicatesInternal( private fun approveOperationStatementsInternal(
flow: PersistentFlow, flow: PersistentFlow,
predicate: Predicate, approvedStatement: OperationStatement,
initialStatements: Collection<LogicStatement>?, initialStatements: Collection<Implication>?,
shouldRemoveSynthetics: Boolean shouldRemoveSynthetics: Boolean
): ArrayListMultimap<RealVariable, DataFlowInfo> { ): ArrayListMultimap<RealVariable, TypeStatement> {
val approvedFacts: ArrayListMultimap<RealVariable, DataFlowInfo> = ArrayListMultimap.create() val approvedFacts: ArrayListMultimap<RealVariable, TypeStatement> = ArrayListMultimap.create()
val predicatesToApprove = LinkedList<Predicate>().apply { this += predicate } val approvedStatements = LinkedList<OperationStatement>().apply { this += approvedStatement }
approvePredicatesInternal(flow, predicatesToApprove, initialStatements, shouldRemoveSynthetics, approvedFacts) approveOperationStatementsInternal(flow, approvedStatements, initialStatements, shouldRemoveSynthetics, approvedFacts)
return approvedFacts return approvedFacts
} }
private fun approvePredicatesInternal( private fun approveOperationStatementsInternal(
flow: PersistentFlow, flow: PersistentFlow,
predicatesToApprove: LinkedList<Predicate>, approvedStatements: LinkedList<OperationStatement>,
initialStatements: Collection<LogicStatement>?, initialStatements: Collection<Implication>?,
shouldRemoveSynthetics: Boolean, shouldRemoveSynthetics: Boolean,
approvedFacts: ArrayListMultimap<RealVariable, DataFlowInfo> approvedTypeStatements: ArrayListMultimap<RealVariable, TypeStatement>
) { ) {
if (predicatesToApprove.isEmpty()) return if (approvedStatements.isEmpty()) return
val approvedVariables = mutableSetOf<RealVariable>() val approvedOperationStatements = mutableSetOf<OperationStatement>()
val approvedPredicates = mutableSetOf<Predicate>()
var firstIteration = true var firstIteration = true
while (predicatesToApprove.isNotEmpty()) { while (approvedStatements.isNotEmpty()) {
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val predicate: Predicate = predicatesToApprove.removeFirst() val approvedStatement: OperationStatement = approvedStatements.removeFirst()
// Defense from cycles in facts // Defense from cycles in facts
if (!approvedPredicates.add(predicate)) { if (!approvedOperationStatements.add(approvedStatement)) {
continue continue
} }
val statements = initialStatements?.takeIf { firstIteration } val statements = initialStatements?.takeIf { firstIteration }
?: flow.logicStatements[predicate.variable]?.takeIf { it.isNotEmpty() } ?: flow.logicStatements[approvedStatement.variable]?.takeIf { it.isNotEmpty() }
?: continue ?: continue
if (shouldRemoveSynthetics && predicate.variable.isSynthetic()) { if (shouldRemoveSynthetics && approvedStatement.variable.isSynthetic()) {
flow.logicStatements -= predicate.variable flow.logicStatements -= approvedStatement.variable
} }
for (statement in statements) { for (statement in statements) {
if (statement.condition == predicate) { if (statement.condition == approvedStatement) {
when (val effect = statement.effect) { when (val effect = statement.effect) {
is Predicate -> predicatesToApprove += effect is OperationStatement -> approvedStatements += effect
is DataFlowInfo -> { is TypeStatement -> approvedTypeStatements.put(effect.variable, effect)
approvedFacts.put(effect.variable, effect)
approvedVariables += effect.variable
}
} }
} }
} }
firstIteration = false firstIteration = false
} }
val newPredicates = LinkedList<Predicate>()
for (approvedVariable in approvedVariables) {
var variable = approvedVariable
foo@ while (variable.explicitReceiverVariable != null && variable.isSafeCall) {
when (val receiver = variable.explicitReceiverVariable!!) {
is RealVariable -> {
approvedFacts.put(receiver, receiver has anyType)
variable = receiver
}
is SyntheticVariable -> {
newPredicates += receiver notEq null
break@foo
}
else -> throw IllegalStateException()
}
}
}
approvePredicatesInternal(flow, newPredicates, initialStatements = null, shouldRemoveSynthetics, approvedFacts)
} }
override fun approvePredicateTo( override fun approveStatementsTo(
destination: MutableKnownFacts, destination: MutableTypeStatements,
flow: PersistentFlow, flow: PersistentFlow,
predicate: Predicate, approvedStatement: OperationStatement,
statements: Collection<LogicStatement> statements: Collection<Implication>
) { ) {
val approvePredicates = approvePredicatesInternal(flow, predicate, statements, shouldRemoveSynthetics = false) val approveOperationStatements = approveOperationStatementsInternal(flow, approvedStatement, statements, shouldRemoveSynthetics = false)
approvePredicates.asMap().forEach { (variable, infos) -> approveOperationStatements.asMap().forEach { (variable, infos) ->
for (info in infos) { for (info in infos) {
val mutableInfo = info.asMutableInfo() val mutableInfo = info.asMutableStatement()
destination.put(variable, mutableInfo) { destination.put(variable, mutableInfo) {
it += mutableInfo it += mutableInfo
it it
@@ -346,11 +321,11 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
return InfoForBooleanOperator( return InfoForBooleanOperator(
leftFlow.logicStatements[leftVariable] ?: emptyList(), leftFlow.logicStatements[leftVariable] ?: emptyList(),
rightFlow.logicStatements[rightVariable] ?: emptyList(), rightFlow.logicStatements[rightVariable] ?: emptyList(),
rightFlow.knownFactsDiff rightFlow.approvedTypeStatementsDiff
) )
} }
override fun getLogicStatementsWithVariable(flow: PersistentFlow, variable: DataFlowVariable): Collection<LogicStatement> { override fun getImplicationsWithVariable(flow: PersistentFlow, variable: DataFlowVariable): Collection<Implication> {
return flow.logicStatements[variable] ?: emptyList() return flow.logicStatements[variable] ?: emptyList()
} }
@@ -377,35 +352,25 @@ private fun lowestCommonFlow(left: PersistentFlow, right: PersistentFlow): Persi
return left return left
} }
private fun <E> Collection<Collection<E>>.intersectSets(): Set<E> { private fun PersistentApprovedTypeStatements.addTypeStatement(info: TypeStatement): PersistentApprovedTypeStatements {
if (isEmpty()) return emptySet()
val iterator = iterator()
val result = HashSet<E>(iterator.next())
while (iterator.hasNext()) {
result.retainAll(iterator.next())
}
return result
}
private fun PersistentKnownFacts.addNewInfo(info: DataFlowInfo): PersistentKnownFacts {
val variable = info.variable val variable = info.variable
val existingInfo = this[variable] val existingInfo = this[variable]
return if (existingInfo == null) { return if (existingInfo == null) {
val persistentInfo = if (info is PersistentDataFlowInfo) info else info.toPersistent() val persistentInfo = if (info is PersistentTypeStatement) info else info.toPersistent()
put(variable, persistentInfo) put(variable, persistentInfo)
} else { } else {
put(variable, existingInfo + info) put(variable, existingInfo + info)
} }
} }
private fun DataFlowInfo.toPersistent(): PersistentDataFlowInfo = PersistentDataFlowInfo( private fun TypeStatement.toPersistent(): PersistentTypeStatement = PersistentTypeStatement(
variable, variable,
exactType.toPersistentSet(), exactType.toPersistentSet(),
exactNotType.toPersistentSet() exactNotType.toPersistentSet()
) )
fun DataFlowInfo.asMutableInfo(): MutableDataFlowInfo = when (this) { fun TypeStatement.asMutableStatement(): MutableTypeStatement = when (this) {
is MutableDataFlowInfo -> this is MutableTypeStatement -> this
is PersistentDataFlowInfo -> MutableDataFlowInfo(variable, exactType.toMutableSet(), exactNotType.toMutableSet()) is PersistentTypeStatement -> MutableTypeStatement(variable, exactType.toMutableSet(), exactNotType.toMutableSet())
else -> throw IllegalArgumentException("Unknown DataFlowInfo type: ${this::class}") else -> throw IllegalArgumentException("Unknown TypeStatement type: ${this::class}")
} }
@@ -1,9 +1,9 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * 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.new package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSymbolOwner import org.jetbrains.kotlin.fir.FirSymbolOwner
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
@UseExperimental(DfaInternals::class)
class VariableStorage { class VariableStorage {
private var counter = 1 private var counter = 1
private val realVariables: MutableMap<Identifier, RealVariable> = HashMap() private val realVariables: MutableMap<Identifier, RealVariable> = HashMap()
@@ -53,7 +54,6 @@ class VariableStorage {
*/ */
private fun createRealVariableInternal(identifier: Identifier, originalFir: FirElement): RealVariable { private fun createRealVariableInternal(identifier: Identifier, originalFir: FirElement): RealVariable {
val receiver: FirExpression? val receiver: FirExpression?
val isSafeCall: Boolean
val isThisReference: Boolean val isThisReference: Boolean
val expression = when (originalFir) { val expression = when (originalFir) {
is FirQualifiedAccessExpression -> originalFir is FirQualifiedAccessExpression -> originalFir
@@ -63,16 +63,14 @@ class VariableStorage {
if (expression != null) { if (expression != null) {
receiver = expression.explicitReceiver receiver = expression.explicitReceiver
isSafeCall = expression.safe
isThisReference = expression.calleeReference is FirThisReference isThisReference = expression.calleeReference is FirThisReference
} else { } else {
receiver = null receiver = null
isSafeCall = false
isThisReference = false isThisReference = false
} }
val receiverVariable = receiver?.let { getOrCreateVariable(it) } val receiverVariable = receiver?.let { getOrCreateVariable(it) }
return RealVariable(identifier, isThisReference, receiverVariable, isSafeCall, counter++) return RealVariable(identifier, isThisReference, receiverVariable, counter++)
} }
@JvmName("getOrCreateRealVariableOrNull") @JvmName("getOrCreateRealVariableOrNull")
@@ -134,19 +132,3 @@ class VariableStorage {
localVariableAliases.clear() localVariableAliases.clear()
} }
} }
internal val FirElement.symbol: AbstractFirBasedSymbol<*>?
get() = when (this) {
is FirResolvable -> symbol
is FirSymbolOwner<*> -> symbol
is FirWhenSubjectExpression -> whenSubject.whenExpression.subject?.symbol
else -> null
}?.takeIf { this is FirThisReceiverExpression || it !is FirFunctionSymbol<*> }
internal val FirResolvable.symbol: AbstractFirBasedSymbol<*>?
get() = when (val reference = calleeReference) {
is FirExplicitThisReference -> reference.boundSymbol
is FirResolvedNamedReference -> reference.resolvedSymbol
is FirNamedReferenceWithCandidate -> reference.candidateSymbol
else -> null
}
@@ -1,16 +1,15 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * 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.new package org.jetbrains.kotlin.fir.resolve.dfa
import com.google.common.collect.Multimap
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.modality import org.jetbrains.kotlin.fir.declarations.modality
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.resolve.dfa.Condition import org.jetbrains.kotlin.fir.resolve.dfa.Operation
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
@@ -18,7 +17,7 @@ import org.jetbrains.kotlin.fir.types.ConeKotlinType
// --------------------------------------- Variables --------------------------------------- // --------------------------------------- Variables ---------------------------------------
class Identifier( data class Identifier(
val symbol: AbstractFirBasedSymbol<*>, val symbol: AbstractFirBasedSymbol<*>,
val dispatchReceiver: DataFlowVariable?, val dispatchReceiver: DataFlowVariable?,
val extensionReceiver: DataFlowVariable? val extensionReceiver: DataFlowVariable?
@@ -27,26 +26,6 @@ class Identifier(
val callableId = (symbol as? FirCallableSymbol<*>)?.callableId val callableId = (symbol as? FirCallableSymbol<*>)?.callableId
return "[$callableId, dispatchReceiver = $dispatchReceiver, extensionReceiver = $extensionReceiver]" return "[$callableId, dispatchReceiver = $dispatchReceiver, extensionReceiver = $extensionReceiver]"
} }
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Identifier
if (symbol !== other.symbol) return false
if (dispatchReceiver != other.dispatchReceiver) return false
if (extensionReceiver != other.extensionReceiver) return false
return true
}
override fun hashCode(): Int {
var result = symbol.hashCode()
result = 31 * result + (dispatchReceiver?.hashCode() ?: 0)
result = 31 * result + (extensionReceiver?.hashCode() ?: 0)
return result
}
} }
sealed class DataFlowVariable(private val variableIndexForDebug: Int) { sealed class DataFlowVariable(private val variableIndexForDebug: Int) {
@@ -61,7 +40,6 @@ class RealVariable(
val identifier: Identifier, val identifier: Identifier,
val isThisReference: Boolean, val isThisReference: Boolean,
val explicitReceiverVariable: DataFlowVariable?, val explicitReceiverVariable: DataFlowVariable?,
val isSafeCall: Boolean,
variableIndexForDebug: Int variableIndexForDebug: Int
) : DataFlowVariable(variableIndexForDebug) { ) : DataFlowVariable(variableIndexForDebug) {
override val isStable: Boolean by lazy { override val isStable: Boolean by lazy {
@@ -72,6 +50,7 @@ class RealVariable(
property.isLocal -> true property.isLocal -> true
property.isVar -> false property.isVar -> false
property.modality != Modality.FINAL -> false property.modality != Modality.FINAL -> false
// TODO: getters, delegates
else -> true else -> true
} }
} }
@@ -105,6 +84,7 @@ class SyntheticVariable(val fir: FirElement, variableIndexForDebug: Int) : DataF
} }
override fun hashCode(): Int { override fun hashCode(): Int {
// hack for enums
return if (fir is FirResolvedQualifier) { return if (fir is FirResolvedQualifier) {
31 * fir.packageFqName.hashCode() + fir.classId.hashCode() 31 * fir.packageFqName.hashCode() + fir.classId.hashCode()
} else { } else {
@@ -122,26 +102,33 @@ private infix fun FirElement.isEqualsTo(other: FirElement): Boolean {
// --------------------------------------- Facts --------------------------------------- // --------------------------------------- Facts ---------------------------------------
sealed class PredicateEffect<T : PredicateEffect<T>> { sealed class Statement<T : Statement<T>> {
abstract fun invert(): T abstract fun invert(): T
} }
data class Predicate(val variable: DataFlowVariable, val condition: Condition) : PredicateEffect<Predicate>() { /*
override fun invert(): Predicate { * Examples:
return Predicate(variable, condition.invert()) * d == Null
* d != Null
* d == True
* d == False
*/
data class OperationStatement(val variable: DataFlowVariable, val operation: Operation) : Statement<OperationStatement>() {
override fun invert(): OperationStatement {
return OperationStatement(variable, operation.invert())
} }
override fun toString(): String { override fun toString(): String {
return "$variable $condition" return "$variable $operation"
} }
} }
abstract class DataFlowInfo : PredicateEffect<DataFlowInfo>() { abstract class TypeStatement : Statement<TypeStatement>() {
abstract val variable: RealVariable abstract val variable: RealVariable
abstract val exactType: Set<ConeKotlinType> abstract val exactType: Set<ConeKotlinType>
abstract val exactNotType: Set<ConeKotlinType> abstract val exactNotType: Set<ConeKotlinType>
abstract operator fun plus(other: DataFlowInfo): DataFlowInfo abstract operator fun plus(other: TypeStatement): TypeStatement
abstract val isEmpty: Boolean abstract val isEmpty: Boolean
val isNotEmpty: Boolean get() = !isEmpty val isNotEmpty: Boolean get() = !isEmpty
@@ -150,12 +137,12 @@ abstract class DataFlowInfo : PredicateEffect<DataFlowInfo>() {
} }
} }
class MutableDataFlowInfo( class MutableTypeStatement(
override val variable: RealVariable, override val variable: RealVariable,
override val exactType: MutableSet<ConeKotlinType> = HashSet(), override val exactType: MutableSet<ConeKotlinType> = HashSet(),
override val exactNotType: MutableSet<ConeKotlinType> = HashSet() override val exactNotType: MutableSet<ConeKotlinType> = HashSet()
) : DataFlowInfo() { ) : TypeStatement() {
override fun plus(other: DataFlowInfo): MutableDataFlowInfo = MutableDataFlowInfo( override fun plus(other: TypeStatement): MutableTypeStatement = MutableTypeStatement(
variable, variable,
HashSet(exactType).apply { addAll(other.exactType) }, HashSet(exactType).apply { addAll(other.exactType) },
HashSet(exactNotType).apply { addAll(other.exactNotType) } HashSet(exactNotType).apply { addAll(other.exactNotType) }
@@ -164,65 +151,64 @@ class MutableDataFlowInfo(
override val isEmpty: Boolean override val isEmpty: Boolean
get() = exactType.isEmpty() && exactType.isEmpty() get() = exactType.isEmpty() && exactType.isEmpty()
override fun invert(): DataFlowInfo { override fun invert(): TypeStatement {
return MutableDataFlowInfo( return MutableTypeStatement(
variable, variable,
HashSet(exactNotType), HashSet(exactNotType),
HashSet(exactType) HashSet(exactType)
) )
} }
operator fun plusAssign(info: DataFlowInfo) { operator fun plusAssign(info: TypeStatement) {
exactType += info.exactType exactType += info.exactType
exactNotType += info.exactNotType exactNotType += info.exactNotType
} }
fun copy(): MutableDataFlowInfo = MutableDataFlowInfo(variable, HashSet(exactType), HashSet(exactNotType)) fun copy(): MutableTypeStatement = MutableTypeStatement(variable, HashSet(exactType), HashSet(exactNotType))
} }
class LogicStatement( class Implication(
val condition: Predicate, val condition: OperationStatement,
val effect: PredicateEffect<*> val effect: Statement<*>
) { ) {
override fun toString(): String { override fun toString(): String {
return "$condition -> $effect" return "$condition -> $effect"
} }
} }
fun LogicStatement.invertCondition(): LogicStatement = LogicStatement(condition.invert(), effect) fun Implication.invertCondition(): Implication = Implication(condition.invert(), effect)
// --------------------------------------- Aliases --------------------------------------- // --------------------------------------- Aliases ---------------------------------------
typealias KnownInfos = Map<RealVariable, DataFlowInfo> typealias TypeStatements = Map<RealVariable, TypeStatement>
typealias MutableKnownFacts = MutableMap<RealVariable, MutableDataFlowInfo> typealias MutableTypeStatements = MutableMap<RealVariable, MutableTypeStatement>
typealias LogicStatements = Multimap<Predicate, LogicStatement>
// --------------------------------------- DSL --------------------------------------- // --------------------------------------- DSL ---------------------------------------
infix fun DataFlowVariable.eq(constant: Boolean?): Predicate { infix fun DataFlowVariable.eq(constant: Boolean?): OperationStatement {
val condition = when (constant) { val condition = when (constant) {
true -> Condition.EqTrue true -> Operation.EqTrue
false -> Condition.EqFalse false -> Operation.EqFalse
null -> Condition.EqNull null -> Operation.EqNull
} }
return Predicate(this, condition) return OperationStatement(this, condition)
} }
infix fun DataFlowVariable.notEq(constant: Boolean?): Predicate { infix fun DataFlowVariable.notEq(constant: Boolean?): OperationStatement {
val condition = when (constant) { val condition = when (constant) {
true -> Condition.EqFalse true -> Operation.EqFalse
false -> Condition.EqTrue false -> Operation.EqTrue
null -> Condition.NotEqNull null -> Operation.NotEqNull
} }
return Predicate(this, condition) return OperationStatement(this, condition)
} }
infix fun Predicate.implies(effect: PredicateEffect<*>): LogicStatement = LogicStatement(this, effect) infix fun OperationStatement.implies(effect: Statement<*>): Implication = Implication(this, effect)
infix fun RealVariable.has(types: MutableSet<ConeKotlinType>): DataFlowInfo = MutableDataFlowInfo(this, types, HashSet()) infix fun RealVariable.typeEq(types: MutableSet<ConeKotlinType>): TypeStatement = MutableTypeStatement(this, types, HashSet())
infix fun RealVariable.has(type: ConeKotlinType): DataFlowInfo = infix fun RealVariable.typeEq(type: ConeKotlinType): TypeStatement =
MutableDataFlowInfo(this, HashSet<ConeKotlinType>().apply { this += type }, HashSet()) MutableTypeStatement(this, HashSet<ConeKotlinType>().apply { this += type }, HashSet())
infix fun RealVariable.hasNot(types: MutableSet<ConeKotlinType>): DataFlowInfo = MutableDataFlowInfo(this, HashSet(), types) infix fun RealVariable.typeNotEq(types: MutableSet<ConeKotlinType>): TypeStatement = MutableTypeStatement(this, HashSet(), types)
infix fun RealVariable.hasNot(type: ConeKotlinType): DataFlowInfo = infix fun RealVariable.typeNotEq(type: ConeKotlinType): TypeStatement =
MutableDataFlowInfo(this, HashSet(), HashSet<ConeKotlinType>().apply { this += type }) MutableTypeStatement(this, HashSet(), HashSet<ConeKotlinType>().apply { this += type })
@@ -1,161 +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.new
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.types.ConeKotlinType
abstract class Flow {
abstract fun getKnownInfo(variable: RealVariable): DataFlowInfo?
abstract fun getLogicStatements(variable: DataFlowVariable): Collection<LogicStatement>
abstract fun getVariablesInKnownInfos(): Collection<RealVariable>
abstract fun removeConditions(variable: DataFlowVariable): Collection<LogicStatement>
}
abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceContext) {
// ------------------------------- Flow operations -------------------------------
abstract fun createEmptyFlow(): FLOW
abstract fun forkFlow(flow: FLOW): FLOW
abstract fun joinFlow(flows: Collection<FLOW>): FLOW
abstract fun addKnownInfo(flow: FLOW, info: DataFlowInfo)
abstract fun addLogicStatement(flow: FLOW, statement: LogicStatement)
abstract fun removeAllAboutVariable(flow: FLOW, variable: RealVariable)
abstract fun translateConditionalVariableInStatements(
flow: FLOW,
originalVariable: DataFlowVariable,
newVariable: DataFlowVariable,
shouldRemoveOriginalStatements: Boolean,
filter: (LogicStatement) -> Boolean = { true },
transform: (LogicStatement) -> LogicStatement = { it }
)
abstract fun approveStatementsInsideFlow(
flow: FLOW,
predicate: Predicate,
shouldForkFlow: Boolean,
shouldRemoveSynthetics: Boolean
): FLOW
protected abstract fun getLogicStatementsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<LogicStatement>
// ------------------------------- Callbacks for updating implicit receiver stack -------------------------------
abstract fun processUpdatedReceiverVariable(flow: FLOW, variable: RealVariable)
abstract fun updateAllReceivers(flow: FLOW)
// ------------------------------- Public DataFlowInfo util functions -------------------------------
data class InfoForBooleanOperator(
val conditionalFromLeft: Collection<LogicStatement>,
val conditionalFromRight: Collection<LogicStatement>,
val knownFromRight: KnownInfos
)
abstract fun collectInfoForBooleanOperator(
leftFlow: FLOW,
leftVariable: DataFlowVariable,
rightFlow: FLOW,
rightVariable: DataFlowVariable
): InfoForBooleanOperator
abstract fun approvePredicateTo(
destination: MutableKnownFacts,
flow: FLOW,
predicate: Predicate,
statements: Collection<LogicStatement>
)
/**
* Recursively collects all DataFlowInfos approved by [predicate] and all predicates
* that has been implied by it
* TODO: or not recursively?
*/
fun approvePredicate(flow: FLOW, predicate: Predicate): Collection<DataFlowInfo> {
val statements = getLogicStatementsWithVariable(flow, predicate.variable)
return approvePredicate(flow, predicate, statements).values
}
fun orForVerifiedFacts(
left: KnownInfos,
right: KnownInfos
): MutableKnownFacts {
if (left.isNullOrEmpty() || right.isNullOrEmpty()) return mutableMapOf()
val map = mutableMapOf<RealVariable, MutableDataFlowInfo>()
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
}
// ------------------------------- Util functions -------------------------------
// TODO
protected fun <E> Collection<Collection<E>>.intersectSets(): Set<E> {
if (isEmpty()) return emptySet()
val iterator = iterator()
val result = HashSet<E>(iterator.next())
while (iterator.hasNext()) {
result.retainAll(iterator.next())
}
return result
}
protected fun or(infos: Collection<DataFlowInfo>): MutableDataFlowInfo {
require(infos.isNotEmpty())
infos.singleOrNull()?.let { return it as MutableDataFlowInfo }
val variable = infos.first().variable
assert(infos.all { it.variable == variable })
val exactType = orTypes(infos.map { it.exactType })
val exactNotType = orTypes(infos.map { it.exactNotType })
return MutableDataFlowInfo(variable, exactType, exactNotType)
}
private fun orTypes(types: Collection<Set<ConeKotlinType>>): MutableSet<ConeKotlinType> {
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
}
}
fun <FLOW : Flow> LogicSystem<FLOW>.approvePredicate(flow: FLOW, predicate: Predicate, statements: Collection<LogicStatement>): MutableKnownFacts {
return mutableMapOf<RealVariable, MutableDataFlowInfo>().apply {
approvePredicateTo(this, flow, predicate, statements)
}
}
/*
* used for:
* 1. val b = x is String
* 2. b = x is String
* 3. !b | b.not() for Booleans
*/
fun <F : Flow> LogicSystem<F>.replaceConditionalVariableInStatements(
flow: F,
originalVariable: DataFlowVariable,
newVariable: DataFlowVariable,
filter: (LogicStatement) -> Boolean = { true },
transform: (LogicStatement) -> LogicStatement = { it }
) {
translateConditionalVariableInStatements(
flow,
originalVariable,
newVariable,
shouldRemoveOriginalStatements = true,
filter,
transform
)
}
@@ -1,47 +1,28 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * 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.new package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSymbolOwner
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirOperation
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext import org.jetbrains.kotlin.fir.references.impl.FirExplicitThisReference
import org.jetbrains.kotlin.fir.resolve.dfa.Condition import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeIntersector
import org.jetbrains.kotlin.fir.types.coneTypeSafe import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
fun ConeInferenceContext.commonSuperTypeOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) {
0 -> null
1 -> types.first()
else -> with(NewCommonSuperTypeCalculator) {
commonSuperType(types) as ConeKotlinType
}
}
}
fun ConeInferenceContext.intersectTypesOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) {
0 -> null
1 -> types.first()
else -> ConeTypeIntersector.intersectTypes(this, types)
}
}
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
fun DataFlowVariable.isSynthetic(): Boolean { fun DataFlowVariable.isSynthetic(): Boolean {
contract { contract {
@@ -60,15 +41,15 @@ fun DataFlowVariable.isReal(): Boolean {
return this is RealVariable return this is RealVariable
} }
operator fun DataFlowInfo.plus(other: DataFlowInfo?): DataFlowInfo = other?.let { this + other } ?: this operator fun TypeStatement.plus(other: TypeStatement?): TypeStatement = other?.let { this + other } ?: this
fun MutableKnownFacts.addInfo(variable: RealVariable, info: DataFlowInfo) { fun MutableTypeStatements.addStatement(variable: RealVariable, statement: TypeStatement) {
put(variable, info.asMutableInfo()) { it.apply { this += info } } put(variable, statement.asMutableStatement()) { it.apply { this += statement } }
} }
fun MutableKnownFacts.mergeInfo(other: Map<RealVariable, DataFlowInfo>) { fun MutableTypeStatements.mergeTypeStatements(other: TypeStatements) {
other.forEach { (variable, info) -> other.forEach { (variable, info) ->
addInfo(variable, info) addStatement(variable, info)
} }
} }
@@ -85,8 +66,7 @@ internal inline fun <K, V> MutableMap<K, V>.put(key: K, value: V, remappingFunct
} }
} }
internal val FirExpression.coneType: ConeKotlinType? get() = typeRef.coneTypeSafe() @DfaInternals
internal fun FirOperation.invert(): FirOperation = when (this) { internal fun FirOperation.invert(): FirOperation = when (this) {
FirOperation.EQ -> FirOperation.NOT_EQ FirOperation.EQ -> FirOperation.NOT_EQ
FirOperation.NOT_EQ -> FirOperation.EQ FirOperation.NOT_EQ -> FirOperation.EQ
@@ -95,6 +75,7 @@ internal fun FirOperation.invert(): FirOperation = when (this) {
else -> throw IllegalArgumentException("$this can not be inverted") else -> throw IllegalArgumentException("$this can not be inverted")
} }
@DfaInternals
internal fun FirOperation.isEq(): Boolean { internal fun FirOperation.isEq(): Boolean {
return when (this) { return when (this) {
FirOperation.EQ, FirOperation.IDENTITY -> true FirOperation.EQ, FirOperation.IDENTITY -> true
@@ -108,10 +89,32 @@ internal fun FirFunctionCall.isBooleanNot(): Boolean {
return symbol.callableId == FirDataFlowAnalyzer.KOTLIN_BOOLEAN_NOT return symbol.callableId == FirDataFlowAnalyzer.KOTLIN_BOOLEAN_NOT
} }
internal fun ConeConstantReference.toCondition(): Condition = when (this) { internal fun ConeConstantReference.toOperation(): Operation = when (this) {
ConeConstantReference.NULL -> Condition.EqNull ConeConstantReference.NULL -> Operation.EqNull
ConeConstantReference.NOT_NULL -> Condition.NotEqNull ConeConstantReference.NOT_NULL -> Operation.NotEqNull
ConeBooleanConstantReference.TRUE -> Condition.EqTrue ConeBooleanConstantReference.TRUE -> Operation.EqTrue
ConeBooleanConstantReference.FALSE -> Condition.EqFalse ConeBooleanConstantReference.FALSE -> Operation.EqFalse
else -> throw IllegalArgumentException("$this can not be transformed to Condition") else -> throw IllegalArgumentException("$this can not be transformed to Operation")
} }
@DfaInternals
internal val FirExpression.coneType: ConeKotlinType?
get() = typeRef.coneTypeSafe()
@DfaInternals
internal val FirElement.symbol: AbstractFirBasedSymbol<*>?
get() = when (this) {
is FirResolvable -> symbol
is FirSymbolOwner<*> -> symbol
is FirWhenSubjectExpression -> whenSubject.whenExpression.subject?.symbol
else -> null
}?.takeIf { this is FirThisReceiverExpression || it !is FirFunctionSymbol<*> }
@DfaInternals
internal val FirResolvable.symbol: AbstractFirBasedSymbol<*>?
get() = when (val reference = calleeReference) {
is FirExplicitThisReference -> reference.boundSymbol
is FirResolvedNamedReference -> reference.resolvedSymbol
is FirNamedReferenceWithCandidate -> reference.candidateSymbol
else -> null
}
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.InferenceComponents import org.jetbrains.kotlin.fir.resolve.calls.InferenceComponents
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionStageRunner import org.jetbrains.kotlin.fir.resolve.calls.ResolutionStageRunner
import org.jetbrains.kotlin.fir.resolve.dfa.new.FirDataFlowAnalyzer import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter
import org.jetbrains.kotlin.fir.resolve.transformers.* import org.jetbrains.kotlin.fir.resolve.transformers.*
import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.FirScope
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.extractLambdaInfoFromFunctionalType import org.jetbrains.kotlin.fir.resolve.calls.extractLambdaInfoFromFunctionalType
import org.jetbrains.kotlin.fir.resolve.dfa.commonSuperTypeOrNull
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.* import org.jetbrains.kotlin.fir.resolve.transformers.*
import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolveTransformer.Companion.resolveStatus import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolveTransformer.Companion.resolveStatus
@@ -5,6 +5,9 @@
package org.jetbrains.kotlin.fir.types package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator.commonSuperType
import org.jetbrains.kotlin.types.AbstractNullabilityChecker import org.jetbrains.kotlin.types.AbstractNullabilityChecker
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
@@ -16,4 +19,22 @@ object ConeNullabilityChecker {
.hasNotNullSupertype(actualType, AbstractTypeCheckerContext.SupertypesPolicy.LowerIfFlexible) .hasNotNullSupertype(actualType, AbstractTypeCheckerContext.SupertypesPolicy.LowerIfFlexible)
} }
} }
} }
fun ConeInferenceContext.commonSuperTypeOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) {
0 -> null
1 -> types.first()
else -> with(NewCommonSuperTypeCalculator) {
commonSuperType(types) as ConeKotlinType
}
}
}
fun ConeInferenceContext.intersectTypesOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) {
0 -> null
1 -> types.first()
else -> ConeTypeIntersector.intersectTypes(this, types)
}
}