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