From 0e2b3ce84545d10b20a6ad3757c298b740c8d3a1 Mon Sep 17 00:00:00 2001 From: Brian Norman Date: Thu, 20 Jul 2023 07:05:53 -0500 Subject: [PATCH] [FIR] Create alternate DFA flows through `finally` blocks Entering a `finally` block can happen from many different places: through an exception, a jump, or normal exit from the `try` block. When in the `finally` block, all DFA flows must be merged to have correct smart casting. However, after the `finally` block, if exiting normally or because of a jump, the combined flow from within the `finally` block should not be used, but rather an alternate flow which combines the correct flows from before the `finally` block. ``` try { str as String // Potential cast exception } finally { str.length // Shouldn`t be resolved } str.length // Should be resolved ``` When building DFA flows, track the start of possible alternate flows, and continue building them until they end. Both of these situations are now marked on CFGNodes via interfaces. When building the default DFA flow, and the source node is the end node of alternate flows, attempt to use the alternate flow with the same edge label instead of the default flow of the source node. #KT-56888 Fixed --- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 107 +- .../kotlin/fir/resolve/dfa/FlowPath.kt | 10 +- .../kotlin/fir/resolve/dfa/cfg/CFGNode.kt | 26 +- .../breakContinueInTryFinallyInLoop.fir.kt | 23 + .../breakContinueInTryFinallyInLoop.kt | 1 - .../castchecks/castInTryWithCatch.fir.kt | 2 +- .../castchecks/castInTryWithJump.dot | 1006 +++++++++-------- .../castchecks/castInTryWithJump.fir.kt | 20 +- .../castchecks/castInTryWithoutCatch.fir.kt | 2 +- .../castchecks/castInTryWithoutCatch.kt | 2 +- .../tests/smartCasts/throwInTry.fir.kt | 2 +- .../tryCatch/falsePositiveSmartcasts.fir.kt | 4 +- .../falsePositiveSmartcasts_after.fir.kt | 4 +- .../diagnostics/notLinked/dfa/neg/11.fir.kt | 2 +- .../diagnostics/notLinked/dfa/neg/12.fir.kt | 2 +- .../diagnostics/notLinked/dfa/neg/15.fir.kt | 2 +- .../diagnostics/notLinked/dfa/neg/24.fir.kt | 4 +- .../diagnostics/notLinked/dfa/neg/35.fir.kt | 4 +- 18 files changed, 677 insertions(+), 546 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinallyInLoop.fir.kt 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 } /*