diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt deleted file mode 100644 index bef8d4dbf17..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt +++ /dev/null @@ -1,111 +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.declarations.FirAnonymousInitializer -import org.jetbrains.kotlin.fir.declarations.FirFunction -import org.jetbrains.kotlin.fir.declarations.FirProperty -import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph -import org.jetbrains.kotlin.fir.types.ConeKotlinType - -class DummyFirDataFlowAnalyzer : FirDataFlowAnalyzer() { - companion object { - private const val DUMMY = "" - } - - override fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection? { - return null - } - - override fun enterFunction(function: FirFunction<*>) {} - - override fun exitFunction(function: FirFunction<*>): ControlFlowGraph? = null - - override fun enterBlock(block: FirBlock) {} - - override fun exitBlock(block: FirBlock) {} - - override fun enterProperty(property: FirProperty) {} - - override fun exitProperty(property: FirProperty) = ControlFlowGraph(DUMMY) - - override fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {} - - override fun exitOperatorCall(operatorCall: FirOperatorCall) {} - - override fun exitJump(jump: FirJump<*>) {} - - override fun enterWhenExpression(whenExpression: FirWhenExpression) {} - - override fun enterWhenBranchCondition(whenBranch: FirWhenBranch) {} - - override fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {} - - override fun exitWhenBranchResult(whenBranch: FirWhenBranch) {} - - override fun exitWhenExpression(whenExpression: FirWhenExpression) {} - - override fun enterWhileLoop(loop: FirLoop) {} - - override fun exitWhileLoopCondition(loop: FirLoop) {} - - override fun exitWhileLoop(loop: FirLoop) {} - - override fun enterDoWhileLoop(loop: FirLoop) {} - - override fun enterDoWhileLoopCondition(loop: FirLoop) {} - - override fun exitDoWhileLoop(loop: FirLoop) {} - - override fun enterTryExpression(tryExpression: FirTryExpression) {} - - override fun exitTryMainBlock(tryExpression: FirTryExpression) {} - - override fun enterCatchClause(catch: FirCatch) {} - - override fun exitCatchClause(catch: FirCatch) {} - - override fun enterFinallyBlock(tryExpression: FirTryExpression) {} - - override fun exitFinallyBlock(tryExpression: FirTryExpression) {} - - override fun exitTryExpression(tryExpression: FirTryExpression) {} - - override fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) {} - - override fun enterFunctionCall(functionCall: FirFunctionCall) {} - - override fun exitFunctionCall(functionCall: FirFunctionCall) {} - - override fun exitConstExpresion(constExpression: FirConstExpression<*>) {} - - override fun exitVariableDeclaration(variable: FirVariable<*>) {} - - override fun exitVariableAssignment(assignment: FirVariableAssignment) {} - - override fun exitThrowExceptionNode(throwExpression: FirThrowExpression) {} - - override fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {} - - override fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {} - - override fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {} - - override fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {} - - override fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) {} - - override fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {} - - override fun enterAnnotationCall(annotationCall: FirAnnotationCall) {} - - override fun exitAnnotationCall(annotationCall: FirAnnotationCall) {} - - override fun enterInitBlock(initBlock: FirAnonymousInitializer) {} - - override fun exitInitBlock(initBlock: FirAnonymousInitializer) {} -} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index 10945559f79..5f1800eb9f8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -5,97 +5,664 @@ package org.jetbrains.kotlin.fir.resolve.dfa +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.FirResolvedCallableReference import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph -import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents +import org.jetbrains.kotlin.fir.resolve.dfa.Condition.* +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* +import org.jetbrains.kotlin.fir.resolve.transformers.FirBodyResolveTransformer +import org.jetbrains.kotlin.fir.symbols.CallableId +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.ir.expressions.IrConstKind +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addToStdlib.safeAs -abstract class FirDataFlowAnalyzer { - abstract fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection? +class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveComponents by transformer { + companion object { + private val KOTLIN_BOOLEAN_NOT = CallableId(FqName("kotlin"), FqName("Boolean"), Name.identifier("not")) + } + + private val context: DataFlowInferenceContext get() = inferenceComponents.ctx as DataFlowInferenceContext + + private val graphBuilder = ControlFlowGraphBuilder() + private val logicSystem = LogicSystem(context) + private val variableStorage = DataFlowVariableStorage() + private val edges = mutableMapOf, Flow>().withDefault { Flow.EMPTY } + + fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection? { + val symbol: FirBasedSymbol<*> = qualifiedAccessExpression.resolvedSymbol ?: return null + val variable = variableStorage[symbol]?.real ?: return null + return graphBuilder.lastNode.flow.approvedFacts(variable)?.exactType ?: return null + } // ----------------------------------- Named function ----------------------------------- - abstract fun enterFunction(function: FirFunction<*>) - abstract fun exitFunction(function: FirFunction<*>): ControlFlowGraph? + fun enterFunction(function: FirFunction<*>) { + graphBuilder.enterFunction(function).passFlow() + + for (valueParameter in function.valueParameters) { + getRealVariable(valueParameter.symbol) + } + } + + fun exitFunction(function: FirFunction<*>): ControlFlowGraph? { + val (node, graph) = graphBuilder.exitFunction(function) + node.passFlow() + for (valueParameter in function.valueParameters) { + variableStorage.removeRealVariable(valueParameter.symbol) + } + if (graphBuilder.isTopLevel()) { + edges.clear() + variableStorage.reset() + } + return graph + } // ----------------------------------- Property ----------------------------------- - abstract fun enterProperty(property: FirProperty) - abstract fun exitProperty(property: FirProperty): ControlFlowGraph + fun enterProperty(property: FirProperty) { + graphBuilder.enterProperty(property).passFlow() + } + + fun exitProperty(property: FirProperty): ControlFlowGraph { + val (node, graph) = graphBuilder.exitProperty(property) + node.passFlow() + return graph + } // ----------------------------------- Block ----------------------------------- - abstract fun enterBlock(block: FirBlock) - abstract fun exitBlock(block: FirBlock) + fun enterBlock(block: FirBlock) { + val node = graphBuilder.enterBlock(block).passFlow(false) + + val previousNode = node.usefulPreviousNodes.singleOrNull() as? WhenBranchConditionExitNode + if (previousNode != null) { + node.flow = logicSystem.approveFactsInsideFlow(previousNode.variable, EqTrue, node.flow) + } + node.flow.freeze() + } + + fun exitBlock(block: FirBlock) { + graphBuilder.exitBlock(block).passFlow() + } // ----------------------------------- Operator call ----------------------------------- - abstract fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) - abstract fun exitOperatorCall(operatorCall: FirOperatorCall) + private fun FirExpression.getResolvedSymbol(): FirCallableSymbol<*>? { + val expression = (this as? FirWhenSubjectExpression)?.whenSubject?.whenExpression?.let { + it.subjectVariable?.symbol?.let { return it } + it.subject + } ?: this + return expression.toResolvedCallableSymbol() as? FirCallableSymbol<*> + } + + fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) { + val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).passFlow(false) + try { + if (typeOperatorCall.operation !in FirOperation.TYPES) return + val symbol: FirCallableSymbol<*> = typeOperatorCall.argument.getResolvedSymbol() ?: return + val type = typeOperatorCall.conversionTypeRef.coneTypeSafe() ?: return + val varVariable = getRealVariable(symbol) + + var flow = node.flow + when (typeOperatorCall.operation) { + FirOperation.IS, FirOperation.NOT_IS -> { + val expressionVariable = getSyntheticVariable(typeOperatorCall) + + val trueInfo = FirDataFlowInfo(setOf(type), emptySet()) + val falseInfo = FirDataFlowInfo(emptySet(), setOf(type)) + + fun chooseInfo(trueBranch: Boolean) = + if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) trueInfo else falseInfo + + flow = flow.addNotApprovedFact( + expressionVariable, + UnapprovedFirDataFlowInfo( + EqTrue, varVariable, chooseInfo(true) + ) + ) + + flow = flow.addNotApprovedFact( + expressionVariable, + UnapprovedFirDataFlowInfo( + EqFalse, varVariable, chooseInfo(false) + ) + ) + } + + FirOperation.AS -> { + flow = flow.addApprovedFact(varVariable, FirDataFlowInfo(setOf(type), emptySet())) + } + + FirOperation.SAFE_AS -> { + val expressionVariable = getSyntheticVariable(typeOperatorCall) + flow = flow.addNotApprovedFact( + expressionVariable, + UnapprovedFirDataFlowInfo( + NotEqNull, varVariable, FirDataFlowInfo(setOf(type), emptySet()) + ) + ).addNotApprovedFact( + expressionVariable, + UnapprovedFirDataFlowInfo( + EqNull, varVariable, FirDataFlowInfo(emptySet(), setOf(type)) + ) + ) + } + + else -> throw IllegalStateException() + } + + node.flow = flow + } finally { + node.flow.freeze() + } + } + + fun exitOperatorCall(operatorCall: FirOperatorCall) { + val node = graphBuilder.exitOperatorCall(operatorCall).passFlow(false) + try { + when (val operation = operatorCall.operation) { + FirOperation.EQ, FirOperation.NOT_EQ, FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> { + val leftOperand = operatorCall.arguments[0] + val rightOperand = operatorCall.arguments[1] + + val leftConst = leftOperand as? FirConstExpression<*> + val rightConst = rightOperand as? FirConstExpression<*> + + when { + leftConst?.kind == IrConstKind.Null -> processEqNull(node, rightOperand, operation) + rightConst?.kind == IrConstKind.Null -> processEqNull(node, leftOperand, operation) + leftConst != null -> processEqWithConst(node, rightOperand, leftConst, operation) + rightConst != null -> processEqWithConst(node, leftOperand, rightConst, operation) + operation != FirOperation.EQ && operation != FirOperation.IDENTITY -> return + else -> processEq(node, leftOperand, rightOperand, operation) + } + } + } + } finally { + node.flow.freeze() + } + } + + private fun processEqWithConst(node: OperatorCallNode, operand: FirExpression, const: FirConstExpression<*>, operation: FirOperation) { + val isEq = when (operation) { + FirOperation.EQ, FirOperation.IDENTITY -> true + FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> false + else -> return + } + + val expressionVariable = getVariable(node.fir) + var flow = node.flow + + // not null for comparisons with constants + getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables -> + operandVariables.forEach { operandVariable -> + flow = flow.addNotApprovedFact( + expressionVariable, UnapprovedFirDataFlowInfo( + isEq.toEqBoolean(), + operandVariable, + FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet()) + ) + ) + } + } + + // propagating facts for (... == true) and (... == false) + variableStorage[operand]?.let { operandVariable -> + if (const.kind != IrConstKind.Boolean) return@let + + val constValue = (const.value as Boolean) + val shouldInvert = isEq xor constValue + + flow.notApprovedFacts[operandVariable].forEach { info -> + flow = flow.addNotApprovedFact(expressionVariable, info.let { if (shouldInvert) it.invert() else it }) + } + } + + node.flow = flow + } + + private fun processEq(node: OperatorCallNode, leftOperand: FirExpression, rightOperand: FirExpression, operation: FirOperation) { + val leftType = leftOperand.typeRef.coneTypeSafe() ?: return + val rightType = rightOperand.typeRef.coneTypeSafe() ?: return + when { + leftType.isMarkedNullable && rightType.isMarkedNullable -> return + leftType.isMarkedNullable -> processEqNull(node, leftOperand, FirOperation.NOT_EQ) + rightType.isMarkedNullable -> processEqNull(node, rightOperand, FirOperation.NOT_EQ) + } + // TODO: process EQUALITY + } + + private fun processEqNull(node: OperatorCallNode, operand: FirExpression, operation: FirOperation) { + var flow = node.flow + val expressionVariable = getVariable(node.fir) + + variableStorage[operand]?.let { operandVariable -> + val condition = when (operation) { + FirOperation.EQ, FirOperation.IDENTITY -> EqNull + FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> NotEqNull + else -> throw IllegalArgumentException() + } + val facts = logicSystem.approveFact(operandVariable, condition, flow) + facts.forEach { (variable, info) -> + flow = flow.addNotApprovedFact( + expressionVariable, + UnapprovedFirDataFlowInfo( + EqTrue, variable, info + ) + ).addNotApprovedFact( + expressionVariable, + UnapprovedFirDataFlowInfo( + EqFalse, variable, info.invert() + ) + ) + } + node.flow = flow + return + } + + val operandVariables = getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() } ?: return + + val condition = when (operation) { + FirOperation.EQ, FirOperation.IDENTITY -> EqFalse + FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> EqTrue + else -> throw IllegalArgumentException() + } + + operandVariables.forEach { operandVariable -> + flow = flow.addNotApprovedFact( + expressionVariable, UnapprovedFirDataFlowInfo( + condition, + operandVariable, + FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet()) + ) + ) + // TODO: design do we need casts to Nothing? + /* + flow = addNotApprovedFact( + expressionVariable, UnapprovedFirDataFlowInfo( + eq(conditionValue.invert()!!), + operandVariable, + FirDataFlowInfo(setOf(session.builtinTypes.nullableNothingType.coneTypeUnsafe()), emptySet()) + ) + ) + */ + + } + node.flow = flow + } // ----------------------------------- Jump ----------------------------------- - abstract fun exitJump(jump: FirJump<*>) + fun exitJump(jump: FirJump<*>) { + graphBuilder.exitJump(jump).passFlow() + } // ----------------------------------- When ----------------------------------- - abstract fun enterWhenExpression(whenExpression: FirWhenExpression) - abstract fun enterWhenBranchCondition(whenBranch: FirWhenBranch) - abstract fun exitWhenBranchCondition(whenBranch: FirWhenBranch) - abstract fun exitWhenBranchResult(whenBranch: FirWhenBranch) - abstract fun exitWhenExpression(whenExpression: FirWhenExpression) + fun enterWhenExpression(whenExpression: FirWhenExpression) { + graphBuilder.enterWhenExpression(whenExpression).passFlow() + } + + fun enterWhenBranchCondition(whenBranch: FirWhenBranch) { + val node = graphBuilder.enterWhenBranchCondition(whenBranch).passFlow(false) + val previousNode = node.previousNodes.single() + if (previousNode is WhenBranchConditionExitNode) { + node.flow = logicSystem.approveFactsInsideFlow(previousNode.variable, EqFalse, node.flow) + } + node.flow.freeze() + } + + fun exitWhenBranchCondition(whenBranch: FirWhenBranch) { + val node = graphBuilder.exitWhenBranchCondition(whenBranch).passFlow() + + val conditionVariable = getVariable(whenBranch.condition) + node.variable = conditionVariable + } + + fun exitWhenBranchResult(whenBranch: FirWhenBranch) { + val node = graphBuilder.exitWhenBranchResult(whenBranch).passFlow(false) + val conditionVariable = getVariable(whenBranch.condition) + node.flow = node.flow.removeSyntheticVariable(conditionVariable) + } + + fun exitWhenExpression(whenExpression: FirWhenExpression) { + val node = graphBuilder.exitWhenExpression(whenExpression) + var flow = logicSystem.or(node.usefulPreviousNodes.map { it.flow }) + val subjectSymbol = whenExpression.subjectVariable?.symbol + if (subjectSymbol != null) { + variableStorage[subjectSymbol]?.let { flow = flow.removeVariable(it) } + } + node.flow = flow + } // ----------------------------------- While Loop ----------------------------------- - abstract fun enterWhileLoop(loop: FirLoop) - abstract fun exitWhileLoopCondition(loop: FirLoop) - abstract fun exitWhileLoop(loop: FirLoop) + fun enterWhileLoop(loop: FirLoop) { + val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop) + loopEnterNode.passFlow() + loopConditionEnterNode.passFlow() + } + + fun exitWhileLoopCondition(loop: FirLoop) { + graphBuilder.exitWhileLoopCondition(loop).passFlow() + } + + fun exitWhileLoop(loop: FirLoop) { + graphBuilder.exitWhileLoop(loop).also { (blockExitNode, exitNode) -> + blockExitNode.passFlow() + exitNode.passFlow() + } + } // ----------------------------------- Do while Loop ----------------------------------- - abstract fun enterDoWhileLoop(loop: FirLoop) - abstract fun enterDoWhileLoopCondition(loop: FirLoop) - abstract fun exitDoWhileLoop(loop: FirLoop) + fun enterDoWhileLoop(loop: FirLoop) { + graphBuilder.enterDoWhileLoop(loop).passFlow() + } + + fun enterDoWhileLoopCondition(loop: FirLoop) { + val (loopBlockExitNode, loopConditionEnterNode) = graphBuilder.enterDoWhileLoopCondition(loop) + loopBlockExitNode.passFlow() + loopConditionEnterNode.passFlow() + } + + fun exitDoWhileLoop(loop: FirLoop) { + graphBuilder.exitDoWhileLoop(loop).passFlow() + } // ----------------------------------- Try-catch-finally ----------------------------------- - abstract fun enterTryExpression(tryExpression: FirTryExpression) - abstract fun exitTryMainBlock(tryExpression: FirTryExpression) - abstract fun enterCatchClause(catch: FirCatch) - abstract fun exitCatchClause(catch: FirCatch) - abstract fun enterFinallyBlock(tryExpression: FirTryExpression) - abstract fun exitFinallyBlock(tryExpression: FirTryExpression) - abstract fun exitTryExpression(tryExpression: FirTryExpression) + fun enterTryExpression(tryExpression: FirTryExpression) { + graphBuilder.enterTryExpression(tryExpression).passFlow() + } + + fun exitTryMainBlock(tryExpression: FirTryExpression) { + graphBuilder.exitTryMainBlock(tryExpression).passFlow() + } + + fun enterCatchClause(catch: FirCatch) { + graphBuilder.enterCatchClause(catch).passFlow() + } + + fun exitCatchClause(catch: FirCatch) { + graphBuilder.exitCatchClause(catch).passFlow() + } + + fun enterFinallyBlock(tryExpression: FirTryExpression) { + // TODO + graphBuilder.enterFinallyBlock(tryExpression).passFlow() + } + + fun exitFinallyBlock(tryExpression: FirTryExpression) { + // TODO + graphBuilder.exitFinallyBlock(tryExpression).passFlow() + } + + fun exitTryExpression(tryExpression: FirTryExpression) { + // TODO + graphBuilder.exitTryExpression(tryExpression).passFlow() + } // ----------------------------------- Resolvable call ----------------------------------- - abstract fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) - abstract fun enterFunctionCall(functionCall: FirFunctionCall) - abstract fun exitFunctionCall(functionCall: FirFunctionCall) - abstract fun exitConstExpresion(constExpression: FirConstExpression<*>) - abstract fun exitVariableDeclaration(variable: FirVariable<*>) - abstract fun exitVariableAssignment(assignment: FirVariableAssignment) - abstract fun exitThrowExceptionNode(throwExpression: FirThrowExpression) + fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) { + graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).passFlow() + } + + fun enterFunctionCall(functionCall: FirFunctionCall) { + // TODO: add processing in-place lambdas + } + + fun exitFunctionCall(functionCall: FirFunctionCall) { + val node = graphBuilder.exitFunctionCall(functionCall).passFlow(false) + if (functionCall.isBooleanNot()) { + exitBooleanNot(functionCall, node) + return + } + } + + private val FirElement.resolvedSymbol: FirBasedSymbol<*>? get() = this.safeAs()?.calleeReference.safeAs()?.coneSymbol as? FirBasedSymbol<*> + + private fun FirFunctionCall.isBooleanNot(): Boolean { + val symbol = calleeReference.safeAs()?.coneSymbol as? FirNamedFunctionSymbol ?: return false + return symbol.callableId == KOTLIN_BOOLEAN_NOT + } + + fun exitConstExpresion(constExpression: FirConstExpression<*>) { + graphBuilder.exitConstExpresion(constExpression).passFlow() + } + + fun exitVariableDeclaration(variable: FirVariable<*>) { + val node = graphBuilder.exitVariableDeclaration(variable).passFlow(false) + try { + val initializer = variable.initializer ?: return + + /* + * That part is needed for cases like that: + * + * val b = x is String + * ... + * if (b) { + * x.length + * } + */ + variableStorage[initializer]?.let { initializerVariable -> + assert(initializerVariable.isSynthetic) + val realVariable = getRealVariable(variable.symbol) + node.flow = node.flow.copyNotApprovedFacts(initializerVariable, realVariable) + } + + initializer.resolvedSymbol?.let { initializerSymbol: FirBasedSymbol<*> -> + val rhsVariable = variableStorage[initializerSymbol]?.takeIf { !it.isSynthetic } ?: return + variableStorage.createAliasVariable(variable.symbol, rhsVariable) + } + + } finally { + node.flow.freeze() + } + } + + fun exitVariableAssignment(assignment: FirVariableAssignment) { + graphBuilder.exitVariableAssignment(assignment).passFlow() + val lhsVariable = variableStorage[assignment.resolvedSymbol ?: return] ?: return + val rhsVariable = variableStorage[assignment.rValue.resolvedSymbol ?: return]?.takeIf { !it.isSynthetic } ?: return + variableStorage.rebindAliasVariable(lhsVariable, rhsVariable) + } + + fun exitThrowExceptionNode(throwExpression: FirThrowExpression) { + graphBuilder.exitThrowExceptionNode(throwExpression).passFlow() + } // ----------------------------------- Boolean operators ----------------------------------- - abstract fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) - abstract fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) - abstract fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) - abstract fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) - abstract fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) - abstract fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) + fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) { + graphBuilder.enterBinaryAnd(binaryLogicExpression).passFlow() + } + + fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) { + val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression) + leftNode.passFlow() + rightNode.passFlow(false) + val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir) + rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqTrue, rightNode.flow).also { it.freeze() } + } + + fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) { + val node = graphBuilder.exitBinaryAnd(binaryLogicExpression).passFlow(false) + val (leftVariable, rightVariable) = binaryLogicExpression.getVariables() + + val flowFromLeft = node.leftOperandNode.flow + val flowFromRight = node.rightOperandNode.flow + val flow = node.flow + + val andVariable = getVariable(binaryLogicExpression) + + val leftIsTrue = approveFact(leftVariable, EqTrue, flowFromRight) + val leftIsFalse = approveFact(leftVariable, EqFalse, flowFromRight) + val rightIsTrue = approveFact(rightVariable, EqTrue, flowFromRight) ?: mutableMapOf() + val rightIsFalse = approveFact(rightVariable, EqFalse, flowFromRight) + + flowFromRight.approvedFacts.forEach { (variable, info) -> + val actualInfo = flowFromLeft.approvedFacts[variable]?.let { info - it } ?: info + if (actualInfo.isNotEmpty) rightIsTrue.compute(variable) { _, existingInfo -> info + existingInfo } + } + + logicSystem.andForVerifiedFacts(leftIsTrue, rightIsTrue)?.let { + for ((variable, info) in it) { + flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqTrue, variable, info)) + } + } + logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse)?.let { + for ((variable, info) in it) { + flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info)) + } + } + node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable).also { it.freeze() } + } + + fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) { + graphBuilder.enterBinaryOr(binaryLogicExpression) + } + + fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) { + val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression) + leftNode.passFlow() + rightNode.passFlow(false) + val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir) + rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqFalse, rightNode.flow).also { it.freeze() } + } + + fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) { + val node = graphBuilder.exitBinaryOr(binaryLogicExpression).passFlow(false) + val (leftVariable, rightVariable) = binaryLogicExpression.getVariables() + + val flowFromLeft = node.leftOperandNode.flow + val flowFromRight = node.rightOperandNode.flow + val flow = node.flow + + val orVariable = getVariable(binaryLogicExpression) + + val leftIsTrue = approveFact(leftVariable, EqTrue, flowFromLeft) + val leftIsFalse = approveFact(leftVariable, EqFalse, flowFromLeft) + val rightIsTrue = approveFact(rightVariable, EqTrue, flowFromRight) + val rightIsFalse = approveFact(rightVariable, EqFalse, flowFromRight) + + logicSystem.orForVerifiedFacts(leftIsTrue, rightIsTrue)?.let { + for ((variable, info) in it) { + flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqTrue, variable, info)) + } + } + logicSystem.andForVerifiedFacts(leftIsFalse, rightIsFalse)?.let { + for ((variable, info) in it) { + flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info)) + } + } + node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable).also { it.freeze() } + } + + private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) { + val booleanExpressionVariable = getVariable(node.previousNodes.first().fir) + val variable = getVariable(functionCall) + node.flow = node.flow.copyNotApprovedFacts(booleanExpressionVariable, variable) { it.invert() }.also { it.freeze() } + } // ----------------------------------- Annotations ----------------------------------- - abstract fun enterAnnotationCall(annotationCall: FirAnnotationCall) - abstract fun exitAnnotationCall(annotationCall: FirAnnotationCall) + fun enterAnnotationCall(annotationCall: FirAnnotationCall) { + graphBuilder.enterAnnotationCall(annotationCall).passFlow() + } + + fun exitAnnotationCall(annotationCall: FirAnnotationCall) { + graphBuilder.exitAnnotationCall(annotationCall).passFlow() + } // ----------------------------------- Init block ----------------------------------- - abstract fun enterInitBlock(initBlock: FirAnonymousInitializer) - abstract fun exitInitBlock(initBlock: FirAnonymousInitializer) -} + fun enterInitBlock(initBlock: FirAnonymousInitializer) { + graphBuilder.enterInitBlock(initBlock) + } + fun exitInitBlock(initBlock: FirAnonymousInitializer) { + graphBuilder.exitInitBlock(initBlock).passFlow() + } + + // ------------------------------------------------------------------------------------------------------------------------- + + private fun approveFact(variable: DataFlowVariable, condition: Condition, flow: Flow): MutableMap? = + logicSystem.approveFact(variable, condition, flow) + + private fun FirBinaryLogicExpression.getVariables(): Pair = + getVariable(leftOperand) to getVariable(rightOperand) + + private var CFGNode<*>.flow: Flow + get() = edges.getValue(this) + set(value) { + edges[this] = value + } + + private fun > T.passFlow(shouldFreeze: Boolean = true): T = this.also { node -> + node.flow = logicSystem.or(node.usefulPreviousNodes.map { it.flow }).also { + if (shouldFreeze) it.freeze() + } + } + + private fun getSyntheticVariable(fir: FirElement): DataFlowVariable = variableStorage.getOrCreateNewSyntheticVariable(fir) + private fun getRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable = variableStorage.getOrCreateNewRealVariable(symbol).real + + private fun getVariable(fir: FirElement): DataFlowVariable { + val symbol = fir.resolvedSymbol + return if (symbol == null) + getSyntheticVariable(fir) + else + getRealVariable(symbol) + } + + private fun getRealVariablesForSafeCallChain(call: FirExpression): Collection { + val result = mutableListOf() + + fun collect(call: FirExpression) { + when (call) { + is FirQualifiedAccess -> { + if (call.safe) { + val explicitReceiver = call.explicitReceiver + require(explicitReceiver != null) + collect(explicitReceiver) + } + ((call.calleeReference as? FirResolvedCallableReference)?.coneSymbol)?.let { symbol -> + if (symbol is FirVariableSymbol<*> || symbol is FirPropertySymbol) { + result += getRealVariable(symbol as FirBasedSymbol<*>) + } + } + } + is FirWhenSubjectExpression -> { + call.whenSubject.whenExpression.subjectVariable?.let { result += getRealVariable(it.symbol) } + call.whenSubject.whenExpression.subject?.let { collect(it) } + } + } + } + + collect(call) + return result + } + + private fun Flow.removeVariable(variable: DataFlowVariable): Flow { + variableStorage.removeVariable(variable) + return removeVariableFromFlow(variable) + } + + private fun Flow.removeSyntheticVariable(variable: DataFlowVariable): Flow { + if (!variable.isSynthetic) return this + return removeVariable(variable) + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt deleted file mode 100644 index 7678fb57225..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt +++ /dev/null @@ -1,667 +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.FirElement -import org.jetbrains.kotlin.fir.FirResolvedCallableReference -import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer -import org.jetbrains.kotlin.fir.declarations.FirFunction -import org.jetbrains.kotlin.fir.declarations.FirProperty -import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents -import org.jetbrains.kotlin.fir.resolve.dfa.Condition.* -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* -import org.jetbrains.kotlin.fir.resolve.transformers.FirBodyResolveTransformer -import org.jetbrains.kotlin.fir.symbols.CallableId -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol -import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.ir.expressions.IrConstKind -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.utils.addToStdlib.safeAs - -class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataFlowAnalyzer(), BodyResolveComponents by transformer { - companion object { - private val KOTLIN_BOOLEAN_NOT = CallableId(FqName("kotlin"), FqName("Boolean"), Name.identifier("not")) - } - - private val context: DataFlowInferenceContext get() = inferenceComponents.ctx as DataFlowInferenceContext - - private val graphBuilder = ControlFlowGraphBuilder() - private val logicSystem = LogicSystem(context) - private val variableStorage = DataFlowVariableStorage() - private val edges = mutableMapOf, Flow>().withDefault { Flow.EMPTY } - - override fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection? { - val symbol: FirBasedSymbol<*> = qualifiedAccessExpression.resolvedSymbol ?: return null - val variable = variableStorage[symbol]?.real ?: return null - return graphBuilder.lastNode.flow.approvedFacts(variable)?.exactType ?: return null - } - - // ----------------------------------- Named function ----------------------------------- - - override fun enterFunction(function: FirFunction<*>) { - graphBuilder.enterFunction(function).passFlow() - - for (valueParameter in function.valueParameters) { - getRealVariable(valueParameter.symbol) - } - } - - override fun exitFunction(function: FirFunction<*>): ControlFlowGraph? { - val (node, graph) = graphBuilder.exitFunction(function) - node.passFlow() - for (valueParameter in function.valueParameters) { - variableStorage.removeRealVariable(valueParameter.symbol) - } - if (graphBuilder.isTopLevel()) { - edges.clear() - variableStorage.reset() - } - return graph - } - - // ----------------------------------- Property ----------------------------------- - - override fun enterProperty(property: FirProperty) { - graphBuilder.enterProperty(property).passFlow() - } - - override fun exitProperty(property: FirProperty): ControlFlowGraph { - val (node, graph) = graphBuilder.exitProperty(property) - node.passFlow() - return graph - } - - // ----------------------------------- Block ----------------------------------- - - override fun enterBlock(block: FirBlock) { - val node = graphBuilder.enterBlock(block).passFlow(false) - - val previousNode = node.usefulPreviousNodes.singleOrNull() as? WhenBranchConditionExitNode - if (previousNode != null) { - node.flow = logicSystem.approveFactsInsideFlow(previousNode.variable, EqTrue, node.flow) - } - node.flow.freeze() - } - - override fun exitBlock(block: FirBlock) { - graphBuilder.exitBlock(block).passFlow() - } - - // ----------------------------------- Operator call ----------------------------------- - - private fun FirExpression.getResolvedSymbol(): FirCallableSymbol<*>? { - val expression = (this as? FirWhenSubjectExpression)?.whenSubject?.whenExpression?.let { - it.subjectVariable?.symbol?.let { return it } - it.subject - } ?: this - return expression.toResolvedCallableSymbol() as? FirCallableSymbol<*> - } - - override fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) { - val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).passFlow(false) - try { - if (typeOperatorCall.operation !in FirOperation.TYPES) return - val symbol: FirCallableSymbol<*> = typeOperatorCall.argument.getResolvedSymbol() ?: return - val type = typeOperatorCall.conversionTypeRef.coneTypeSafe() ?: return - val varVariable = getRealVariable(symbol) - - var flow = node.flow - when (typeOperatorCall.operation) { - FirOperation.IS, FirOperation.NOT_IS -> { - val expressionVariable = getSyntheticVariable(typeOperatorCall) - - val trueInfo = FirDataFlowInfo(setOf(type), emptySet()) - val falseInfo = FirDataFlowInfo(emptySet(), setOf(type)) - - fun chooseInfo(trueBranch: Boolean) = if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) trueInfo else falseInfo - - flow = flow.addNotApprovedFact( - expressionVariable, - UnapprovedFirDataFlowInfo( - EqTrue, varVariable, chooseInfo(true) - ) - ) - - flow = flow.addNotApprovedFact( - expressionVariable, - UnapprovedFirDataFlowInfo( - EqFalse, varVariable, chooseInfo(false) - ) - ) - } - - FirOperation.AS -> { - flow = flow.addApprovedFact(varVariable, FirDataFlowInfo(setOf(type), emptySet())) - } - - FirOperation.SAFE_AS -> { - val expressionVariable = getSyntheticVariable(typeOperatorCall) - flow = flow.addNotApprovedFact( - expressionVariable, - UnapprovedFirDataFlowInfo( - NotEqNull, varVariable, FirDataFlowInfo(setOf(type), emptySet()) - ) - ).addNotApprovedFact( - expressionVariable, - UnapprovedFirDataFlowInfo( - EqNull, varVariable, FirDataFlowInfo(emptySet(), setOf(type)) - ) - ) - } - - else -> throw IllegalStateException() - } - - node.flow = flow - } finally { - node.flow.freeze() - } - } - - override fun exitOperatorCall(operatorCall: FirOperatorCall) { - val node = graphBuilder.exitOperatorCall(operatorCall).passFlow(false) - try { - when (val operation = operatorCall.operation) { - FirOperation.EQ, FirOperation.NOT_EQ, FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> { - val leftOperand = operatorCall.arguments[0] - val rightOperand = operatorCall.arguments[1] - - val leftConst = leftOperand as? FirConstExpression<*> - val rightConst = rightOperand as? FirConstExpression<*> - - when { - leftConst?.kind == IrConstKind.Null -> processEqNull(node, rightOperand, operation) - rightConst?.kind == IrConstKind.Null -> processEqNull(node, leftOperand, operation) - leftConst != null -> processEqWithConst(node, rightOperand, leftConst, operation) - rightConst != null -> processEqWithConst(node, leftOperand, rightConst, operation) - operation != FirOperation.EQ && operation != FirOperation.IDENTITY -> return - else -> processEq(node, leftOperand, rightOperand, operation) - } - } - } - } finally { - node.flow.freeze() - } - } - - private fun processEqWithConst(node: OperatorCallNode, operand: FirExpression, const: FirConstExpression<*>, operation: FirOperation) { - val isEq = when (operation) { - FirOperation.EQ, FirOperation.IDENTITY -> true - FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> false - else -> return - } - - val expressionVariable = getVariable(node.fir) - var flow = node.flow - - // not null for comparisons with constants - getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables -> - operandVariables.forEach { operandVariable -> - flow = flow.addNotApprovedFact( - expressionVariable, UnapprovedFirDataFlowInfo( - isEq.toEqBoolean(), - operandVariable, - FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet()) - ) - ) - } - } - - // propagating facts for (... == true) and (... == false) - variableStorage[operand]?.let { operandVariable -> - if (const.kind != IrConstKind.Boolean) return@let - - val constValue = (const.value as Boolean) - val shouldInvert = isEq xor constValue - - flow.notApprovedFacts[operandVariable].forEach { info -> - flow = flow.addNotApprovedFact(expressionVariable, info.let { if (shouldInvert) it.invert() else it }) - } - } - - node.flow = flow - } - - private fun processEq(node: OperatorCallNode, leftOperand: FirExpression, rightOperand: FirExpression, operation: FirOperation) { - val leftType = leftOperand.typeRef.coneTypeSafe() ?: return - val rightType = rightOperand.typeRef.coneTypeSafe() ?: return - when { - leftType.isMarkedNullable && rightType.isMarkedNullable -> return - leftType.isMarkedNullable -> processEqNull(node, leftOperand, FirOperation.NOT_EQ) - rightType.isMarkedNullable -> processEqNull(node, rightOperand, FirOperation.NOT_EQ) - } - // TODO: process EQUALITY - } - - private fun processEqNull(node: OperatorCallNode, operand: FirExpression, operation: FirOperation) { - var flow = node.flow - val expressionVariable = getVariable(node.fir) - - variableStorage[operand]?.let { operandVariable -> - val condition = when (operation) { - FirOperation.EQ, FirOperation.IDENTITY -> EqNull - FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> NotEqNull - else -> throw IllegalArgumentException() - } - val facts = logicSystem.approveFact(operandVariable, condition, flow) - facts.forEach { (variable, info) -> - flow = flow.addNotApprovedFact( - expressionVariable, - UnapprovedFirDataFlowInfo( - EqTrue, variable, info - ) - ).addNotApprovedFact( - expressionVariable, - UnapprovedFirDataFlowInfo( - EqFalse, variable, info.invert() - ) - ) - } - node.flow = flow - return - } - - val operandVariables = getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() } ?: return - - val condition = when (operation) { - FirOperation.EQ, FirOperation.IDENTITY -> EqFalse - FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> EqTrue - else -> throw IllegalArgumentException() - } - - operandVariables.forEach { operandVariable -> - flow = flow.addNotApprovedFact( - expressionVariable, UnapprovedFirDataFlowInfo( - condition, - operandVariable, - FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet()) - ) - ) - // TODO: design do we need casts to Nothing? - /* - flow = addNotApprovedFact( - expressionVariable, UnapprovedFirDataFlowInfo( - eq(conditionValue.invert()!!), - operandVariable, - FirDataFlowInfo(setOf(session.builtinTypes.nullableNothingType.coneTypeUnsafe()), emptySet()) - ) - ) - */ - - } - node.flow = flow - } - - // ----------------------------------- Jump ----------------------------------- - - override fun exitJump(jump: FirJump<*>) { - graphBuilder.exitJump(jump).passFlow() - } - - // ----------------------------------- When ----------------------------------- - - override fun enterWhenExpression(whenExpression: FirWhenExpression) { - graphBuilder.enterWhenExpression(whenExpression).passFlow() - } - - override fun enterWhenBranchCondition(whenBranch: FirWhenBranch) { - val node = graphBuilder.enterWhenBranchCondition(whenBranch).passFlow(false) - val previousNode = node.previousNodes.single() - if (previousNode is WhenBranchConditionExitNode) { - node.flow = logicSystem.approveFactsInsideFlow(previousNode.variable, EqFalse, node.flow) - } - node.flow.freeze() - } - - override fun exitWhenBranchCondition(whenBranch: FirWhenBranch) { - val node = graphBuilder.exitWhenBranchCondition(whenBranch).passFlow() - - val conditionVariable = getVariable(whenBranch.condition) - node.variable = conditionVariable - } - - override fun exitWhenBranchResult(whenBranch: FirWhenBranch) { - val node = graphBuilder.exitWhenBranchResult(whenBranch).passFlow(false) - val conditionVariable = getVariable(whenBranch.condition) - node.flow = node.flow.removeSyntheticVariable(conditionVariable) - } - - override fun exitWhenExpression(whenExpression: FirWhenExpression) { - val node = graphBuilder.exitWhenExpression(whenExpression) - var flow = logicSystem.or(node.usefulPreviousNodes.map { it.flow }) - val subjectSymbol = whenExpression.subjectVariable?.symbol - if (subjectSymbol != null) { - variableStorage[subjectSymbol]?.let { flow = flow.removeVariable(it) } - } - node.flow = flow - } - - // ----------------------------------- While Loop ----------------------------------- - - override fun enterWhileLoop(loop: FirLoop) { - val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop) - loopEnterNode.passFlow() - loopConditionEnterNode.passFlow() - } - - override fun exitWhileLoopCondition(loop: FirLoop) { - graphBuilder.exitWhileLoopCondition(loop).passFlow() - } - - override fun exitWhileLoop(loop: FirLoop) { - graphBuilder.exitWhileLoop(loop).also { (blockExitNode, exitNode) -> - blockExitNode.passFlow() - exitNode.passFlow() - } - } - - // ----------------------------------- Do while Loop ----------------------------------- - - override fun enterDoWhileLoop(loop: FirLoop) { - graphBuilder.enterDoWhileLoop(loop).passFlow() - } - - override fun enterDoWhileLoopCondition(loop: FirLoop) { - val (loopBlockExitNode, loopConditionEnterNode) = graphBuilder.enterDoWhileLoopCondition(loop) - loopBlockExitNode.passFlow() - loopConditionEnterNode.passFlow() - } - - override fun exitDoWhileLoop(loop: FirLoop) { - graphBuilder.exitDoWhileLoop(loop).passFlow() - } - - // ----------------------------------- Try-catch-finally ----------------------------------- - - override fun enterTryExpression(tryExpression: FirTryExpression) { - graphBuilder.enterTryExpression(tryExpression).passFlow() - } - - override fun exitTryMainBlock(tryExpression: FirTryExpression) { - graphBuilder.exitTryMainBlock(tryExpression).passFlow() - } - - override fun enterCatchClause(catch: FirCatch) { - graphBuilder.enterCatchClause(catch).passFlow() - } - - override fun exitCatchClause(catch: FirCatch) { - graphBuilder.exitCatchClause(catch).passFlow() - } - - override fun enterFinallyBlock(tryExpression: FirTryExpression) { - // TODO - graphBuilder.enterFinallyBlock(tryExpression).passFlow() - } - - override fun exitFinallyBlock(tryExpression: FirTryExpression) { - // TODO - graphBuilder.exitFinallyBlock(tryExpression).passFlow() - } - - override fun exitTryExpression(tryExpression: FirTryExpression) { - // TODO - graphBuilder.exitTryExpression(tryExpression).passFlow() - } - - // ----------------------------------- Resolvable call ----------------------------------- - - override fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) { - graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).passFlow() - } - - override fun enterFunctionCall(functionCall: FirFunctionCall) { - // TODO: add processing in-place lambdas - } - - override fun exitFunctionCall(functionCall: FirFunctionCall) { - val node = graphBuilder.exitFunctionCall(functionCall).passFlow(false) - if (functionCall.isBooleanNot()) { - exitBooleanNot(functionCall, node) - return - } - } - - private val FirElement.resolvedSymbol: FirBasedSymbol<*>? get() = this.safeAs()?.calleeReference.safeAs()?.coneSymbol as? FirBasedSymbol<*> - - private fun FirFunctionCall.isBooleanNot(): Boolean { - val symbol = calleeReference.safeAs()?.coneSymbol as? FirNamedFunctionSymbol ?: return false - return symbol.callableId == KOTLIN_BOOLEAN_NOT - } - - override fun exitConstExpresion(constExpression: FirConstExpression<*>) { - graphBuilder.exitConstExpresion(constExpression).passFlow() - } - - override fun exitVariableDeclaration(variable: FirVariable<*>) { - val node = graphBuilder.exitVariableDeclaration(variable).passFlow(false) - try { - val initializer = variable.initializer ?: return - - /* - * That part is needed for cases like that: - * - * val b = x is String - * ... - * if (b) { - * x.length - * } - */ - variableStorage[initializer]?.let { initializerVariable -> - assert(initializerVariable.isSynthetic) - val realVariable = getRealVariable(variable.symbol) - node.flow = node.flow.copyNotApprovedFacts(initializerVariable, realVariable) - } - - initializer.resolvedSymbol?.let { initializerSymbol: FirBasedSymbol<*> -> - val rhsVariable = variableStorage[initializerSymbol]?.takeIf { !it.isSynthetic } ?: return - variableStorage.createAliasVariable(variable.symbol, rhsVariable) - } - - } finally { - node.flow.freeze() - } - } - - override fun exitVariableAssignment(assignment: FirVariableAssignment) { - graphBuilder.exitVariableAssignment(assignment).passFlow() - val lhsVariable = variableStorage[assignment.resolvedSymbol ?: return] ?: return - val rhsVariable = variableStorage[assignment.rValue.resolvedSymbol ?: return]?.takeIf { !it.isSynthetic } ?: return - variableStorage.rebindAliasVariable(lhsVariable, rhsVariable) - } - - override fun exitThrowExceptionNode(throwExpression: FirThrowExpression) { - graphBuilder.exitThrowExceptionNode(throwExpression).passFlow() - } - - // ----------------------------------- Boolean operators ----------------------------------- - - override fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) { - graphBuilder.enterBinaryAnd(binaryLogicExpression).passFlow() - } - - override fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) { - val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression) - leftNode.passFlow() - rightNode.passFlow(false) - val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir) - rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqTrue, rightNode.flow).also { it.freeze() } - } - - override fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) { - val node = graphBuilder.exitBinaryAnd(binaryLogicExpression).passFlow(false) - val (leftVariable, rightVariable) = binaryLogicExpression.getVariables() - - val flowFromLeft = node.leftOperandNode.flow - val flowFromRight = node.rightOperandNode.flow - val flow = node.flow - - val andVariable = getVariable(binaryLogicExpression) - - val leftIsTrue = approveFact(leftVariable, EqTrue, flowFromRight) - val leftIsFalse = approveFact(leftVariable, EqFalse, flowFromRight) - val rightIsTrue = approveFact(rightVariable, EqTrue, flowFromRight) ?: mutableMapOf() - val rightIsFalse = approveFact(rightVariable, EqFalse, flowFromRight) - - flowFromRight.approvedFacts.forEach { (variable, info) -> - val actualInfo = flowFromLeft.approvedFacts[variable]?.let { info - it } ?: info - if (actualInfo.isNotEmpty) rightIsTrue.compute(variable) { _, existingInfo -> info + existingInfo } - } - - logicSystem.andForVerifiedFacts(leftIsTrue, rightIsTrue)?.let { - for ((variable, info) in it) { - flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqTrue, variable, info)) - } - } - logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse)?.let { - for ((variable, info) in it) { - flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info)) - } - } - node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable).also { it.freeze() } - } - - override fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) { - graphBuilder.enterBinaryOr(binaryLogicExpression) - } - - override fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) { - val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression) - leftNode.passFlow() - rightNode.passFlow(false) - val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir) - rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqFalse, rightNode.flow).also { it.freeze() } - } - - override fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) { - val node = graphBuilder.exitBinaryOr(binaryLogicExpression).passFlow(false) - val (leftVariable, rightVariable) = binaryLogicExpression.getVariables() - - val flowFromLeft = node.leftOperandNode.flow - val flowFromRight = node.rightOperandNode.flow - val flow = node.flow - - val orVariable = getVariable(binaryLogicExpression) - - val leftIsTrue = approveFact(leftVariable, EqTrue, flowFromLeft) - val leftIsFalse = approveFact(leftVariable, EqFalse, flowFromLeft) - val rightIsTrue = approveFact(rightVariable, EqTrue, flowFromRight) - val rightIsFalse = approveFact(rightVariable, EqFalse, flowFromRight) - - logicSystem.orForVerifiedFacts(leftIsTrue, rightIsTrue)?.let { - for ((variable, info) in it) { - flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqTrue, variable, info)) - } - } - logicSystem.andForVerifiedFacts(leftIsFalse, rightIsFalse)?.let { - for ((variable, info) in it) { - flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info)) - } - } - node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable).also { it.freeze() } - } - - private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) { - val booleanExpressionVariable = getVariable(node.previousNodes.first().fir) - val variable = getVariable(functionCall) - node.flow = node.flow.copyNotApprovedFacts(booleanExpressionVariable, variable) { it.invert() }.also { it.freeze() } - } - - // ----------------------------------- Annotations ----------------------------------- - - override fun enterAnnotationCall(annotationCall: FirAnnotationCall) { - graphBuilder.enterAnnotationCall(annotationCall).passFlow() - } - - override fun exitAnnotationCall(annotationCall: FirAnnotationCall) { - graphBuilder.exitAnnotationCall(annotationCall).passFlow() - } - - // ----------------------------------- Init block ----------------------------------- - - override fun enterInitBlock(initBlock: FirAnonymousInitializer) { - graphBuilder.enterInitBlock(initBlock) - } - - override fun exitInitBlock(initBlock: FirAnonymousInitializer) { - graphBuilder.exitInitBlock(initBlock).passFlow() - } - - // ------------------------------------------------------------------------------------------------------------------------- - - private fun approveFact(variable: DataFlowVariable, condition: Condition, flow: Flow): MutableMap? = - logicSystem.approveFact(variable, condition, flow) - - private fun FirBinaryLogicExpression.getVariables(): Pair = - getVariable(leftOperand) to getVariable(rightOperand) - - private var CFGNode<*>.flow: Flow - get() = edges.getValue(this) - set(value) { - edges[this] = value - } - - private fun > T.passFlow(shouldFreeze: Boolean = true): T = this.also { node -> - node.flow = logicSystem.or(node.usefulPreviousNodes.map { it.flow }).also { - if (shouldFreeze) it.freeze() - } - } - - private fun getSyntheticVariable(fir: FirElement): DataFlowVariable = variableStorage.getOrCreateNewSyntheticVariable(fir) - private fun getRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable = variableStorage.getOrCreateNewRealVariable(symbol).real - - private fun getVariable(fir: FirElement): DataFlowVariable { - val symbol = fir.resolvedSymbol - return if (symbol == null) - getSyntheticVariable(fir) - else - getRealVariable(symbol) - } - - private fun getRealVariablesForSafeCallChain(call: FirExpression): Collection { - val result = mutableListOf() - - fun collect(call: FirExpression) { - when (call) { - is FirQualifiedAccess -> { - if (call.safe) { - val explicitReceiver = call.explicitReceiver - require(explicitReceiver != null) - collect(explicitReceiver) - } - ((call.calleeReference as? FirResolvedCallableReference)?.coneSymbol)?.let { symbol -> - if (symbol is FirVariableSymbol<*> || symbol is FirPropertySymbol) { - result += getRealVariable(symbol as FirBasedSymbol<*>) - } - } - } - is FirWhenSubjectExpression -> { - call.whenSubject.whenExpression.subjectVariable?.let { result += getRealVariable(it.symbol) } - call.whenSubject.whenExpression.subject?.let { collect(it) } - } - } - } - - collect(call) - return result - } - - private fun Flow.removeVariable(variable: DataFlowVariable): Flow { - variableStorage.removeVariable(variable) - return removeVariableFromFlow(variable) - } - - private fun Flow.removeSyntheticVariable(variable: DataFlowVariable): Flow { - if (!variable.isSynthetic) return this - return removeVariable(variable) - } -} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt index a6ac18012b1..def311324ff 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt @@ -18,7 +18,6 @@ import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.dfa.DataFlowInferenceContext import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer -import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzerImpl import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.addImportingScopes @@ -86,7 +85,7 @@ open class FirBodyResolveTransformer( ) private val syntheticCallGenerator: FirSyntheticCallGenerator = FirSyntheticCallGenerator(this) - private val dataFlowAnalyzer: FirDataFlowAnalyzer = FirDataFlowAnalyzerImpl(this) + private val dataFlowAnalyzer: FirDataFlowAnalyzer = FirDataFlowAnalyzer(this) override val AbstractFirBasedSymbol.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner get() {