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 9657fe6d4b9..201970d99e7 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 @@ -631,21 +631,21 @@ abstract class FirDataFlowAnalyzer( fun exitWhileLoop(loop: FirLoop) { val (conditionEnterNode, blockExitNode, exitNode) = graphBuilder.exitWhileLoop(loop) blockExitNode.mergeIncomingFlow() - exitNode.mergeIncomingFlow { _, flow -> - processWhileLoopExit(flow, exitNode, conditionEnterNode) + exitNode.mergeIncomingFlow { path, flow -> + processWhileLoopExit(path, flow, exitNode, conditionEnterNode) processLoopExit(flow, exitNode, exitNode.firstPreviousNode as LoopConditionExitNode) } } - private fun processWhileLoopExit(flow: MutableFlow, node: LoopExitNode, conditionEnterNode: LoopConditionEnterNode) { + private fun processWhileLoopExit(path: FlowPath, flow: MutableFlow, node: LoopExitNode, conditionEnterNode: LoopConditionEnterNode) { val possiblyChangedVariables = exitRepeatableStatement(node.fir) if (possiblyChangedVariables.isNullOrEmpty()) return // While analyzing the loop we might have added some backwards jumps to `conditionEnterNode` which weren't // there at the time its flow was computed - which is why we erased all information about `possiblyChangedVariables` // from it. Now that we have those edges, we can restore type information for the code after the loop. - val conditionEnterFlow = conditionEnterNode.flow - val loopEnterAndContinueFlows = conditionEnterNode.livePreviousFlows - val conditionExitAndBreakFlows = node.livePreviousFlows + val conditionEnterFlow = conditionEnterNode.getFlow(path) + val loopEnterAndContinueFlows = conditionEnterNode.previousLiveNodes.map { it.getFlow(path) }.toList() + val conditionExitAndBreakFlows = node.previousLiveNodes.map { it.getFlow(path) }.toList() possiblyChangedVariables.forEach { variable -> // The statement about `variable` in `conditionEnterFlow` should be empty, so to obtain the new statement // we can simply add the now-known input to whatever was inferred from nothing so long as the value is the same. @@ -769,7 +769,7 @@ abstract class FirDataFlowAnalyzer( fun exitSafeCall(safeCall: FirSafeCallExpression) { val node = graphBuilder.exitSafeCall() - node.mergeIncomingFlow { _, flow -> + node.mergeIncomingFlow { path, flow -> // If there is only 1 previous node, then this is LHS of `a?.b ?: c`; then the null-case // edge from `a` goes directly to `c` and this node's flow already assumes `b` executed. if (node.previousNodes.size < 2) return@mergeIncomingFlow @@ -779,7 +779,7 @@ abstract class FirDataFlowAnalyzer( // TODO? all new implications in previous node's flow are valid here if receiver != null // (that requires a second level of implications: receiver != null => condition => effect). // KT-59689 - flow.addAllConditionally(expressionVariable notEq null, node.lastPreviousNode.flow) + flow.addAllConditionally(expressionVariable notEq null, node.lastPreviousNode.getFlow(path)) } } @@ -1003,10 +1003,10 @@ abstract class FirDataFlowAnalyzer( graphBuilder.exitBinaryLogicExpression(binaryLogicExpression).mergeBinaryLogicOperatorFlow() } - private fun AbstractBinaryExitNode.mergeBinaryLogicOperatorFlow() = mergeIncomingFlow { _, flow -> + private fun AbstractBinaryExitNode.mergeBinaryLogicOperatorFlow() = mergeIncomingFlow { path, flow -> val isAnd = fir.kind == LogicOperationKind.AND - val flowFromLeft = leftOperandNode.flow - val flowFromRight = rightOperandNode.flow + val flowFromLeft = leftOperandNode.getFlow(path) + val flowFromRight = rightOperandNode.getFlow(path) val leftVariable = variableStorage.get(flowFromLeft, fir.leftOperand) val leftIsBoolean = leftVariable != null && fir.leftOperand.coneType.isBoolean @@ -1105,19 +1105,21 @@ abstract class FirDataFlowAnalyzer( val (lhsExitNode, lhsIsNotNullNode, rhsEnterNode) = graphBuilder.exitElvisLhs(elvisExpression) lhsExitNode.mergeIncomingFlow() - val lhsVariable = variableStorage.getOrCreateIfReal(lhsExitNode.flow, elvisExpression.lhs) - lhsIsNotNullNode.mergeIncomingFlow { _, flow -> - lhsVariable?.let { flow.commitOperationStatement(it notEq null) } + fun getLhsVariable(path: FlowPath): DataFlowVariable? = + variableStorage.getOrCreateIfReal(lhsExitNode.getFlow(path), elvisExpression.lhs) + + lhsIsNotNullNode.mergeIncomingFlow { path, flow -> + getLhsVariable(path)?.let { flow.commitOperationStatement(it notEq null) } } - rhsEnterNode.mergeIncomingFlow { _, flow -> - lhsVariable?.let { flow.commitOperationStatement(it eq null) } + rhsEnterNode.mergeIncomingFlow { path, flow -> + getLhsVariable(path)?.let { flow.commitOperationStatement(it eq null) } } } @OptIn(UnexpandedTypeCheck::class) fun exitElvis(elvisExpression: FirElvisExpression, isLhsNotNull: Boolean, callCompleted: Boolean) { val node = graphBuilder.exitElvis(isLhsNotNull, callCompleted) - node.mergeIncomingFlow { _, flow -> + node.mergeIncomingFlow { path, flow -> // If LHS is never null, then the edge from RHS is dead and this node's flow already contains // all statements from LHS unconditionally. if (isLhsNotNull) return@mergeIncomingFlow @@ -1144,7 +1146,7 @@ abstract class FirDataFlowAnalyzer( // implications, but for constant v the logic system can handle some basic cases of P(x). val rhs = (elvisExpression.rhs as? FirConstExpression<*>)?.value as? Boolean if (rhs != null) { - flow.addAllConditionally(elvisVariable eq !rhs, node.firstPreviousNode.flow) + flow.addAllConditionally(elvisVariable eq !rhs, node.firstPreviousNode.getFlow(path)) } } } @@ -1161,9 +1163,6 @@ abstract class FirDataFlowAnalyzer( // ------------------------------------------------------ Utils ------------------------------------------------------ - private val CFGNode<*>.livePreviousFlows: List - get() = previousNodes.mapNotNull { it.takeIf { this.isDead || !it.isDead }?.flow } - // Smart cast information is taken from `graphBuilder.lastNode`, but the problem with receivers specifically // is that they also affect tower resolver's scope stack. To allow accessing members on smart casted receivers, // we explicitly patch up the stack by calling `receiverUpdated` in a way that maintains consistency with @@ -1177,18 +1176,39 @@ abstract class FirDataFlowAnalyzer( path: FlowPath, builder: (FlowPath, MutableFlow) -> Unit, ): MutableFlow { - val previousFlows = previousNodes.mapNotNull { - val incomingEdgeKind = edgeFrom(it).kind - if (if (isDead) !incomingEdgeKind.usedInDeadDfa else !incomingEdgeKind.usedInDfa) return@mapNotNull null + val previousFlows = previousDfaNodes.mapNotNull { (edge, node) -> + // For CFGNodes that cause alternate flow paths to be created, only edges with matching labels should be merged. However, when + // an alternate flow is being propagated through one of these CFGNodes - i.e., when the FirElements do not match - only + // NormalPath edges should be merged. + if (this is AlternateFlowStartMarker && path is FlowPath.CfgEdge) { + if (path.fir == this.fir && edge.label != path.label) { + return@mapNotNull null + } else if (path.fir != this.fir && edge.label != NormalPath) { + return@mapNotNull null + } + } // `MergePostponedLambdaExitsNode` nodes form a parallel data flow graph. We never compute // data flow for any of them until reaching a completed call. - if (it is MergePostponedLambdaExitsNode) it.mergeIncomingFlow() + if (node is MergePostponedLambdaExitsNode && !node.flowInitialized) node.mergeIncomingFlow() - it.flow - } + when (path) { + FlowPath.Default -> { + // For CFGNodes that are the end of alternate flows, use the alternate flow associated with the edge label. + if (node is AlternateFlowEndMarker) { + val alternatePath = FlowPath.CfgEdge(edge.label, node.fir) + node.getAlternateFlow(alternatePath) ?: node.flow + } else { + node.flow + } + } + else -> { + node.getAlternateFlow(path) ?: node.flow + } + } + }.toList() val result = logicSystem.joinFlow(previousFlows, isUnion) - if (graphBuilder.lastNodeOrNull == this) { + if (path == FlowPath.Default && graphBuilder.lastNodeOrNull == this) { // Here it is, the new `lastNode`. If the previous state is the only predecessor, then there is actually // nothing to update; `addTypeStatement` has already ensured we have the correct information. if (currentReceiverState == null || previousFlows.singleOrNull() != currentReceiverState) { @@ -1203,11 +1223,42 @@ abstract class FirDataFlowAnalyzer( private fun CFGNode<*>.mergeIncomingFlow( builder: (FlowPath, MutableFlow) -> Unit = { _, _ -> }, ) { + // Always build the default flow path for all nodes. val mutableDefaultFlow = buildIncomingFlow(FlowPath.Default, builder) val defaultFlow = mutableDefaultFlow.freeze().also { this.flow = it } if (currentReceiverState === mutableDefaultFlow) { currentReceiverState = defaultFlow } + + // Propagate alternate flows from previous nodes. + val propagatePaths = previousDfaNodes.flatMapTo(mutableSetOf()) { (edge, node) -> + when (node) { + // If the source node is the end of alternate flows, do not propagate the alternate flows which have ended. + is AlternateFlowEndMarker -> node.alternateFlowPaths.filter { it !is FlowPath.CfgEdge || it.fir != node.fir } + // Otherwise, only propagate alternate flows which originate along a normal path edge. + else -> node.alternateFlowPaths.takeIf { edge.label == NormalPath } ?: emptyList() + } + } + for (path in propagatePaths) { + addAlternateFlow(path, buildIncomingFlow(path, builder).freeze()) + } + + // Add any new alternate flows that should be created. + if (this is AlternateFlowStartMarker) { + val additionalPaths = previousDfaNodes + .mapNotNullTo(mutableSetOf()) { (edge, _) -> edge.label.takeIf { it != UncaughtExceptionPath } } + .map { FlowPath.CfgEdge(it, this.fir) } + for (path in additionalPaths) { + addAlternateFlow(path, buildIncomingFlow(path, builder).freeze()) + } + } + } + + private fun CFGNode<*>.getFlow(path: FlowPath): PersistentFlow { + return when (path) { + FlowPath.Default -> flow + else -> getAlternateFlow(path) ?: error("no alternate flow for $path") + } } // In rare cases (like after exiting functions) after adding more nodes `graphBuilder` will revert the current diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/FlowPath.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/FlowPath.kt index 4b9d1be91c2..fcf0360e8a5 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/FlowPath.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/FlowPath.kt @@ -9,8 +9,8 @@ import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.resolve.dfa.cfg.EdgeLabel /** - * Sealed interface representing a type of path through a [Control Flow Graph (CFG) Node][org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode] - * used for data flow analysis. Most CFG nodes only have a single data flow path through them, but there are times when a node may require + * Sealed class representing a type of path through a [Control Flow Graph (CFG) Node][org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode] used + * for data flow analysis. Most CFG nodes only have a single data flow path through them, but there are times when a node may require * multiple paths to be calculated. The most common use case is for `finally` code blocks, where multiple code paths may enter, but these * paths diverge after exiting the code block. Consider the following (very) contrived example: * @@ -56,11 +56,11 @@ import org.jetbrains.kotlin.fir.resolve.dfa.cfg.EdgeLabel * 3. A flow which will be used when the entire `try` expression exits without exception or jumping. This data flow is a continuation of the * data flow leading into the `finally` block from the main `try` block. */ -sealed interface FlowPath { +sealed class FlowPath { /** * The [FlowPath] which represents the combination of all flows leading into a CFG Node. */ - data object Default : FlowPath + data object Default : FlowPath() /** * The [FlowPath] which represents the combination of all flows leading into a CFG Node that follow an edge with the specified @@ -68,5 +68,5 @@ sealed interface FlowPath { * multiple flows. For example, a `finally` block within another `finally` block will require multiple * [normal paths][org.jetbrains.kotlin.fir.resolve.dfa.cfg.NormalPath] through each that diverge at different nodes. */ - data class CfgEdge(val label: EdgeLabel, val fir: FirElement) : FlowPath + data class CfgEdge(val label: EdgeLabel, val fir: FirElement) : FlowPath() } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt index 6f2fe99b0fc..d9c09509d28 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt @@ -170,11 +170,22 @@ sealed class CFGNode(val owner: ControlFlowGraph, val level: val CFGNode<*>.firstPreviousNode: CFGNode<*> get() = previousNodes[0] val CFGNode<*>.lastPreviousNode: CFGNode<*> get() = previousNodes.last() +val CFGNode<*>.previousDfaNodes: Sequence>> + get() = previousNodes.asSequence() + .map { edgeFrom(it) to it } + .filter { (edge, _) -> if (isDead) edge.kind.usedInDeadDfa else edge.kind.usedInDfa } +val CFGNode<*>.previousLiveNodes: Sequence> + get() = when { + this.isDead -> previousNodes.asSequence() + else -> previousNodes.asSequence().mapNotNull { it.takeIf { !it.isDead } } + } interface EnterNodeMarker interface ExitNodeMarker interface GraphEnterNodeMarker : EnterNodeMarker interface GraphExitNodeMarker : ExitNodeMarker +interface AlternateFlowStartMarker +interface AlternateFlowEndMarker // ----------------------------------- EnterNode for declaration with CFG ----------------------------------- @@ -260,6 +271,17 @@ class PostponedLambdaExitNode(owner: ControlFlowGraph, override val fir: FirAnon } class MergePostponedLambdaExitsNode(owner: ControlFlowGraph, override val fir: FirElement, level: Int) : CFGNode(owner, level) { + + private var _flowInitialized = false + val flowInitialized: Boolean get() = _flowInitialized + override var flow: PersistentFlow + get() = super.flow + @CfgInternals + set(value) { + super.flow = value + _flowInitialized = true + } + override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitMergePostponedLambdaExitsNode(this, data) } @@ -543,13 +565,13 @@ class CatchClauseExitNode(owner: ControlFlowGraph, override val fir: FirCatch, l } } class FinallyBlockEnterNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int) : CFGNode(owner, level), - EnterNodeMarker { + EnterNodeMarker, AlternateFlowStartMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitFinallyBlockEnterNode(this, data) } } class FinallyBlockExitNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int) : CFGNode(owner, level), - ExitNodeMarker { + ExitNodeMarker, AlternateFlowEndMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitFinallyBlockExitNode(this, data) } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.fir.kt new file mode 100644 index 00000000000..fae804b9416 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.fir.kt @@ -0,0 +1,23 @@ +// ISSUE: KT-51759 + +fun testBreak(b: Boolean, s: String?) { + while (b) { + val x: String? + try { + x = s ?: break + } finally { + } + x!!.length + } +} + +fun testContinue(b: Boolean, s: String?) { + while (b) { + val x: String? + try { + x = s ?: continue + } finally { + } + x!!.length + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.kt index b2d5153168a..359d8535c4e 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // ISSUE: KT-51759 fun testBreak(b: Boolean, s: String?) { diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithCatch.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithCatch.fir.kt index 225108b566a..67646bbd21b 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithCatch.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithCatch.fir.kt @@ -17,7 +17,7 @@ fun castInTryAndCatch(s: Any) { } finally { s.length // shouldn't be resolved } - s.length // should be smartcast + s.length // should be smartcast } fun castAtAll(s: Any) { diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithJump.dot b/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithJump.dot index 849607e4c95..565729ef8b4 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithJump.dot +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithJump.dot @@ -123,32 +123,38 @@ digraph castInTryWithJump_fir_kt { 48 [label="Try expression exit"]; } 49 [label="Access variable R|/x|"]; - 50 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 51 [label="Access variable R|/x|"]; - 52 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 53 [label="Exit block"]; + 50 [label="Smart cast: R|/x|"]; + 51 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 52 [label="Access variable R|/x|"]; + 53 [label="Smart cast: R|/x|"]; + 54 [label="Function call: R|/x|.R|/B.bbb|()" style="filled" fillcolor=yellow]; + 55 [label="Exit block"]; } - 54 [label="Exit finally"]; + 56 [label="Exit finally"]; } - 55 [label="Try expression exit" style="filled" fillcolor=gray]; + 57 [label="Try expression exit" style="filled" fillcolor=gray]; } - 56 [label="Access variable R|/x|" style="filled" fillcolor=gray]; - 57 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; 58 [label="Access variable R|/x|" style="filled" fillcolor=gray]; - 59 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; - 60 [label="Exit block" style="filled" fillcolor=gray]; + 59 [label="Smart cast: R|/x|" style="filled" fillcolor=gray]; + 60 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; + 61 [label="Access variable R|/x|" style="filled" fillcolor=gray]; + 62 [label="Smart cast: R|/x|" style="filled" fillcolor=gray]; + 63 [label="Function call: R|/x|.R|/B.bbb|()" style="filled" fillcolor=gray]; + 64 [label="Exit block" style="filled" fillcolor=gray]; } - 61 [label="Exit loop block" style="filled" fillcolor=gray]; + 65 [label="Exit loop block" style="filled" fillcolor=gray]; } - 62 [label="Exit while loop"]; + 66 [label="Exit while loop"]; } - 63 [label="Access variable R|/x|"]; - 64 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 65 [label="Access variable R|/x|"]; - 66 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 67 [label="Exit block"]; + 67 [label="Access variable R|/x|"]; + 68 [label="Smart cast: R|/x|"]; + 69 [label="Function call: R|/x|.R|/A.aaa|()" style="filled" fillcolor=yellow]; + 70 [label="Access variable R|/x|"]; + 71 [label="Smart cast: R|/x|"]; + 72 [label="Function call: R|/x|.R|/B.bbb|()" style="filled" fillcolor=yellow]; + 73 [label="Exit block"]; } - 68 [label="Exit function breakInTry_withNestedFinally" style="filled" fillcolor=red]; + 74 [label="Exit function breakInTry_withNestedFinally" style="filled" fillcolor=red]; } 12 -> {13}; 13 -> {14}; @@ -158,7 +164,7 @@ digraph castInTryWithJump_fir_kt { 17 -> {18}; 18 -> {19}; 19 -> {20}; - 19 -> {62} [style=dotted]; + 19 -> {66} [style=dotted]; 20 -> {21}; 21 -> {22}; 22 -> {23}; @@ -200,276 +206,283 @@ digraph castInTryWithJump_fir_kt { 51 -> {52}; 52 -> {53}; 53 -> {54}; - 54 -> {62} [label="break"]; - 54 -> {55} [style=dotted]; - 55 -> {56} [style=dotted]; + 54 -> {55}; + 55 -> {56}; + 56 -> {66} [label="break"]; 56 -> {57} [style=dotted]; 57 -> {58} [style=dotted]; 58 -> {59} [style=dotted]; 59 -> {60} [style=dotted]; 60 -> {61} [style=dotted]; - 61 -> {17} [color=green style=dotted]; - 62 -> {63}; - 63 -> {64}; - 64 -> {65}; - 65 -> {66}; + 61 -> {62} [style=dotted]; + 62 -> {63} [style=dotted]; + 63 -> {64} [style=dotted]; + 64 -> {65} [style=dotted]; + 65 -> {17} [color=green style=dotted]; 66 -> {67}; 67 -> {68}; - - subgraph cluster_22 { - color=red - 69 [label="Enter function returnInCatch" style="filled" fillcolor=red]; - subgraph cluster_23 { - color=blue - 70 [label="Enter block"]; - 71 [label="Const: Null(null)"]; - 72 [label="Variable declaration: lvar x: R|kotlin/Any?|"]; - subgraph cluster_24 { - color=blue - 73 [label="Try expression enter"]; - subgraph cluster_25 { - color=blue - 74 [label="Try main block enter"]; - subgraph cluster_26 { - color=blue - 75 [label="Enter block"]; - 76 [label="Access variable R|/x|"]; - 77 [label="Type operator: (R|/x| as R|A|)"]; - 78 [label="Exit block"]; - } - 79 [label="Try main block exit"]; - } - subgraph cluster_27 { - color=blue - 80 [label="Catch enter"]; - 81 [label="Variable declaration: e: R|kotlin/Exception|"]; - subgraph cluster_28 { - color=blue - 82 [label="Enter block"]; - 83 [label="Access variable R|/x|"]; - 84 [label="Type operator: (R|/x| as R|B|)"]; - 85 [label="Jump: ^returnInCatch Unit"]; - 86 [label="Stub" style="filled" fillcolor=gray]; - 87 [label="Exit block" style="filled" fillcolor=gray]; - } - 88 [label="Catch exit" style="filled" fillcolor=gray]; - } - subgraph cluster_29 { - color=blue - 89 [label="Enter finally"]; - subgraph cluster_30 { - color=blue - 90 [label="Enter block"]; - 91 [label="Access variable R|/x|"]; - 92 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 93 [label="Access variable R|/x|"]; - 94 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 95 [label="Exit block"]; - } - 96 [label="Exit finally"]; - } - 97 [label="Try expression exit"]; - } - 98 [label="Access variable R|/x|"]; - 99 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 100 [label="Access variable R|/x|"]; - 101 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 102 [label="Exit block"]; - } - 103 [label="Exit function returnInCatch" style="filled" fillcolor=red]; - } + 68 -> {69}; 69 -> {70}; 70 -> {71}; 71 -> {72}; 72 -> {73}; - 73 -> {74 80}; - 73 -> {89} [label="onUncaughtException"]; - 74 -> {75}; + 73 -> {74}; + + subgraph cluster_22 { + color=red + 75 [label="Enter function returnInCatch" style="filled" fillcolor=red]; + subgraph cluster_23 { + color=blue + 76 [label="Enter block"]; + 77 [label="Const: Null(null)"]; + 78 [label="Variable declaration: lvar x: R|kotlin/Any?|"]; + subgraph cluster_24 { + color=blue + 79 [label="Try expression enter"]; + subgraph cluster_25 { + color=blue + 80 [label="Try main block enter"]; + subgraph cluster_26 { + color=blue + 81 [label="Enter block"]; + 82 [label="Access variable R|/x|"]; + 83 [label="Type operator: (R|/x| as R|A|)"]; + 84 [label="Exit block"]; + } + 85 [label="Try main block exit"]; + } + subgraph cluster_27 { + color=blue + 86 [label="Catch enter"]; + 87 [label="Variable declaration: e: R|kotlin/Exception|"]; + subgraph cluster_28 { + color=blue + 88 [label="Enter block"]; + 89 [label="Access variable R|/x|"]; + 90 [label="Type operator: (R|/x| as R|B|)"]; + 91 [label="Jump: ^returnInCatch Unit"]; + 92 [label="Stub" style="filled" fillcolor=gray]; + 93 [label="Exit block" style="filled" fillcolor=gray]; + } + 94 [label="Catch exit" style="filled" fillcolor=gray]; + } + subgraph cluster_29 { + color=blue + 95 [label="Enter finally"]; + subgraph cluster_30 { + color=blue + 96 [label="Enter block"]; + 97 [label="Access variable R|/x|"]; + 98 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 99 [label="Access variable R|/x|"]; + 100 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 101 [label="Exit block"]; + } + 102 [label="Exit finally"]; + } + 103 [label="Try expression exit"]; + } + 104 [label="Access variable R|/x|"]; + 105 [label="Smart cast: R|/x|"]; + 106 [label="Function call: R|/x|.R|/A.aaa|()" style="filled" fillcolor=yellow]; + 107 [label="Access variable R|/x|"]; + 108 [label="Smart cast: R|/x|"]; + 109 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 110 [label="Exit block"]; + } + 111 [label="Exit function returnInCatch" style="filled" fillcolor=red]; + } 75 -> {76}; - 76 -> {77 80}; - 76 -> {89} [label="onUncaughtException"]; - 77 -> {78 80}; - 77 -> {89} [label="onUncaughtException"]; + 76 -> {77}; + 77 -> {78}; 78 -> {79}; - 79 -> {80 89}; + 79 -> {80 86}; + 79 -> {95} [label="onUncaughtException"]; 80 -> {81}; - 80 -> {89} [label="onUncaughtException"]; 81 -> {82}; - 82 -> {83}; - 83 -> {84}; - 83 -> {89} [label="onUncaughtException"]; + 82 -> {83 86}; + 82 -> {95} [label="onUncaughtException"]; + 83 -> {84 86}; + 83 -> {95} [label="onUncaughtException"]; 84 -> {85}; - 84 -> {89} [label="onUncaughtException"]; - 85 -> {89} [label="return@/returnInCatch"]; - 85 -> {86} [style=dotted]; - 86 -> {87} [style=dotted]; - 87 -> {88} [style=dotted]; - 88 -> {89} [style=dotted]; + 85 -> {86 95}; + 86 -> {87}; + 86 -> {95} [label="onUncaughtException"]; + 87 -> {88}; + 88 -> {89}; 89 -> {90}; + 89 -> {95} [label="onUncaughtException"]; 90 -> {91}; - 91 -> {92}; - 92 -> {93}; - 93 -> {94}; - 94 -> {95}; + 90 -> {95} [label="onUncaughtException"]; + 91 -> {95} [label="return@/returnInCatch"]; + 91 -> {92} [style=dotted]; + 92 -> {93} [style=dotted]; + 93 -> {94} [style=dotted]; + 94 -> {95} [style=dotted]; 95 -> {96}; 96 -> {97}; - 96 -> {103} [label="return@/returnInCatch"]; 97 -> {98}; 98 -> {99}; 99 -> {100}; 100 -> {101}; 101 -> {102}; 102 -> {103}; - - subgraph cluster_31 { - color=red - 104 [label="Enter function returnInCatch_insideFinally" style="filled" fillcolor=red]; - subgraph cluster_32 { - color=blue - 105 [label="Enter block"]; - 106 [label="Const: Null(null)"]; - 107 [label="Variable declaration: lvar x: R|kotlin/Any?|"]; - subgraph cluster_33 { - color=blue - 108 [label="Try expression enter"]; - subgraph cluster_34 { - color=blue - 109 [label="Try main block enter"]; - subgraph cluster_35 { - color=blue - 110 [label="Enter block"]; - 111 [label="Access variable R|/x|"]; - 112 [label="Type operator: (R|/x| as R|C|)"]; - 113 [label="Exit block"]; - } - 114 [label="Try main block exit"]; - } - subgraph cluster_36 { - color=blue - 115 [label="Enter finally"]; - subgraph cluster_37 { - color=blue - 116 [label="Enter block"]; - subgraph cluster_38 { - color=blue - 117 [label="Try expression enter"]; - subgraph cluster_39 { - color=blue - 118 [label="Try main block enter"]; - subgraph cluster_40 { - color=blue - 119 [label="Enter block"]; - 120 [label="Access variable R|/x|"]; - 121 [label="Type operator: (R|/x| as R|A|)"]; - 122 [label="Exit block"]; - } - 123 [label="Try main block exit"]; - } - subgraph cluster_41 { - color=blue - 124 [label="Catch enter"]; - 125 [label="Variable declaration: e: R|kotlin/Exception|"]; - subgraph cluster_42 { - color=blue - 126 [label="Enter block"]; - 127 [label="Access variable R|/x|"]; - 128 [label="Type operator: (R|/x| as R|B|)"]; - 129 [label="Jump: ^returnInCatch_insideFinally Unit"]; - 130 [label="Stub" style="filled" fillcolor=gray]; - 131 [label="Exit block" style="filled" fillcolor=gray]; - } - 132 [label="Catch exit" style="filled" fillcolor=gray]; - } - subgraph cluster_43 { - color=blue - 133 [label="Enter finally"]; - subgraph cluster_44 { - color=blue - 134 [label="Enter block"]; - 135 [label="Access variable R|/x|"]; - 136 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 137 [label="Access variable R|/x|"]; - 138 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 139 [label="Access variable R|/x|"]; - 140 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 141 [label="Exit block"]; - } - 142 [label="Exit finally"]; - } - 143 [label="Try expression exit"]; - } - 144 [label="Access variable R|/x|"]; - 145 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 146 [label="Access variable R|/x|"]; - 147 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 148 [label="Access variable R|/x|"]; - 149 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 150 [label="Exit block"]; - } - 151 [label="Exit finally"]; - } - 152 [label="Try expression exit"]; - } - 153 [label="Access variable R|/x|"]; - 154 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 155 [label="Access variable R|/x|"]; - 156 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 157 [label="Access variable R|/x|"]; - 158 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 159 [label="Exit block"]; - } - 160 [label="Exit function returnInCatch_insideFinally" style="filled" fillcolor=red]; - } + 102 -> {111} [label="return@/returnInCatch"]; + 103 -> {104}; 104 -> {105}; 105 -> {106}; 106 -> {107}; 107 -> {108}; 108 -> {109}; - 108 -> {115} [label="onUncaughtException"]; 109 -> {110}; 110 -> {111}; - 111 -> {112}; - 111 -> {115} [label="onUncaughtException"]; + + subgraph cluster_31 { + color=red + 112 [label="Enter function returnInCatch_insideFinally" style="filled" fillcolor=red]; + subgraph cluster_32 { + color=blue + 113 [label="Enter block"]; + 114 [label="Const: Null(null)"]; + 115 [label="Variable declaration: lvar x: R|kotlin/Any?|"]; + subgraph cluster_33 { + color=blue + 116 [label="Try expression enter"]; + subgraph cluster_34 { + color=blue + 117 [label="Try main block enter"]; + subgraph cluster_35 { + color=blue + 118 [label="Enter block"]; + 119 [label="Access variable R|/x|"]; + 120 [label="Type operator: (R|/x| as R|C|)"]; + 121 [label="Exit block"]; + } + 122 [label="Try main block exit"]; + } + subgraph cluster_36 { + color=blue + 123 [label="Enter finally"]; + subgraph cluster_37 { + color=blue + 124 [label="Enter block"]; + subgraph cluster_38 { + color=blue + 125 [label="Try expression enter"]; + subgraph cluster_39 { + color=blue + 126 [label="Try main block enter"]; + subgraph cluster_40 { + color=blue + 127 [label="Enter block"]; + 128 [label="Access variable R|/x|"]; + 129 [label="Type operator: (R|/x| as R|A|)"]; + 130 [label="Exit block"]; + } + 131 [label="Try main block exit"]; + } + subgraph cluster_41 { + color=blue + 132 [label="Catch enter"]; + 133 [label="Variable declaration: e: R|kotlin/Exception|"]; + subgraph cluster_42 { + color=blue + 134 [label="Enter block"]; + 135 [label="Access variable R|/x|"]; + 136 [label="Type operator: (R|/x| as R|B|)"]; + 137 [label="Jump: ^returnInCatch_insideFinally Unit"]; + 138 [label="Stub" style="filled" fillcolor=gray]; + 139 [label="Exit block" style="filled" fillcolor=gray]; + } + 140 [label="Catch exit" style="filled" fillcolor=gray]; + } + subgraph cluster_43 { + color=blue + 141 [label="Enter finally"]; + subgraph cluster_44 { + color=blue + 142 [label="Enter block"]; + 143 [label="Access variable R|/x|"]; + 144 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 145 [label="Access variable R|/x|"]; + 146 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 147 [label="Access variable R|/x|"]; + 148 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 149 [label="Exit block"]; + } + 150 [label="Exit finally"]; + } + 151 [label="Try expression exit"]; + } + 152 [label="Access variable R|/x|"]; + 153 [label="Smart cast: R|/x|"]; + 154 [label="Function call: R|/x|.R|/A.aaa|()" style="filled" fillcolor=yellow]; + 155 [label="Access variable R|/x|"]; + 156 [label="Smart cast: R|/x|"]; + 157 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 158 [label="Access variable R|/x|"]; + 159 [label="Smart cast: R|/x|"]; + 160 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 161 [label="Exit block"]; + } + 162 [label="Exit finally"]; + } + 163 [label="Try expression exit"]; + } + 164 [label="Access variable R|/x|"]; + 165 [label="Smart cast: R|/x|"]; + 166 [label="Function call: R|/x|.R|/A.aaa|()" style="filled" fillcolor=yellow]; + 167 [label="Access variable R|/x|"]; + 168 [label="Smart cast: R|/x|"]; + 169 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 170 [label="Access variable R|/x|"]; + 171 [label="Smart cast: R|/x|"]; + 172 [label="Function call: R|/x|.R|/C.ccc|()" style="filled" fillcolor=yellow]; + 173 [label="Exit block"]; + } + 174 [label="Exit function returnInCatch_insideFinally" style="filled" fillcolor=red]; + } 112 -> {113}; - 112 -> {115} [label="onUncaughtException"]; 113 -> {114}; 114 -> {115}; 115 -> {116}; 116 -> {117}; - 117 -> {118 124}; - 117 -> {133} [label="onUncaughtException"]; + 116 -> {123} [label="onUncaughtException"]; + 117 -> {118}; 118 -> {119}; 119 -> {120}; - 120 -> {121 124}; - 120 -> {133} [label="onUncaughtException"]; - 121 -> {122 124}; - 121 -> {133} [label="onUncaughtException"]; + 119 -> {123} [label="onUncaughtException"]; + 120 -> {121}; + 120 -> {123} [label="onUncaughtException"]; + 121 -> {122}; 122 -> {123}; - 123 -> {124 133}; + 123 -> {124}; 124 -> {125}; - 124 -> {133} [label="onUncaughtException"]; - 125 -> {126}; + 125 -> {126 132}; + 125 -> {141} [label="onUncaughtException"]; 126 -> {127}; 127 -> {128}; - 127 -> {133} [label="onUncaughtException"]; - 128 -> {129}; - 128 -> {133} [label="onUncaughtException"]; - 129 -> {133} [label="return@/returnInCatch_insideFinally"]; - 129 -> {130} [style=dotted]; - 130 -> {131} [style=dotted]; - 131 -> {132} [style=dotted]; - 132 -> {133} [style=dotted]; + 128 -> {129 132}; + 128 -> {141} [label="onUncaughtException"]; + 129 -> {130 132}; + 129 -> {141} [label="onUncaughtException"]; + 130 -> {131}; + 131 -> {132 141}; + 132 -> {133}; + 132 -> {141} [label="onUncaughtException"]; 133 -> {134}; 134 -> {135}; 135 -> {136}; + 135 -> {141} [label="onUncaughtException"]; 136 -> {137}; - 137 -> {138}; - 138 -> {139}; - 139 -> {140}; - 140 -> {141}; + 136 -> {141} [label="onUncaughtException"]; + 137 -> {141} [label="return@/returnInCatch_insideFinally"]; + 137 -> {138} [style=dotted]; + 138 -> {139} [style=dotted]; + 139 -> {140} [style=dotted]; + 140 -> {141} [style=dotted]; 141 -> {142}; 142 -> {143}; - 142 -> {160} [label="return@/returnInCatch_insideFinally"]; 143 -> {144}; 144 -> {145}; 145 -> {146}; @@ -478,6 +491,7 @@ digraph castInTryWithJump_fir_kt { 148 -> {149}; 149 -> {150}; 150 -> {151}; + 150 -> {174} [label="return@/returnInCatch_insideFinally"]; 151 -> {152}; 152 -> {153}; 153 -> {154}; @@ -487,94 +501,7 @@ digraph castInTryWithJump_fir_kt { 157 -> {158}; 158 -> {159}; 159 -> {160}; - - subgraph cluster_45 { - color=red - 161 [label="Enter function breakInCatch" style="filled" fillcolor=red]; - subgraph cluster_46 { - color=blue - 162 [label="Enter block"]; - 163 [label="Const: Null(null)"]; - 164 [label="Variable declaration: lvar x: R|kotlin/Any?|"]; - subgraph cluster_47 { - color=blue - 165 [label="Enter while loop"]; - subgraph cluster_48 { - color=blue - 166 [label="Enter loop condition"]; - 167 [label="Const: Boolean(true)"]; - 168 [label="Exit loop condition"]; - } - subgraph cluster_49 { - color=blue - 169 [label="Enter loop block"]; - subgraph cluster_50 { - color=blue - 170 [label="Enter block"]; - subgraph cluster_51 { - color=blue - 171 [label="Try expression enter"]; - subgraph cluster_52 { - color=blue - 172 [label="Try main block enter"]; - subgraph cluster_53 { - color=blue - 173 [label="Enter block"]; - 174 [label="Access variable R|/x|"]; - 175 [label="Type operator: (R|/x| as R|A|)"]; - 176 [label="Exit block"]; - } - 177 [label="Try main block exit"]; - } - subgraph cluster_54 { - color=blue - 178 [label="Catch enter"]; - 179 [label="Variable declaration: e: R|kotlin/Exception|"]; - subgraph cluster_55 { - color=blue - 180 [label="Enter block"]; - 181 [label="Access variable R|/x|"]; - 182 [label="Type operator: (R|/x| as R|B|)"]; - 183 [label="Jump: break@@@[Boolean(true)] "]; - 184 [label="Stub" style="filled" fillcolor=gray]; - 185 [label="Exit block" style="filled" fillcolor=gray]; - } - 186 [label="Catch exit" style="filled" fillcolor=gray]; - } - subgraph cluster_56 { - color=blue - 187 [label="Enter finally"]; - subgraph cluster_57 { - color=blue - 188 [label="Enter block"]; - 189 [label="Access variable R|/x|"]; - 190 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 191 [label="Access variable R|/x|"]; - 192 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 193 [label="Exit block"]; - } - 194 [label="Exit finally"]; - } - 195 [label="Try expression exit"]; - } - 196 [label="Access variable R|/x|"]; - 197 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 198 [label="Access variable R|/x|"]; - 199 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 200 [label="Exit block"]; - } - 201 [label="Exit loop block"]; - } - 202 [label="Exit while loop"]; - } - 203 [label="Access variable R|/x|"]; - 204 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 205 [label="Access variable R|/x|"]; - 206 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 207 [label="Exit block"]; - } - 208 [label="Exit function breakInCatch" style="filled" fillcolor=red]; - } + 160 -> {161}; 161 -> {162}; 162 -> {163}; 163 -> {164}; @@ -583,226 +510,335 @@ digraph castInTryWithJump_fir_kt { 166 -> {167}; 167 -> {168}; 168 -> {169}; - 168 -> {202} [style=dotted]; 169 -> {170}; 170 -> {171}; - 171 -> {172 178}; - 171 -> {187} [label="onUncaughtException"]; + 171 -> {172}; 172 -> {173}; 173 -> {174}; - 174 -> {175 178}; - 174 -> {187} [label="onUncaughtException"]; - 175 -> {176 178}; - 175 -> {187} [label="onUncaughtException"]; + + subgraph cluster_45 { + color=red + 175 [label="Enter function breakInCatch" style="filled" fillcolor=red]; + subgraph cluster_46 { + color=blue + 176 [label="Enter block"]; + 177 [label="Const: Null(null)"]; + 178 [label="Variable declaration: lvar x: R|kotlin/Any?|"]; + subgraph cluster_47 { + color=blue + 179 [label="Enter while loop"]; + subgraph cluster_48 { + color=blue + 180 [label="Enter loop condition"]; + 181 [label="Const: Boolean(true)"]; + 182 [label="Exit loop condition"]; + } + subgraph cluster_49 { + color=blue + 183 [label="Enter loop block"]; + subgraph cluster_50 { + color=blue + 184 [label="Enter block"]; + subgraph cluster_51 { + color=blue + 185 [label="Try expression enter"]; + subgraph cluster_52 { + color=blue + 186 [label="Try main block enter"]; + subgraph cluster_53 { + color=blue + 187 [label="Enter block"]; + 188 [label="Access variable R|/x|"]; + 189 [label="Type operator: (R|/x| as R|A|)"]; + 190 [label="Exit block"]; + } + 191 [label="Try main block exit"]; + } + subgraph cluster_54 { + color=blue + 192 [label="Catch enter"]; + 193 [label="Variable declaration: e: R|kotlin/Exception|"]; + subgraph cluster_55 { + color=blue + 194 [label="Enter block"]; + 195 [label="Access variable R|/x|"]; + 196 [label="Type operator: (R|/x| as R|B|)"]; + 197 [label="Jump: break@@@[Boolean(true)] "]; + 198 [label="Stub" style="filled" fillcolor=gray]; + 199 [label="Exit block" style="filled" fillcolor=gray]; + } + 200 [label="Catch exit" style="filled" fillcolor=gray]; + } + subgraph cluster_56 { + color=blue + 201 [label="Enter finally"]; + subgraph cluster_57 { + color=blue + 202 [label="Enter block"]; + 203 [label="Access variable R|/x|"]; + 204 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 205 [label="Access variable R|/x|"]; + 206 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 207 [label="Exit block"]; + } + 208 [label="Exit finally"]; + } + 209 [label="Try expression exit"]; + } + 210 [label="Access variable R|/x|"]; + 211 [label="Smart cast: R|/x|"]; + 212 [label="Function call: R|/x|.R|/A.aaa|()" style="filled" fillcolor=yellow]; + 213 [label="Access variable R|/x|"]; + 214 [label="Smart cast: R|/x|"]; + 215 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 216 [label="Exit block"]; + } + 217 [label="Exit loop block"]; + } + 218 [label="Exit while loop"]; + } + 219 [label="Access variable R|/x|"]; + 220 [label="Smart cast: R|/x|"]; + 221 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 222 [label="Access variable R|/x|"]; + 223 [label="Smart cast: R|/x|"]; + 224 [label="Function call: R|/x|.R|/B.bbb|()" style="filled" fillcolor=yellow]; + 225 [label="Exit block"]; + } + 226 [label="Exit function breakInCatch" style="filled" fillcolor=red]; + } + 175 -> {176}; 176 -> {177}; - 177 -> {178 187}; + 177 -> {178}; 178 -> {179}; - 178 -> {187} [label="onUncaughtException"]; 179 -> {180}; 180 -> {181}; 181 -> {182}; - 181 -> {187} [label="onUncaughtException"]; 182 -> {183}; - 182 -> {187} [label="onUncaughtException"]; - 183 -> {187} [label="break"]; - 183 -> {184} [style=dotted]; - 184 -> {185} [style=dotted]; - 185 -> {186} [style=dotted]; - 186 -> {187} [style=dotted]; + 182 -> {218} [style=dotted]; + 183 -> {184}; + 184 -> {185}; + 185 -> {186 192}; + 185 -> {201} [label="onUncaughtException"]; + 186 -> {187}; 187 -> {188}; - 188 -> {189}; - 189 -> {190}; + 188 -> {189 192}; + 188 -> {201} [label="onUncaughtException"]; + 189 -> {190 192}; + 189 -> {201} [label="onUncaughtException"]; 190 -> {191}; - 191 -> {192}; + 191 -> {192 201}; 192 -> {193}; + 192 -> {201} [label="onUncaughtException"]; 193 -> {194}; 194 -> {195}; - 194 -> {202} [label="break"]; 195 -> {196}; + 195 -> {201} [label="onUncaughtException"]; 196 -> {197}; - 197 -> {198}; - 198 -> {199}; - 199 -> {200}; - 200 -> {201}; - 201 -> {166} [color=green style=dashed]; + 196 -> {201} [label="onUncaughtException"]; + 197 -> {201} [label="break"]; + 197 -> {198} [style=dotted]; + 198 -> {199} [style=dotted]; + 199 -> {200} [style=dotted]; + 200 -> {201} [style=dotted]; + 201 -> {202}; 202 -> {203}; 203 -> {204}; 204 -> {205}; 205 -> {206}; 206 -> {207}; 207 -> {208}; - - subgraph cluster_58 { - color=red - 209 [label="Enter function returnInFinally_insideTry_nonLocal" style="filled" fillcolor=red]; - subgraph cluster_59 { - color=blue - 210 [label="Enter block"]; - 211 [label="Const: Null(null)"]; - 212 [label="Variable declaration: lvar x: R|kotlin/Any?|"]; - 213 [label="Postponed enter to lambda"]; - subgraph cluster_60 { - color=blue - 214 [label="Enter function " style="filled" fillcolor=red]; - subgraph cluster_61 { - color=blue - 215 [label="Enter block"]; - subgraph cluster_62 { - color=blue - 216 [label="Try expression enter"]; - subgraph cluster_63 { - color=blue - 217 [label="Try main block enter"]; - subgraph cluster_64 { - color=blue - 218 [label="Enter block"]; - 219 [label="Access variable R|/x|"]; - 220 [label="Type operator: (R|/x| as R|B|)"]; - subgraph cluster_65 { - color=blue - 221 [label="Try expression enter"]; - subgraph cluster_66 { - color=blue - 222 [label="Try main block enter"]; - subgraph cluster_67 { - color=blue - 223 [label="Enter block"]; - 224 [label="Access variable R|/x|"]; - 225 [label="Smart cast: R|/x|"]; - 226 [label="Type operator: (R|/x| as R|A|)"]; - 227 [label="Exit block"]; - } - 228 [label="Try main block exit"]; - } - subgraph cluster_68 { - color=blue - 229 [label="Enter finally"]; - subgraph cluster_69 { - color=blue - 230 [label="Enter block"]; - 231 [label="Access variable R|/x|"]; - 232 [label="Smart cast: R|/x|"]; - 233 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 234 [label="Access variable R|/x|"]; - 235 [label="Smart cast: R|/x|"]; - 236 [label="Function call: R|/x|.R|/B.bbb|()" style="filled" fillcolor=yellow]; - 237 [label="Jump: ^returnInFinally_insideTry_nonLocal Unit"]; - 238 [label="Stub" style="filled" fillcolor=gray]; - 239 [label="Exit block" style="filled" fillcolor=gray]; - } - 240 [label="Exit finally" style="filled" fillcolor=gray]; - } - 241 [label="Try expression exit" style="filled" fillcolor=gray]; - } - 242 [label="Exit block" style="filled" fillcolor=gray]; - } - 243 [label="Try main block exit" style="filled" fillcolor=gray]; - } - subgraph cluster_70 { - color=blue - 244 [label="Enter finally"]; - subgraph cluster_71 { - color=blue - 245 [label="Enter block"]; - 246 [label="Access variable R|/x|"]; - 247 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 248 [label="Access variable R|/x|"]; - 249 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; - 250 [label="Exit block"]; - } - 251 [label="Exit finally"]; - } - 252 [label="Try expression exit" style="filled" fillcolor=gray]; - } - 253 [label="Access variable R|/x|" style="filled" fillcolor=gray]; - 254 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; - 255 [label="Access variable R|/x|" style="filled" fillcolor=gray]; - 256 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; - 257 [label="Exit block" style="filled" fillcolor=gray]; - } - 258 [label="Exit function " style="filled" fillcolor=gray]; - } - 259 [label="Postponed exit from lambda" style="filled" fillcolor=gray]; - 260 [label="Function call: R|kotlin/run#|<>(...)" style="filled" fillcolor=gray]; - 261 [label="Access variable R|/x|" style="filled" fillcolor=gray]; - 262 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; - 263 [label="Access variable R|/x|" style="filled" fillcolor=gray]; - 264 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; - 265 [label="Exit block" style="filled" fillcolor=gray]; - } - 266 [label="Exit function returnInFinally_insideTry_nonLocal" style="filled" fillcolor=red]; - } + 208 -> {209}; + 208 -> {218} [label="break"]; 209 -> {210}; 210 -> {211}; 211 -> {212}; 212 -> {213}; 213 -> {214}; - 213 -> {259 260} [style=dotted]; - 213 -> {214} [style=dashed]; 214 -> {215}; 215 -> {216}; 216 -> {217}; - 216 -> {244} [label="onUncaughtException"]; - 217 -> {218}; + 217 -> {180} [color=green style=dashed]; 218 -> {219}; 219 -> {220}; - 219 -> {244} [label="onUncaughtException"]; 220 -> {221}; - 220 -> {244} [label="onUncaughtException"]; 221 -> {222}; - 221 -> {229} [label="onUncaughtException"]; 222 -> {223}; 223 -> {224}; 224 -> {225}; - 224 -> {229} [label="onUncaughtException"]; 225 -> {226}; - 226 -> {227}; - 226 -> {229} [label="onUncaughtException"]; + + subgraph cluster_58 { + color=red + 227 [label="Enter function returnInFinally_insideTry_nonLocal" style="filled" fillcolor=red]; + subgraph cluster_59 { + color=blue + 228 [label="Enter block"]; + 229 [label="Const: Null(null)"]; + 230 [label="Variable declaration: lvar x: R|kotlin/Any?|"]; + 231 [label="Postponed enter to lambda"]; + subgraph cluster_60 { + color=blue + 232 [label="Enter function " style="filled" fillcolor=red]; + subgraph cluster_61 { + color=blue + 233 [label="Enter block"]; + subgraph cluster_62 { + color=blue + 234 [label="Try expression enter"]; + subgraph cluster_63 { + color=blue + 235 [label="Try main block enter"]; + subgraph cluster_64 { + color=blue + 236 [label="Enter block"]; + 237 [label="Access variable R|/x|"]; + 238 [label="Type operator: (R|/x| as R|B|)"]; + subgraph cluster_65 { + color=blue + 239 [label="Try expression enter"]; + subgraph cluster_66 { + color=blue + 240 [label="Try main block enter"]; + subgraph cluster_67 { + color=blue + 241 [label="Enter block"]; + 242 [label="Access variable R|/x|"]; + 243 [label="Smart cast: R|/x|"]; + 244 [label="Type operator: (R|/x| as R|A|)"]; + 245 [label="Exit block"]; + } + 246 [label="Try main block exit"]; + } + subgraph cluster_68 { + color=blue + 247 [label="Enter finally"]; + subgraph cluster_69 { + color=blue + 248 [label="Enter block"]; + 249 [label="Access variable R|/x|"]; + 250 [label="Smart cast: R|/x|"]; + 251 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 252 [label="Access variable R|/x|"]; + 253 [label="Smart cast: R|/x|"]; + 254 [label="Function call: R|/x|.R|/B.bbb|()" style="filled" fillcolor=yellow]; + 255 [label="Jump: ^returnInFinally_insideTry_nonLocal Unit"]; + 256 [label="Stub" style="filled" fillcolor=gray]; + 257 [label="Exit block" style="filled" fillcolor=gray]; + } + 258 [label="Exit finally" style="filled" fillcolor=gray]; + } + 259 [label="Try expression exit" style="filled" fillcolor=gray]; + } + 260 [label="Exit block" style="filled" fillcolor=gray]; + } + 261 [label="Try main block exit" style="filled" fillcolor=gray]; + } + subgraph cluster_70 { + color=blue + 262 [label="Enter finally"]; + subgraph cluster_71 { + color=blue + 263 [label="Enter block"]; + 264 [label="Access variable R|/x|"]; + 265 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 266 [label="Access variable R|/x|"]; + 267 [label="Function call: R|/x|.#()" style="filled" fillcolor=yellow]; + 268 [label="Exit block"]; + } + 269 [label="Exit finally"]; + } + 270 [label="Try expression exit" style="filled" fillcolor=gray]; + } + 271 [label="Access variable R|/x|" style="filled" fillcolor=gray]; + 272 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; + 273 [label="Access variable R|/x|" style="filled" fillcolor=gray]; + 274 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; + 275 [label="Exit block" style="filled" fillcolor=gray]; + } + 276 [label="Exit function " style="filled" fillcolor=gray]; + } + 277 [label="Postponed exit from lambda" style="filled" fillcolor=gray]; + 278 [label="Function call: R|kotlin/run#|<>(...)" style="filled" fillcolor=gray]; + 279 [label="Access variable R|/x|" style="filled" fillcolor=gray]; + 280 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; + 281 [label="Access variable R|/x|" style="filled" fillcolor=gray]; + 282 [label="Function call: R|/x|.#()" style="filled" fillcolor=gray]; + 283 [label="Exit block" style="filled" fillcolor=gray]; + } + 284 [label="Exit function returnInFinally_insideTry_nonLocal" style="filled" fillcolor=red]; + } 227 -> {228}; 228 -> {229}; 229 -> {230}; 230 -> {231}; 231 -> {232}; - 231 -> {244} [label="onUncaughtException"]; + 231 -> {277 278} [style=dotted]; + 231 -> {232} [style=dashed]; 232 -> {233}; 233 -> {234}; - 233 -> {244} [label="onUncaughtException"]; 234 -> {235}; - 234 -> {244} [label="onUncaughtException"]; + 234 -> {262} [label="onUncaughtException"]; 235 -> {236}; 236 -> {237}; - 236 -> {244} [label="onUncaughtException"]; - 237 -> {244} [label="return@/returnInFinally_insideTry_nonLocal"]; - 237 -> {238} [style=dotted]; - 238 -> {239} [style=dotted]; - 239 -> {240} [style=dotted]; - 240 -> {244} [style=dotted label="onUncaughtException"]; - 240 -> {241} [style=dotted]; - 241 -> {242} [style=dotted]; - 242 -> {243} [style=dotted]; - 243 -> {244} [style=dotted]; + 237 -> {238}; + 237 -> {262} [label="onUncaughtException"]; + 238 -> {239}; + 238 -> {262} [label="onUncaughtException"]; + 239 -> {240}; + 239 -> {247} [label="onUncaughtException"]; + 240 -> {241}; + 241 -> {242}; + 242 -> {243}; + 242 -> {247} [label="onUncaughtException"]; + 243 -> {244}; 244 -> {245}; + 244 -> {247} [label="onUncaughtException"]; 245 -> {246}; 246 -> {247}; 247 -> {248}; 248 -> {249}; 249 -> {250}; + 249 -> {262} [label="onUncaughtException"]; 250 -> {251}; - 251 -> {266} [label="return@/returnInFinally_insideTry_nonLocal"]; - 251 -> {252} [style=dotted]; - 252 -> {253} [style=dotted]; - 253 -> {254} [style=dotted]; - 254 -> {255} [style=dotted]; + 251 -> {252}; + 251 -> {262} [label="onUncaughtException"]; + 252 -> {253}; + 252 -> {262} [label="onUncaughtException"]; + 253 -> {254}; + 254 -> {255}; + 254 -> {262} [label="onUncaughtException"]; + 255 -> {262} [label="return@/returnInFinally_insideTry_nonLocal"]; 255 -> {256} [style=dotted]; 256 -> {257} [style=dotted]; 257 -> {258} [style=dotted]; + 258 -> {262} [style=dotted label="onUncaughtException"]; 258 -> {259} [style=dotted]; 259 -> {260} [style=dotted]; 260 -> {261} [style=dotted]; 261 -> {262} [style=dotted]; - 262 -> {263} [style=dotted]; - 263 -> {264} [style=dotted]; - 264 -> {265} [style=dotted]; - 265 -> {266} [style=dotted]; + 262 -> {263}; + 263 -> {264}; + 264 -> {265}; + 265 -> {266}; + 266 -> {267}; + 267 -> {268}; + 268 -> {269}; + 269 -> {284} [label="return@/returnInFinally_insideTry_nonLocal"]; + 269 -> {270} [style=dotted]; + 270 -> {271} [style=dotted]; + 271 -> {272} [style=dotted]; + 272 -> {273} [style=dotted]; + 273 -> {274} [style=dotted]; + 274 -> {275} [style=dotted]; + 275 -> {276} [style=dotted]; + 276 -> {277} [style=dotted]; + 277 -> {278} [style=dotted]; + 278 -> {279} [style=dotted]; + 279 -> {280} [style=dotted]; + 280 -> {281} [style=dotted]; + 281 -> {282} [style=dotted]; + 282 -> {283} [style=dotted]; + 283 -> {284} [style=dotted]; } diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithJump.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithJump.fir.kt index aee6f134a8a..ee39aedd601 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithJump.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithJump.fir.kt @@ -26,13 +26,13 @@ fun breakInTry_withNestedFinally() { x.bbb() // should be error } x.aaa() // should be error - x.bbb() // should be ok + x.bbb() // should be ok } x.aaa() // should be error - x.bbb() // should be ok + x.bbb() // should be ok } - x.aaa() // should be ok - x.bbb() // should be ok + x.aaa() // should be ok + x.bbb() // should be ok } fun returnInCatch() { @@ -46,7 +46,7 @@ fun returnInCatch() { x.aaa() // should be error x.bbb() // should be error } - x.aaa() // should be ok + x.aaa() // should be ok x.bbb() // should be error } @@ -65,13 +65,13 @@ fun returnInCatch_insideFinally() { x.bbb() // should be error x.ccc() // should be error } - x.aaa() // should be ok + x.aaa() // should be ok x.bbb() // should be error x.ccc() // should be error } - x.aaa() // should be ok + x.aaa() // should be ok x.bbb() // should be error - x.ccc() // should be ok + x.ccc() // should be ok } fun breakInCatch() { @@ -86,11 +86,11 @@ fun breakInCatch() { x.aaa() // should be error x.bbb() // should be error } - x.aaa() // should be ok + x.aaa() // should be ok x.bbb() // should be error } x.aaa() // should be error - x.bbb() // should be ok + x.bbb() // should be ok } fun returnInFinally_insideTry_nonLocal() { diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithoutCatch.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithoutCatch.fir.kt index 01937ff9cb1..40eecaee6df 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithoutCatch.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithoutCatch.fir.kt @@ -4,7 +4,7 @@ fun castInTry(s: Any) { } finally { s.length // Shouldn't be resolved } - s.length // Shouldn't be resolved + s.length // Should be smartcast } fun castInTryAndFinally(s: Any) { diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithoutCatch.kt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithoutCatch.kt index 754f1bc6ab2..abcc6575a0b 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithoutCatch.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/castInTryWithoutCatch.kt @@ -4,7 +4,7 @@ fun castInTry(s: Any) { } finally { s.length // Shouldn't be resolved } - s.length // Shouldn't be resolved + s.length // Should be smartcast } fun castInTryAndFinally(s: Any) { diff --git a/compiler/testData/diagnostics/tests/smartCasts/throwInTry.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/throwInTry.fir.kt index 1be79dc71b7..0eee350ed6a 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/throwInTry.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/throwInTry.fir.kt @@ -72,7 +72,7 @@ fun conditionalThrowInTry_rethrow_smartcastAfterTryCatchFinally(a: A) { } catch (e: Throwable) { throw e } finally {} - takeB(a) + takeB(a) } fun conditionalThrowInTry_rethrow_noSmartcastInFinally(a: A) { diff --git a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts.fir.kt index e25de2a8790..84ff3581ff6 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts.fir.kt @@ -82,7 +82,7 @@ fun test5() { s2.length } s1.length - s2.length + s2.length } fun test6(s1: String?, s2: String?) { @@ -100,6 +100,6 @@ fun test6(s1: String?, s2: String?) { requireNotNull(s2) } s.length - s1.length + s1.length s2.length } diff --git a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts_after.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts_after.fir.kt index 8be2fd96420..f0b9a982cb9 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts_after.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts_after.fir.kt @@ -84,7 +84,7 @@ fun test5() { s2.length } s1.length - s2.length + s2.length } fun test6(s1: String?, s2: String?) { @@ -102,6 +102,6 @@ fun test6(s1: String?, s2: String?) { requireNotNull(s2) } s.length - s1.length + s1.length s2.length } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/11.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/11.fir.kt index 47459b33fc2..e6e478641cc 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/11.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/11.fir.kt @@ -56,7 +56,7 @@ fun case_4() { */ fun case_5() { var x: Int? = null - if (x == try { x = 10; null } finally {} && x != null) { + if (x == try { x = 10; null } finally {} && x != null) { x x.inv() } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/12.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/12.fir.kt index 010e29518d4..68e0d7320d7 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/12.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/12.fir.kt @@ -52,5 +52,5 @@ fun case_4() { try { x = null } finally { } - x.not() + x.not() } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/15.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/15.fir.kt index 00d5f0044da..5e2740bcb39 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/15.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/15.fir.kt @@ -57,7 +57,7 @@ fun case_3() { */ fun case_4() { var x: Int? = null - if (x == try { x = 10; null } finally {} && x != null) { + if (x == try { x = 10; null } finally {} && x != null) { x x.inv() println(1) diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/24.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/24.fir.kt index 92b04018e97..dbbb9162348 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/24.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/24.fir.kt @@ -39,8 +39,8 @@ fun case_2() { try { x = null } finally { } - x - x.not() + x + x.not() } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/35.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/35.fir.kt index dcb2f38bbe4..48d08d30f4c 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/35.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/35.fir.kt @@ -32,8 +32,8 @@ fun case_2() { var x: String? x = "Test" println("${try { x = null } finally { }}") - x - x.length + x + x.length } /*