[FIR] Cleanup DFA code and rename some classes for better understanding
This commit is contained in:
@@ -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
|
||||
}
|
||||
-30
@@ -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
|
||||
+57
-59
@@ -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,
|
||||
)
|
||||
}
|
||||
+91
-126
@@ -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}")
|
||||
}
|
||||
+4
-22
@@ -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
|
||||
}
|
||||
+50
-64
@@ -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
|
||||
)
|
||||
}
|
||||
+45
-42
@@ -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
|
||||
}
|
||||
+1
-1
@@ -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
|
||||
|
||||
-1
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user