[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.resolve.calls.InferenceComponents
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.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.types.FirTypeRef
@@ -5,10 +5,10 @@
package org.jetbrains.kotlin.fir.resolve.dfa
enum class Condition {
enum class Operation {
EqTrue, EqFalse, EqNull, NotEqNull;
fun invert(): Condition = when (this) {
fun invert(): Operation = when (this) {
EqTrue -> EqFalse
EqFalse -> EqTrue
EqNull -> NotEqNull
@@ -21,6 +21,4 @@ enum class Condition {
EqNull -> "== 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.
*/
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.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.ResolutionMode
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.contracts.buildContractFir
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.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import kotlin.IllegalArgumentException
@UseExperimental(DfaInternals::class)
abstract class FirDataFlowAnalyzer<FLOW : Flow>(
protected val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents
) {
@@ -44,12 +42,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
): FirDataFlowAnalyzer<*> = object : FirDataFlowAnalyzer<PersistentFlow>(components) {
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) {
val symbol = variable.identifier.symbol
val index = receiverStack.getReceiverIndex(symbol) ?: return
val info = flow.getKnownInfo(variable)
val info = flow.getTypeStatement(variable)
if (info == null) {
receiverStack.replaceReceiverType(index, receiverStack.getOriginalType(index))
@@ -91,7 +89,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
*/
val symbol: AbstractFirBasedSymbol<*> = qualifiedAccessExpression.symbol ?: 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> {
@@ -165,31 +163,31 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall)
val isNotNullCheck = operation == FirOperation.IS && type.nullability == ConeNullability.NOT_NULL
if (operandVariable.isReal()) {
val hasTypeInfo = operandVariable has type
val hasNotTypeInfo = operandVariable hasNot type
val hasTypeInfo = operandVariable typeEq type
val hasNotTypeInfo = operandVariable typeNotEq type
fun chooseInfo(trueBranch: Boolean) =
if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) hasTypeInfo else hasNotTypeInfo
flow.addLogicStatement((expressionVariable eq true) implies chooseInfo(true))
flow.addLogicStatement((expressionVariable eq false) implies chooseInfo(false))
flow.addImplication((expressionVariable eq true) implies chooseInfo(true))
flow.addImplication((expressionVariable eq false) implies chooseInfo(false))
if (operation == FirOperation.NOT_IS && type == nullableNothing) {
flow.addKnownInfo(operandVariable has any)
flow.addTypeStatement(operandVariable typeEq any)
}
if (isNotNullCheck) {
flow.addLogicStatement((expressionVariable eq true) implies (operandVariable has any)) }
flow.addImplication((expressionVariable eq true) implies (operandVariable typeEq any)) }
} else {
if (isNotNullCheck) {
flow.addLogicStatement((expressionVariable eq true) implies (operandVariable notEq null))
flow.addImplication((expressionVariable eq true) implies (operandVariable notEq null))
}
}
}
FirOperation.AS -> {
if (operandVariable.isReal()) {
flow.addKnownInfo(operandVariable has type)
flow.addTypeStatement(operandVariable typeEq type)
} else {
logicSystem.approveStatementsInsideFlow(
flow,
@@ -203,11 +201,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
FirOperation.SAFE_AS -> {
val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall)
if (operandVariable.isReal()) {
flow.addLogicStatement((expressionVariable notEq null) implies (operandVariable has type))
flow.addLogicStatement((expressionVariable eq null) implies (operandVariable hasNot type))
flow.addImplication((expressionVariable notEq null) implies (operandVariable typeEq type))
flow.addImplication((expressionVariable eq null) implies (operandVariable typeNotEq type))
} else {
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 operandVariable = variableStorage.getOrCreateVariable(operand)
// 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) {
flow.addLogicStatement((expressionVariable eq isEq) implies (operandVariable has any))
flow.addImplication((expressionVariable eq isEq) implies (operandVariable typeEq any))
}
// propagating facts for (... == true) and (... == false)
@@ -256,7 +254,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val constValue = const.value as Boolean
val shouldInvert = isEq xor constValue
logicSystem.translateConditionalVariableInStatements(
logicSystem.translateVariableFromConditionInStatements(
flow,
operandVariable,
expressionVariable,
@@ -291,21 +289,21 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
false -> operandVariable notEq null
}
logicSystem.approvePredicate(flow, predicate).forEach { effect ->
flow.addLogicStatement((expressionVariable eq true) implies effect)
flow.addLogicStatement((expressionVariable eq false) implies effect.invert())
logicSystem.approveOperationStatement(flow, predicate).forEach { effect ->
flow.addImplication((expressionVariable eq true) implies effect)
flow.addImplication((expressionVariable eq false) implies effect.invert())
}
flow.addLogicStatement((expressionVariable eq isEq) implies (operandVariable eq null))
flow.addLogicStatement((expressionVariable notEq isEq) implies (operandVariable notEq null))
flow.addImplication((expressionVariable eq isEq) implies (operandVariable eq null))
flow.addImplication((expressionVariable notEq isEq) implies (operandVariable notEq null))
if (operandVariable is RealVariable) {
flow.addLogicStatement((expressionVariable eq isEq) implies (operandVariable hasNot any))
flow.addLogicStatement((expressionVariable notEq isEq) implies (operandVariable has any))
flow.addImplication((expressionVariable eq isEq) implies (operandVariable typeNotEq any))
flow.addImplication((expressionVariable notEq isEq) implies (operandVariable typeEq any))
// TODO: design do we need casts to Nothing?
// flow.addLogicStatement((expressionVariable eq !isEq) implies (operandVariable has nullableNothing))
// flow.addLogicStatement((expressionVariable notEq !isEq) implies (operandVariable hasNot nullableNothing))
// flow.addImplication((expressionVariable eq !isEq) implies (operandVariable typeEq nullableNothing))
// flow.addImplication((expressionVariable notEq !isEq) implies (operandVariable typeNotEq nullableNothing))
}
node.flow = flow
}
@@ -323,7 +321,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val node = graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow()
val argument = checkNotNullCall.argument
val operandVariable = variableStorage.getOrCreateRealVariable(argument.symbol, argument) ?: return
node.flow.addKnownInfo(operandVariable has any)
node.flow.addTypeStatement(operandVariable typeEq any)
}
// ----------------------------------- When -----------------------------------
@@ -504,7 +502,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
if (shouldFork) {
flow = logicSystem.forkFlow(flow)
}
flow.addKnownInfo(variable has type)
flow.addTypeStatement(variable typeEq type)
}
is SyntheticVariable -> {
flow = logicSystem.approveStatementsInsideFlow(
@@ -544,9 +542,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
is RealVariable -> variable.explicitReceiverVariable!!
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()) {
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 -> {
logicSystem.replaceConditionalVariableInStatements(
logicSystem.replaceVariableFromConditionInStatements(
lastNode.flow,
argumentVariable,
functionCallVariable,
filter = { it.condition.condition == value.toCondition() }
filter = { it.condition.operation == value.toOperation() }
)
}
ConeConstantReference.NOT_NULL, ConeConstantReference.NULL -> {
logicSystem.replaceConditionalVariableInStatements(
logicSystem.replaceVariableFromConditionInStatements(
lastNode.flow,
argumentVariable,
functionCallVariable,
filter = { it.condition.condition == Condition.EqTrue },
transform = { Predicate(it.condition.variable, value.toCondition()) implies it.effect }
filter = { it.condition.operation == Operation.EqTrue },
transform = { OperationStatement(it.condition.variable, value.toOperation()) implies it.effect }
)
}
@@ -628,7 +626,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
* x.length
* }
*/
logicSystem.replaceConditionalVariableInStatements(node.flow, initializerVariable, propertyVariable)
logicSystem.replaceVariableFromConditionInStatements(node.flow, initializerVariable, propertyVariable)
return
}
@@ -636,12 +634,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
if (initializerVariable.isStable) {
variableStorage.attachSymbolToVariable(variable.symbol, initializerVariable)
} else {
node.flow.addLogicStatement((propertyVariable notEq null) implies (initializerVariable notEq null))
node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null))
}
}
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 == False
val approvedIfTrue: MutableKnownFacts = mutableMapOf()
logicSystem.approvePredicateTo(approvedIfTrue, flowFromRight, leftVariable eq bothEvaluated, conditionalFromLeft)
logicSystem.approvePredicateTo(approvedIfTrue, flowFromRight, rightVariable eq bothEvaluated, conditionalFromRight)
val approvedIfTrue: MutableTypeStatements = mutableMapOf()
logicSystem.approveStatementsTo(approvedIfTrue, flowFromRight, leftVariable eq bothEvaluated, conditionalFromLeft)
logicSystem.approveStatementsTo(approvedIfTrue, flowFromRight, rightVariable eq bothEvaluated, conditionalFromRight)
approvedFromRight.forEach { (variable, info) ->
approvedIfTrue.addInfo(variable, info)
approvedIfTrue.addStatement(variable, info)
}
approvedIfTrue.values.forEach { info ->
flow.addLogicStatement((operatorVariable eq bothEvaluated) implies info)
flow.addImplication((operatorVariable eq bothEvaluated) implies info)
}
// left && right == False
// left || right == True
val approvedIfFalse: MutableKnownFacts = mutableMapOf()
val leftIsFalse = logicSystem.approvePredicate(flowFromLeft, leftVariable eq onlyLeftEvaluated, conditionalFromLeft)
val rightIsFalse = logicSystem.approvePredicate(flowFromRight, rightVariable eq onlyLeftEvaluated, conditionalFromRight)
approvedIfFalse.mergeInfo(logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse))
val approvedIfFalse: MutableTypeStatements = mutableMapOf()
val leftIsFalse = logicSystem.approveOperationStatement(flowFromLeft, leftVariable eq onlyLeftEvaluated, conditionalFromLeft)
val rightIsFalse = logicSystem.approveOperationStatement(flowFromRight, rightVariable eq onlyLeftEvaluated, conditionalFromRight)
approvedIfFalse.mergeTypeStatements(logicSystem.orForTypeStatements(leftIsFalse, rightIsFalse))
approvedIfFalse.values.forEach { info ->
flow.addLogicStatement((operatorVariable eq onlyLeftEvaluated) implies info)
flow.addImplication((operatorVariable eq onlyLeftEvaluated) implies info)
}
node.flow = flow
@@ -755,7 +753,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
val booleanExpressionVariable = variableStorage.getOrCreateVariable(node.previousNodes.first().fir)
val variable = variableStorage.getOrCreateVariable(functionCall)
logicSystem.replaceConditionalVariableInStatements(
logicSystem.replaceVariableFromConditionInStatements(
node.flow,
booleanExpressionVariable,
variable,
@@ -798,16 +796,16 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
node.flow = logicSystem.joinFlow(previousFlows)
}
private fun FLOW.addLogicStatement(statement: LogicStatement) {
logicSystem.addLogicStatement(this, statement)
private fun FLOW.addImplication(statement: Implication) {
logicSystem.addImplication(this, statement)
}
private fun FLOW.addKnownInfo(info: DataFlowInfo) {
logicSystem.addKnownInfo(this, info)
private fun FLOW.addTypeStatement(info: TypeStatement) {
logicSystem.addTypeStatement(this, info)
}
private fun FLOW.removeAllAboutVariable(variable: RealVariable?) {
if (variable == null) return
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.
*/
package org.jetbrains.kotlin.fir.resolve.dfa.new
package org.jetbrains.kotlin.fir.resolve.dfa
import com.google.common.collect.ArrayListMultimap
import kotlinx.collections.immutable.*
@@ -11,15 +11,14 @@ import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import java.util.*
import kotlin.NoSuchElementException
import kotlin.collections.HashSet
data class PersistentDataFlowInfo(
data class PersistentTypeStatement(
override val variable: RealVariable,
override val exactType: PersistentSet<ConeKotlinType>,
override val exactNotType: PersistentSet<ConeKotlinType>
) : DataFlowInfo() {
override operator fun plus(other: DataFlowInfo): PersistentDataFlowInfo {
return PersistentDataFlowInfo(
) : TypeStatement() {
override operator fun plus(other: TypeStatement): PersistentTypeStatement {
return PersistentTypeStatement(
variable,
exactType + other.exactType,
exactNotType + other.exactNotType
@@ -29,49 +28,49 @@ data class PersistentDataFlowInfo(
override val isEmpty: Boolean
get() = exactType.isEmpty() && exactNotType.isEmpty()
override fun invert(): PersistentDataFlowInfo {
return PersistentDataFlowInfo(variable, exactNotType, exactType)
override fun invert(): PersistentTypeStatement {
return PersistentTypeStatement(variable, exactNotType, exactType)
}
}
typealias PersistentKnownFacts = PersistentMap<RealVariable, PersistentDataFlowInfo>
typealias PersistentLogicStatements = PersistentMap<DataFlowVariable, PersistentList<LogicStatement>>
typealias PersistentApprovedTypeStatements = PersistentMap<RealVariable, PersistentTypeStatement>
typealias PersistentImplications = PersistentMap<DataFlowVariable, PersistentList<Implication>>
class PersistentFlow : Flow {
val previousFlow: PersistentFlow?
var knownFacts: PersistentKnownFacts
var logicStatements: PersistentLogicStatements
var approvedTypeStatements: PersistentApprovedTypeStatements
var logicStatements: PersistentImplications
val level: Int
var knownFactsDiff: PersistentKnownFacts = persistentHashMapOf()
var approvedTypeStatementsDiff: PersistentApprovedTypeStatements = persistentHashMapOf()
constructor(previousFlow: PersistentFlow) {
this.previousFlow = previousFlow
knownFacts = previousFlow.knownFacts
approvedTypeStatements = previousFlow.approvedTypeStatements
logicStatements = previousFlow.logicStatements
level = previousFlow.level + 1
}
constructor() {
previousFlow = null
knownFacts = persistentHashMapOf()
approvedTypeStatements = persistentHashMapOf()
logicStatements = persistentHashMapOf()
level = 1
}
override fun getKnownInfo(variable: RealVariable): DataFlowInfo? {
return knownFacts[variable]
override fun getTypeStatement(variable: RealVariable): TypeStatement? {
return approvedTypeStatements[variable]
}
override fun getLogicStatements(variable: DataFlowVariable): Collection<LogicStatement> {
override fun getImplications(variable: DataFlowVariable): Collection<Implication> {
return logicStatements[variable] ?: emptyList()
}
override fun getVariablesInKnownInfos(): Collection<RealVariable> {
return knownFacts.keys
override fun getVariablesInTypeStatements(): Collection<RealVariable> {
return approvedTypeStatements.keys
}
override fun removeConditions(variable: DataFlowVariable): Collection<LogicStatement> {
return getLogicStatements(variable).also {
override fun removeOperations(variable: DataFlowVariable): Collection<Implication> {
return getImplications(variable).also {
if (it.isNotEmpty()) {
logicStatements -= variable
}
@@ -79,8 +78,7 @@ class PersistentFlow : Flow {
}
}
abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, context: ConeInferenceContext) :
LogicSystem<PersistentFlow>(context) {
abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSystem<PersistentFlow>(context) {
override fun createEmptyFlow(): PersistentFlow {
return PersistentFlow()
}
@@ -99,11 +97,11 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
?: return commonFlow
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
commonFlow.knownFacts = commonFlow.knownFacts.addNewInfo(info)
commonFlow.approvedTypeStatements = commonFlow.approvedTypeStatements.addTypeStatement(info)
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
}
private fun PersistentFlow.getKnownFactsDiff(variable: RealVariable, parentFlow: PersistentFlow): MutableDataFlowInfo {
private fun PersistentFlow.getApprovedTypeStatementsDiff(variable: RealVariable, parentFlow: PersistentFlow): MutableTypeStatement {
var flow = this
val result = MutableDataFlowInfo(variable)
val result = MutableTypeStatement(variable)
while (flow != parentFlow) {
flow.knownFactsDiff[variable]?.let {
flow.approvedTypeStatementsDiff[variable]?.let {
result += it
}
flow = flow.previousFlow!!
@@ -131,7 +129,7 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
private fun PersistentFlow.diffVariablesIterable(parentFlow: PersistentFlow): Iterable<RealVariable> =
object : DiffIterable<RealVariable>(parentFlow, this) {
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) {
knownFacts = knownFacts.addNewInfo(info)
approvedTypeStatements = approvedTypeStatements.addTypeStatement(statement)
if (previousFlow != null) {
knownFactsDiff = knownFactsDiff.addNewInfo(info)
approvedTypeStatementsDiff = approvedTypeStatementsDiff.addTypeStatement(statement)
}
if (info.variable.isThisReference) {
processUpdatedReceiverVariable(flow, info.variable)
if (statement.variable.isThisReference) {
processUpdatedReceiverVariable(flow, statement.variable)
}
}
}
override fun addLogicStatement(flow: PersistentFlow, statement: LogicStatement) {
if (statement.condition == statement.effect) return
override fun addImplication(flow: PersistentFlow, implication: Implication) {
if (implication.condition == implication.effect) return
with(flow) {
val variable = statement.condition.variable
val existingFacts = logicStatements[variable]
logicStatements = if (existingFacts == null) {
logicStatements.put(variable, persistentListOf(statement))
val variable = implication.condition.variable
val existingImplications = logicStatements[variable]
logicStatements = if (existingImplications == null) {
logicStatements.put(variable, persistentListOf(implication))
} else {
logicStatements.put(variable, existingFacts + statement)
logicStatements.put(variable, existingImplications + implication)
}
}
}
override fun removeAllAboutVariable(flow: PersistentFlow, variable: RealVariable) {
flow.knownFacts -= variable
flow.knownFactsDiff -= variable
flow.approvedTypeStatements -= variable
flow.approvedTypeStatementsDiff -= variable
// TODO: should we search variable in all logic statements?
}
override fun translateConditionalVariableInStatements(
override fun translateVariableFromConditionInStatements(
flow: PersistentFlow,
originalVariable: DataFlowVariable,
newVariable: DataFlowVariable,
shouldRemoveOriginalStatements: Boolean,
filter: (LogicStatement) -> Boolean,
transform: (LogicStatement) -> LogicStatement
filter: (Implication) -> Boolean,
transform: (Implication) -> Implication
) {
with(flow) {
val statements = logicStatements[originalVariable]?.takeIf { it.isNotEmpty() } ?: return
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)
}.toPersistentList()
if (shouldRemoveOriginalStatements) {
@@ -216,13 +214,13 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
override fun approveStatementsInsideFlow(
flow: PersistentFlow,
predicate: Predicate,
approvedStatement: OperationStatement,
shouldForkFlow: Boolean,
shouldRemoveSynthetics: Boolean
): PersistentFlow {
val approvedFacts = approvePredicatesInternal(
val approvedFacts = approveOperationStatementsInternal(
flow,
predicate,
approvedStatement,
initialStatements = null,
shouldRemoveSynthetics
)
@@ -232,14 +230,14 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
val updatedReceivers = mutableSetOf<RealVariable>()
approvedFacts.asMap().forEach { (variable, infos) ->
var resultInfo = PersistentDataFlowInfo(variable, persistentSetOf(), persistentSetOf())
var resultInfo = PersistentTypeStatement(variable, persistentSetOf(), persistentSetOf())
for (info in infos) {
resultInfo += info
}
if (variable.isThisReference) {
updatedReceivers += variable
}
addKnownInfo(resultFlow, resultInfo)
addTypeStatement(resultFlow, resultInfo)
}
updatedReceivers.forEach {
@@ -249,86 +247,63 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
return resultFlow
}
private fun approvePredicatesInternal(
private fun approveOperationStatementsInternal(
flow: PersistentFlow,
predicate: Predicate,
initialStatements: Collection<LogicStatement>?,
approvedStatement: OperationStatement,
initialStatements: Collection<Implication>?,
shouldRemoveSynthetics: Boolean
): ArrayListMultimap<RealVariable, DataFlowInfo> {
val approvedFacts: ArrayListMultimap<RealVariable, DataFlowInfo> = ArrayListMultimap.create()
val predicatesToApprove = LinkedList<Predicate>().apply { this += predicate }
approvePredicatesInternal(flow, predicatesToApprove, initialStatements, shouldRemoveSynthetics, approvedFacts)
): ArrayListMultimap<RealVariable, TypeStatement> {
val approvedFacts: ArrayListMultimap<RealVariable, TypeStatement> = ArrayListMultimap.create()
val approvedStatements = LinkedList<OperationStatement>().apply { this += approvedStatement }
approveOperationStatementsInternal(flow, approvedStatements, initialStatements, shouldRemoveSynthetics, approvedFacts)
return approvedFacts
}
private fun approvePredicatesInternal(
private fun approveOperationStatementsInternal(
flow: PersistentFlow,
predicatesToApprove: LinkedList<Predicate>,
initialStatements: Collection<LogicStatement>?,
approvedStatements: LinkedList<OperationStatement>,
initialStatements: Collection<Implication>?,
shouldRemoveSynthetics: Boolean,
approvedFacts: ArrayListMultimap<RealVariable, DataFlowInfo>
approvedTypeStatements: ArrayListMultimap<RealVariable, TypeStatement>
) {
if (predicatesToApprove.isEmpty()) return
val approvedVariables = mutableSetOf<RealVariable>()
val approvedPredicates = mutableSetOf<Predicate>()
if (approvedStatements.isEmpty()) return
val approvedOperationStatements = mutableSetOf<OperationStatement>()
var firstIteration = true
while (predicatesToApprove.isNotEmpty()) {
while (approvedStatements.isNotEmpty()) {
@Suppress("NAME_SHADOWING")
val predicate: Predicate = predicatesToApprove.removeFirst()
val approvedStatement: OperationStatement = approvedStatements.removeFirst()
// Defense from cycles in facts
if (!approvedPredicates.add(predicate)) {
if (!approvedOperationStatements.add(approvedStatement)) {
continue
}
val statements = initialStatements?.takeIf { firstIteration }
?: flow.logicStatements[predicate.variable]?.takeIf { it.isNotEmpty() }
?: flow.logicStatements[approvedStatement.variable]?.takeIf { it.isNotEmpty() }
?: continue
if (shouldRemoveSynthetics && predicate.variable.isSynthetic()) {
flow.logicStatements -= predicate.variable
if (shouldRemoveSynthetics && approvedStatement.variable.isSynthetic()) {
flow.logicStatements -= approvedStatement.variable
}
for (statement in statements) {
if (statement.condition == predicate) {
if (statement.condition == approvedStatement) {
when (val effect = statement.effect) {
is Predicate -> predicatesToApprove += effect
is DataFlowInfo -> {
approvedFacts.put(effect.variable, effect)
approvedVariables += effect.variable
}
is OperationStatement -> approvedStatements += effect
is TypeStatement -> approvedTypeStatements.put(effect.variable, effect)
}
}
}
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(
destination: MutableKnownFacts,
override fun approveStatementsTo(
destination: MutableTypeStatements,
flow: PersistentFlow,
predicate: Predicate,
statements: Collection<LogicStatement>
approvedStatement: OperationStatement,
statements: Collection<Implication>
) {
val approvePredicates = approvePredicatesInternal(flow, predicate, statements, shouldRemoveSynthetics = false)
approvePredicates.asMap().forEach { (variable, infos) ->
val approveOperationStatements = approveOperationStatementsInternal(flow, approvedStatement, statements, shouldRemoveSynthetics = false)
approveOperationStatements.asMap().forEach { (variable, infos) ->
for (info in infos) {
val mutableInfo = info.asMutableInfo()
val mutableInfo = info.asMutableStatement()
destination.put(variable, mutableInfo) {
it += mutableInfo
it
@@ -346,11 +321,11 @@ abstract class PersistentLogicSystem(private val anyType: ConeKotlinType, contex
return InfoForBooleanOperator(
leftFlow.logicStatements[leftVariable] ?: 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()
}
@@ -377,35 +352,25 @@ private fun lowestCommonFlow(left: PersistentFlow, right: PersistentFlow): Persi
return left
}
private 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
}
private fun PersistentKnownFacts.addNewInfo(info: DataFlowInfo): PersistentKnownFacts {
private fun PersistentApprovedTypeStatements.addTypeStatement(info: TypeStatement): PersistentApprovedTypeStatements {
val variable = info.variable
val existingInfo = this[variable]
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)
} else {
put(variable, existingInfo + info)
}
}
private fun DataFlowInfo.toPersistent(): PersistentDataFlowInfo = PersistentDataFlowInfo(
private fun TypeStatement.toPersistent(): PersistentTypeStatement = PersistentTypeStatement(
variable,
exactType.toPersistentSet(),
exactNotType.toPersistentSet()
)
fun DataFlowInfo.asMutableInfo(): MutableDataFlowInfo = when (this) {
is MutableDataFlowInfo -> this
is PersistentDataFlowInfo -> MutableDataFlowInfo(variable, exactType.toMutableSet(), exactNotType.toMutableSet())
else -> throw IllegalArgumentException("Unknown DataFlowInfo type: ${this::class}")
fun TypeStatement.asMutableStatement(): MutableTypeStatement = when (this) {
is MutableTypeStatement -> this
is PersistentTypeStatement -> MutableTypeStatement(variable, exactType.toMutableSet(), exactNotType.toMutableSet())
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.
*/
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
@@ -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.impl.FirFunctionSymbol
@UseExperimental(DfaInternals::class)
class VariableStorage {
private var counter = 1
private val realVariables: MutableMap<Identifier, RealVariable> = HashMap()
@@ -53,7 +54,6 @@ class VariableStorage {
*/
private fun createRealVariableInternal(identifier: Identifier, originalFir: FirElement): RealVariable {
val receiver: FirExpression?
val isSafeCall: Boolean
val isThisReference: Boolean
val expression = when (originalFir) {
is FirQualifiedAccessExpression -> originalFir
@@ -63,16 +63,14 @@ class VariableStorage {
if (expression != null) {
receiver = expression.explicitReceiver
isSafeCall = expression.safe
isThisReference = expression.calleeReference is FirThisReference
} else {
receiver = null
isSafeCall = false
isThisReference = false
}
val receiverVariable = receiver?.let { getOrCreateVariable(it) }
return RealVariable(identifier, isThisReference, receiverVariable, isSafeCall, counter++)
return RealVariable(identifier, isThisReference, receiverVariable, counter++)
}
@JvmName("getOrCreateRealVariableOrNull")
@@ -134,19 +132,3 @@ class VariableStorage {
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.
*/
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.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.modality
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.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
@@ -18,7 +17,7 @@ import org.jetbrains.kotlin.fir.types.ConeKotlinType
// --------------------------------------- Variables ---------------------------------------
class Identifier(
data class Identifier(
val symbol: AbstractFirBasedSymbol<*>,
val dispatchReceiver: DataFlowVariable?,
val extensionReceiver: DataFlowVariable?
@@ -27,26 +26,6 @@ class Identifier(
val callableId = (symbol as? FirCallableSymbol<*>)?.callableId
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) {
@@ -61,7 +40,6 @@ class RealVariable(
val identifier: Identifier,
val isThisReference: Boolean,
val explicitReceiverVariable: DataFlowVariable?,
val isSafeCall: Boolean,
variableIndexForDebug: Int
) : DataFlowVariable(variableIndexForDebug) {
override val isStable: Boolean by lazy {
@@ -72,6 +50,7 @@ class RealVariable(
property.isLocal -> true
property.isVar -> false
property.modality != Modality.FINAL -> false
// TODO: getters, delegates
else -> true
}
}
@@ -105,6 +84,7 @@ class SyntheticVariable(val fir: FirElement, variableIndexForDebug: Int) : DataF
}
override fun hashCode(): Int {
// hack for enums
return if (fir is FirResolvedQualifier) {
31 * fir.packageFqName.hashCode() + fir.classId.hashCode()
} else {
@@ -122,26 +102,33 @@ private infix fun FirElement.isEqualsTo(other: FirElement): Boolean {
// --------------------------------------- Facts ---------------------------------------
sealed class PredicateEffect<T : PredicateEffect<T>> {
sealed class Statement<T : Statement<T>> {
abstract fun invert(): T
}
data class Predicate(val variable: DataFlowVariable, val condition: Condition) : PredicateEffect<Predicate>() {
override fun invert(): Predicate {
return Predicate(variable, condition.invert())
/*
* Examples:
* 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 {
return "$variable $condition"
return "$variable $operation"
}
}
abstract class DataFlowInfo : PredicateEffect<DataFlowInfo>() {
abstract class TypeStatement : Statement<TypeStatement>() {
abstract val variable: RealVariable
abstract val exactType: 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
val isNotEmpty: Boolean get() = !isEmpty
@@ -150,12 +137,12 @@ abstract class DataFlowInfo : PredicateEffect<DataFlowInfo>() {
}
}
class MutableDataFlowInfo(
class MutableTypeStatement(
override val variable: RealVariable,
override val exactType: MutableSet<ConeKotlinType> = HashSet(),
override val exactNotType: MutableSet<ConeKotlinType> = HashSet()
) : DataFlowInfo() {
override fun plus(other: DataFlowInfo): MutableDataFlowInfo = MutableDataFlowInfo(
) : TypeStatement() {
override fun plus(other: TypeStatement): MutableTypeStatement = MutableTypeStatement(
variable,
HashSet(exactType).apply { addAll(other.exactType) },
HashSet(exactNotType).apply { addAll(other.exactNotType) }
@@ -164,65 +151,64 @@ class MutableDataFlowInfo(
override val isEmpty: Boolean
get() = exactType.isEmpty() && exactType.isEmpty()
override fun invert(): DataFlowInfo {
return MutableDataFlowInfo(
override fun invert(): TypeStatement {
return MutableTypeStatement(
variable,
HashSet(exactNotType),
HashSet(exactType)
)
}
operator fun plusAssign(info: DataFlowInfo) {
operator fun plusAssign(info: TypeStatement) {
exactType += info.exactType
exactNotType += info.exactNotType
}
fun copy(): MutableDataFlowInfo = MutableDataFlowInfo(variable, HashSet(exactType), HashSet(exactNotType))
fun copy(): MutableTypeStatement = MutableTypeStatement(variable, HashSet(exactType), HashSet(exactNotType))
}
class LogicStatement(
val condition: Predicate,
val effect: PredicateEffect<*>
class Implication(
val condition: OperationStatement,
val effect: Statement<*>
) {
override fun toString(): String {
return "$condition -> $effect"
}
}
fun LogicStatement.invertCondition(): LogicStatement = LogicStatement(condition.invert(), effect)
fun Implication.invertCondition(): Implication = Implication(condition.invert(), effect)
// --------------------------------------- Aliases ---------------------------------------
typealias KnownInfos = Map<RealVariable, DataFlowInfo>
typealias MutableKnownFacts = MutableMap<RealVariable, MutableDataFlowInfo>
typealias LogicStatements = Multimap<Predicate, LogicStatement>
typealias TypeStatements = Map<RealVariable, TypeStatement>
typealias MutableTypeStatements = MutableMap<RealVariable, MutableTypeStatement>
// --------------------------------------- DSL ---------------------------------------
infix fun DataFlowVariable.eq(constant: Boolean?): Predicate {
infix fun DataFlowVariable.eq(constant: Boolean?): OperationStatement {
val condition = when (constant) {
true -> Condition.EqTrue
false -> Condition.EqFalse
null -> Condition.EqNull
true -> Operation.EqTrue
false -> Operation.EqFalse
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) {
true -> Condition.EqFalse
false -> Condition.EqTrue
null -> Condition.NotEqNull
true -> Operation.EqFalse
false -> Operation.EqTrue
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.has(type: ConeKotlinType): DataFlowInfo =
MutableDataFlowInfo(this, HashSet<ConeKotlinType>().apply { this += type }, HashSet())
infix fun RealVariable.typeEq(types: MutableSet<ConeKotlinType>): TypeStatement = MutableTypeStatement(this, types, HashSet())
infix fun RealVariable.typeEq(type: ConeKotlinType): TypeStatement =
MutableTypeStatement(this, HashSet<ConeKotlinType>().apply { this += type }, HashSet())
infix fun RealVariable.hasNot(types: MutableSet<ConeKotlinType>): DataFlowInfo = MutableDataFlowInfo(this, HashSet(), types)
infix fun RealVariable.hasNot(type: ConeKotlinType): DataFlowInfo =
MutableDataFlowInfo(this, HashSet(), HashSet<ConeKotlinType>().apply { this += type })
infix fun RealVariable.typeNotEq(types: MutableSet<ConeKotlinType>): TypeStatement = MutableTypeStatement(this, HashSet(), types)
infix fun RealVariable.typeNotEq(type: ConeKotlinType): TypeStatement =
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.
*/
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.ConeConstantReference
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirOperation
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.resolve.dfa.Condition
import org.jetbrains.kotlin.fir.references.impl.FirExplicitThisReference
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.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeIntersector
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 kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
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)
fun DataFlowVariable.isSynthetic(): Boolean {
contract {
@@ -60,15 +41,15 @@ fun DataFlowVariable.isReal(): Boolean {
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) {
put(variable, info.asMutableInfo()) { it.apply { this += info } }
fun MutableTypeStatements.addStatement(variable: RealVariable, statement: TypeStatement) {
put(variable, statement.asMutableStatement()) { it.apply { this += statement } }
}
fun MutableKnownFacts.mergeInfo(other: Map<RealVariable, DataFlowInfo>) {
fun MutableTypeStatements.mergeTypeStatements(other: TypeStatements) {
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) {
FirOperation.EQ -> FirOperation.NOT_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")
}
@DfaInternals
internal fun FirOperation.isEq(): Boolean {
return when (this) {
FirOperation.EQ, FirOperation.IDENTITY -> true
@@ -108,10 +89,32 @@ internal fun FirFunctionCall.isBooleanNot(): Boolean {
return symbol.callableId == FirDataFlowAnalyzer.KOTLIN_BOOLEAN_NOT
}
internal fun ConeConstantReference.toCondition(): Condition = when (this) {
ConeConstantReference.NULL -> Condition.EqNull
ConeConstantReference.NOT_NULL -> Condition.NotEqNull
ConeBooleanConstantReference.TRUE -> Condition.EqTrue
ConeBooleanConstantReference.FALSE -> Condition.EqFalse
else -> throw IllegalArgumentException("$this can not be transformed to Condition")
}
internal fun ConeConstantReference.toOperation(): Operation = when (this) {
ConeConstantReference.NULL -> Operation.EqNull
ConeConstantReference.NOT_NULL -> Operation.NotEqNull
ConeBooleanConstantReference.TRUE -> Operation.EqTrue
ConeBooleanConstantReference.FALSE -> Operation.EqFalse
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.calls.InferenceComponents
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.transformers.*
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.ImplicitExtensionReceiverValue
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.transformers.*
import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolveTransformer.Companion.resolveStatus
@@ -5,6 +5,9 @@
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.AbstractTypeCheckerContext
@@ -16,4 +19,22 @@ object ConeNullabilityChecker {
.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)
}
}