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