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 558a49b2bf2..ef04c9e2076 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 @@ -7,7 +7,10 @@ package org.jetbrains.kotlin.fir.resolve.dfa import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSymbolOwner -import org.jetbrains.kotlin.fir.contracts.description.* +import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference +import org.jetbrains.kotlin.fir.contracts.description.ConeConditionalEffectDeclaration +import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference +import org.jetbrains.kotlin.fir.contracts.description.ConeReturnsEffectDeclaration import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.impl.FirThisReceiverExpressionImpl @@ -24,10 +27,10 @@ import org.jetbrains.kotlin.fir.resolve.dfa.contracts.createArgumentsMapping import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.resolve.withNullability -import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.CallableId import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.name.FqName @@ -143,7 +146,7 @@ class FirDataFlowAnalyzer(private val components: FirAbstractBodyResolveTransfor if (typeOperatorCall.operation !in FirOperation.TYPES) return val type = typeOperatorCall.conversionTypeRef.coneTypeSafe() ?: return - val operandVariable = getOrCreateRealVariable(typeOperatorCall.argument)?.variableUnderAlias ?: return + val operandVariable = getOrCreateRealVariable(typeOperatorCall.argument) ?: return val flow = node.flow when (typeOperatorCall.operation) { @@ -339,6 +342,19 @@ class FirDataFlowAnalyzer(private val components: FirAbstractBodyResolveTransfor graphBuilder.exitJump(jump).mergeIncomingFlow() } + // ----------------------------------- Check not null call ----------------------------------- + + fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall) { + // Add `Any` to the set of possible types; the intersection type `T? & Any` will be reduced to `T` after smartcast. + val node = graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow() + val operandVariable = getOrCreateRealVariable(checkNotNullCall.argument) ?: return + logicSystem.addApprovedInfo( + node.flow, + operandVariable, + FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet()) + ) + } + // ----------------------------------- When ----------------------------------- fun enterWhenExpression(whenExpression: FirWhenExpression) { @@ -837,7 +853,7 @@ class FirDataFlowAnalyzer(private val components: FirAbstractBodyResolveTransfor if (fir is FirThisReceiverExpressionImpl) { return variableStorage.getOrCreateNewThisRealVariable(fir.calleeReference.boundSymbol ?: return null) } - val symbol: AbstractFirBasedSymbol<*> = fir.resolvedSymbol ?: return null + val symbol = fir.resolvedSymbol as? FirVariableSymbol<*> ?: return null return variableStorage.getOrCreateNewRealVariable(symbol).variableUnderAlias } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index 17ccad0a297..d17da577d7c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -125,6 +125,10 @@ class OperatorCallNode(owner: ControlFlowGraph, override val fir: FirOperatorCal class JumpNode(owner: ControlFlowGraph, override val fir: FirJump<*>, level: Int) : CFGNode>(owner, level) class ConstExpressionNode(owner: ControlFlowGraph, override val fir: FirConstExpression<*>, level: Int) : CFGNode>(owner, level) +// ----------------------------------- Check not null call ----------------------------------- + +class CheckNotNullCallNode(owner: ControlFlowGraph, override val fir: FirCheckNotNullCall, level: Int) : CFGNode(owner, level) + // ----------------------------------- Resolvable call ----------------------------------- class QualifiedAccessNode( diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index 4895c424f2e..a8673e2f1c9 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -229,6 +229,12 @@ class ControlFlowGraphBuilder { return node } + // ----------------------------------- Check not null call ----------------------------------- + + fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall): CheckNotNullCallNode { + return createCheckNotNullCallNode(checkNotNullCall).also { addNewSimpleNode(it) } + } + // ----------------------------------- When ----------------------------------- fun enterWhenExpression(whenExpression: FirWhenExpression): WhenEnterNode { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt index 1e1ea0329b8..7ec0271aa3e 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt @@ -34,6 +34,9 @@ fun ControlFlowGraphBuilder.createWhenBranchConditionExitNode(fir: FirWhenBranch fun ControlFlowGraphBuilder.createJumpNode(fir: FirJump<*>): JumpNode = JumpNode(graph, fir, levelCounter) +fun ControlFlowGraphBuilder.createCheckNotNullCallNode(fir: FirCheckNotNullCall): CheckNotNullCallNode = + CheckNotNullCallNode(graph, fir, levelCounter) + fun ControlFlowGraphBuilder.createQualifiedAccessNode(fir: FirQualifiedAccessExpression): QualifiedAccessNode = QualifiedAccessNode(graph, fir, levelCounter) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt index 038ed621319..d6f2a13838f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt @@ -110,6 +110,7 @@ fun CFGNode<*>.render(): String = is TypeOperatorCallNode -> "Type operator: \"${fir.psi?.text?.toString() ?: fir.render()}\"" is JumpNode -> "Jump: ${fir.render()}" is StubNode -> "Stub" + is CheckNotNullCallNode -> "Check not null: ${fir.render()}" is ConstExpressionNode -> "Const: ${fir.render()}" is VariableDeclarationNode -> diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index e948ecb8cb6..4f3e4129797 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -308,7 +308,7 @@ class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) : FirErrorTypeRefImpl(null, FirSimpleDiagnostic("Can't resolve !! operator call", DiagnosticKind.InferenceError)) checkNotNullCall } - // TODO: Data flow analysis + dataFlowAnalyzer.exitCheckNotNullCall(result) return result.compose() } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.dot b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.dot index 196c40b167d..0d104d9dcb2 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.dot +++ b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.dot @@ -14,55 +14,12 @@ digraph bangbang_kt { subgraph cluster_1 { color=red 2 [label="Enter function test_0" style="filled" fillcolor=red]; - subgraph cluster_2 { - color=blue - 3 [label="Enter when"]; - 4 [label="Access variable R|/a|"]; - 5 [label="Variable declaration: lval : R|A?|"]; - subgraph cluster_3 { - color=blue - 6 [label="Enter when branch condition "]; - 7 [label="Const: Null(null)"]; - 8 [label="Operator =="]; - 9 [label="Exit when branch condition"]; - } - subgraph cluster_4 { - color=blue - 10 [label="Enter when branch condition else"]; - 11 [label="Exit when branch condition"]; - } - 12 [label="Enter when branch result"]; - subgraph cluster_5 { - color=blue - 13 [label="Enter block"]; - 14 [label="Access variable R|/|"]; - 15 [label="Exit block"]; - } - 16 [label="Exit when branch result"]; - 17 [label="Enter when branch result"]; - subgraph cluster_6 { - color=blue - 18 [label="Enter block"]; - 19 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 20 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 21 [label="Stub" style="filled" fillcolor=gray]; - 22 [label="Exit block" style="filled" fillcolor=gray]; - } - 23 [label="Exit when branch result" style="filled" fillcolor=gray]; - 24 [label="Exit when"]; - } - 25 [label="Function call: when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } -} -.R|/A.foo|()"]; - 26 [label="Access variable R|/a|"]; - 27 [label="Function call: R|/a|.R|/A.foo|()"]; - 28 [label="Exit function test_0" style="filled" fillcolor=red]; + 3 [label="Access variable R|/a|"]; + 4 [label="Check not null: R|/a|!!"]; + 5 [label="Function call: R|/a|!!.R|/A.foo|()"]; + 6 [label="Access variable R|/a|"]; + 7 [label="Function call: R|/a|.R|/A.foo|()"]; + 8 [label="Exit function test_0" style="filled" fillcolor=red]; } 2 -> {3}; @@ -71,643 +28,330 @@ digraph bangbang_kt { 5 -> {6}; 6 -> {7}; 7 -> {8}; - 8 -> {9}; - 9 -> {17 10}; + + subgraph cluster_2 { + color=red + 9 [label="Enter function test_1" style="filled" fillcolor=red]; + subgraph cluster_3 { + color=blue + 10 [label="Enter when"]; + subgraph cluster_4 { + color=blue + 11 [label="Enter when branch condition "]; + 12 [label="Access variable R|/a|"]; + 13 [label="Check not null: R|/a|!!"]; + 14 [label="Function call: R|/a|!!.R|/A.foo|()"]; + 15 [label="Exit when branch condition"]; + } + 16 [label="Synthetic else branch"]; + 17 [label="Enter when branch result"]; + subgraph cluster_5 { + color=blue + 18 [label="Enter block"]; + 19 [label="Access variable R|/a|"]; + 20 [label="Function call: R|/a|.R|/A.foo|()"]; + 21 [label="Exit block"]; + } + 22 [label="Exit when branch result"]; + 23 [label="Exit when"]; + } + 24 [label="Access variable R|/a|"]; + 25 [label="Function call: R|/a|.R|/A.foo|()"]; + 26 [label="Exit function test_1" style="filled" fillcolor=red]; + } + + 9 -> {10}; 10 -> {11}; 11 -> {12}; 12 -> {13}; 13 -> {14}; 14 -> {15}; - 15 -> {16}; - 16 -> {24}; + 15 -> {17 16}; + 16 -> {23}; 17 -> {18}; 18 -> {19}; 19 -> {20}; - 20 -> {28}; - 20 -> {21} [style=dotted]; - 21 -> {22} [style=dotted]; - 22 -> {23} [style=dotted]; - 23 -> {24} [style=dotted]; + 20 -> {21}; + 21 -> {22}; + 22 -> {23}; + 23 -> {24}; 24 -> {25}; 25 -> {26}; - 26 -> {27}; - 27 -> {28}; - subgraph cluster_7 { + subgraph cluster_6 { color=red - 29 [label="Enter function test_1" style="filled" fillcolor=red]; - subgraph cluster_8 { + 27 [label="Enter function test_2" style="filled" fillcolor=red]; + subgraph cluster_7 { color=blue - 30 [label="Enter when"]; - subgraph cluster_9 { + 28 [label="Enter when"]; + subgraph cluster_8 { color=blue - 31 [label="Enter when branch condition "]; - subgraph cluster_10 { + 29 [label="Enter when branch condition "]; + subgraph cluster_9 { color=blue - 32 [label="Enter when"]; - 33 [label="Access variable R|/a|"]; - 34 [label="Variable declaration: lval : R|A?|"]; - subgraph cluster_11 { - color=blue - 35 [label="Enter when branch condition "]; - 36 [label="Const: Null(null)"]; - 37 [label="Operator =="]; - 38 [label="Exit when branch condition"]; - } - subgraph cluster_12 { - color=blue - 39 [label="Enter when branch condition else"]; - 40 [label="Exit when branch condition"]; - } - 41 [label="Enter when branch result"]; - subgraph cluster_13 { - color=blue - 42 [label="Enter block"]; - 43 [label="Access variable R|/|"]; - 44 [label="Exit block"]; - } - 45 [label="Exit when branch result"]; - 46 [label="Enter when branch result"]; - subgraph cluster_14 { - color=blue - 47 [label="Enter block"]; - 48 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 49 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 50 [label="Stub" style="filled" fillcolor=gray]; - 51 [label="Exit block" style="filled" fillcolor=gray]; - } - 52 [label="Exit when branch result" style="filled" fillcolor=gray]; - 53 [label="Exit when"]; + 30 [label="Enter &&"]; + 31 [label="Access variable R|/a|"]; + 32 [label="Check not null: R|/a|!!"]; + 33 [label="Function call: R|/a|!!.R|/A.foo|()"]; + 34 [label="Exit left part of &&"]; + 35 [label="Enter right part of &&"]; + 36 [label="Access variable R|/b|"]; + 37 [label="Exit &&"]; } - 54 [label="Function call: when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } -} -.R|/A.foo|()"]; - 55 [label="Exit when branch condition"]; + 38 [label="Exit when branch condition"]; } - 56 [label="Synthetic else branch"]; - 57 [label="Enter when branch result"]; - subgraph cluster_15 { + 39 [label="Synthetic else branch"]; + 40 [label="Enter when branch result"]; + subgraph cluster_10 { color=blue - 58 [label="Enter block"]; - 59 [label="Access variable R|/a|"]; - 60 [label="Function call: R|/a|.R|/A.foo|()"]; - 61 [label="Exit block"]; + 41 [label="Enter block"]; + 42 [label="Access variable R|/a|"]; + 43 [label="Function call: R|/a|.R|/A.foo|()"]; + 44 [label="Exit block"]; } - 62 [label="Exit when branch result"]; - 63 [label="Exit when"]; + 45 [label="Exit when branch result"]; + 46 [label="Exit when"]; } - 64 [label="Access variable R|/a|"]; - 65 [label="Function call: R|/a|.R|/A.foo|()"]; - 66 [label="Exit function test_1" style="filled" fillcolor=red]; + 47 [label="Access variable R|/a|"]; + 48 [label="Function call: R|/a|.R|/A.foo|()"]; + 49 [label="Exit function test_2" style="filled" fillcolor=red]; } + 27 -> {28}; + 28 -> {29}; 29 -> {30}; 30 -> {31}; 31 -> {32}; 32 -> {33}; 33 -> {34}; - 34 -> {35}; + 34 -> {37 35}; 35 -> {36}; 36 -> {37}; 37 -> {38}; - 38 -> {46 39}; - 39 -> {40}; + 38 -> {40 39}; + 39 -> {46}; 40 -> {41}; 41 -> {42}; 42 -> {43}; 43 -> {44}; 44 -> {45}; - 45 -> {53}; + 45 -> {46}; 46 -> {47}; 47 -> {48}; 48 -> {49}; - 49 -> {66}; - 49 -> {50} [style=dotted]; - 50 -> {51} [style=dotted]; - 51 -> {52} [style=dotted]; - 52 -> {53} [style=dotted]; + + subgraph cluster_11 { + color=red + 50 [label="Enter function test_3" style="filled" fillcolor=red]; + subgraph cluster_12 { + color=blue + 51 [label="Enter when"]; + subgraph cluster_13 { + color=blue + 52 [label="Enter when branch condition "]; + subgraph cluster_14 { + color=blue + 53 [label="Enter &&"]; + 54 [label="Access variable R|/b|"]; + 55 [label="Exit left part of &&"]; + 56 [label="Enter right part of &&"]; + 57 [label="Access variable R|/a|"]; + 58 [label="Check not null: R|/a|!!"]; + 59 [label="Function call: R|/a|!!.R|/A.foo|()"]; + 60 [label="Exit &&"]; + } + 61 [label="Exit when branch condition"]; + } + 62 [label="Synthetic else branch"]; + 63 [label="Enter when branch result"]; + subgraph cluster_15 { + color=blue + 64 [label="Enter block"]; + 65 [label="Access variable R|/a|"]; + 66 [label="Function call: R|/a|.R|/A.foo|()"]; + 67 [label="Exit block"]; + } + 68 [label="Exit when branch result"]; + 69 [label="Exit when"]; + } + 70 [label="Access variable R|/a|"]; + 71 [label="Function call: R|/a|.#()"]; + 72 [label="Exit function test_3" style="filled" fillcolor=red]; + } + + 50 -> {51}; + 51 -> {52}; + 52 -> {53}; 53 -> {54}; 54 -> {55}; - 55 -> {57 56}; - 56 -> {63}; + 55 -> {60 56}; + 56 -> {57}; 57 -> {58}; 58 -> {59}; 59 -> {60}; 60 -> {61}; - 61 -> {62}; - 62 -> {63}; + 61 -> {63 62}; + 62 -> {69}; 63 -> {64}; 64 -> {65}; 65 -> {66}; - - subgraph cluster_16 { - color=red - 67 [label="Enter function test_2" style="filled" fillcolor=red]; - subgraph cluster_17 { - color=blue - 68 [label="Enter when"]; - subgraph cluster_18 { - color=blue - 69 [label="Enter when branch condition "]; - subgraph cluster_19 { - color=blue - 70 [label="Enter &&"]; - subgraph cluster_20 { - color=blue - 71 [label="Enter when"]; - 72 [label="Access variable R|/a|"]; - 73 [label="Variable declaration: lval : R|A?|"]; - subgraph cluster_21 { - color=blue - 74 [label="Enter when branch condition "]; - 75 [label="Const: Null(null)"]; - 76 [label="Operator =="]; - 77 [label="Exit when branch condition"]; - } - subgraph cluster_22 { - color=blue - 78 [label="Enter when branch condition else"]; - 79 [label="Exit when branch condition"]; - } - 80 [label="Enter when branch result"]; - subgraph cluster_23 { - color=blue - 81 [label="Enter block"]; - 82 [label="Access variable R|/|"]; - 83 [label="Exit block"]; - } - 84 [label="Exit when branch result"]; - 85 [label="Enter when branch result"]; - subgraph cluster_24 { - color=blue - 86 [label="Enter block"]; - 87 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 88 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 89 [label="Stub" style="filled" fillcolor=gray]; - 90 [label="Exit block" style="filled" fillcolor=gray]; - } - 91 [label="Exit when branch result" style="filled" fillcolor=gray]; - 92 [label="Exit when"]; - } - 93 [label="Function call: when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } -} -.R|/A.foo|()"]; - 94 [label="Exit left part of &&"]; - 95 [label="Enter right part of &&"]; - 96 [label="Access variable R|/b|"]; - 97 [label="Exit &&"]; - } - 98 [label="Exit when branch condition"]; - } - 99 [label="Synthetic else branch"]; - 100 [label="Enter when branch result"]; - subgraph cluster_25 { - color=blue - 101 [label="Enter block"]; - 102 [label="Access variable R|/a|"]; - 103 [label="Function call: R|/a|.R|/A.foo|()"]; - 104 [label="Exit block"]; - } - 105 [label="Exit when branch result"]; - 106 [label="Exit when"]; - } - 107 [label="Access variable R|/a|"]; - 108 [label="Function call: R|/a|.R|/A.foo|()"]; - 109 [label="Exit function test_2" style="filled" fillcolor=red]; - } - + 66 -> {67}; 67 -> {68}; 68 -> {69}; 69 -> {70}; 70 -> {71}; 71 -> {72}; - 72 -> {73}; + + subgraph cluster_16 { + color=red + 73 [label="Enter function test_4" style="filled" fillcolor=red]; + subgraph cluster_17 { + color=blue + 74 [label="Enter when"]; + subgraph cluster_18 { + color=blue + 75 [label="Enter when branch condition "]; + subgraph cluster_19 { + color=blue + 76 [label="Enter ||"]; + 77 [label="Access variable R|/a|"]; + 78 [label="Check not null: R|/a|!!"]; + 79 [label="Function call: R|/a|!!.R|/A.foo|()"]; + 80 [label="Exit left part of ||"]; + 81 [label="Enter right part of ||"]; + 82 [label="Access variable R|/b|"]; + 83 [label="Exit ||"]; + } + 84 [label="Exit when branch condition"]; + } + 85 [label="Synthetic else branch"]; + 86 [label="Enter when branch result"]; + subgraph cluster_20 { + color=blue + 87 [label="Enter block"]; + 88 [label="Access variable R|/a|"]; + 89 [label="Function call: R|/a|.R|/A.foo|()"]; + 90 [label="Exit block"]; + } + 91 [label="Exit when branch result"]; + 92 [label="Exit when"]; + } + 93 [label="Access variable R|/a|"]; + 94 [label="Function call: R|/a|.R|/A.foo|()"]; + 95 [label="Exit function test_4" style="filled" fillcolor=red]; + } + 73 -> {74}; 74 -> {75}; 75 -> {76}; 76 -> {77}; - 77 -> {85 78}; + 77 -> {78}; 78 -> {79}; 79 -> {80}; - 80 -> {81}; + 80 -> {83 81}; 81 -> {82}; 82 -> {83}; 83 -> {84}; - 84 -> {92}; - 85 -> {86}; + 84 -> {86 85}; + 85 -> {92}; 86 -> {87}; 87 -> {88}; - 88 -> {109}; - 88 -> {89} [style=dotted]; - 89 -> {90} [style=dotted]; - 90 -> {91} [style=dotted]; - 91 -> {92} [style=dotted]; + 88 -> {89}; + 89 -> {90}; + 90 -> {91}; + 91 -> {92}; 92 -> {93}; 93 -> {94}; - 94 -> {97 95}; - 95 -> {96}; + 94 -> {95}; + + subgraph cluster_21 { + color=red + 96 [label="Enter function test_5" style="filled" fillcolor=red]; + subgraph cluster_22 { + color=blue + 97 [label="Enter when"]; + subgraph cluster_23 { + color=blue + 98 [label="Enter when branch condition "]; + subgraph cluster_24 { + color=blue + 99 [label="Enter ||"]; + 100 [label="Access variable R|/b|"]; + 101 [label="Exit left part of ||"]; + 102 [label="Enter right part of ||"]; + 103 [label="Access variable R|/a|"]; + 104 [label="Check not null: R|/a|!!"]; + 105 [label="Function call: R|/a|!!.R|/A.foo|()"]; + 106 [label="Exit ||"]; + } + 107 [label="Exit when branch condition"]; + } + 108 [label="Synthetic else branch"]; + 109 [label="Enter when branch result"]; + subgraph cluster_25 { + color=blue + 110 [label="Enter block"]; + 111 [label="Access variable R|/a|"]; + 112 [label="Function call: R|/a|.#()"]; + 113 [label="Exit block"]; + } + 114 [label="Exit when branch result"]; + 115 [label="Exit when"]; + } + 116 [label="Access variable R|/a|"]; + 117 [label="Function call: R|/a|.#()"]; + 118 [label="Exit function test_5" style="filled" fillcolor=red]; + } + 96 -> {97}; 97 -> {98}; - 98 -> {100 99}; - 99 -> {106}; + 98 -> {99}; + 99 -> {100}; 100 -> {101}; - 101 -> {102}; + 101 -> {106 102}; 102 -> {103}; 103 -> {104}; 104 -> {105}; 105 -> {106}; 106 -> {107}; - 107 -> {108}; - 108 -> {109}; - - subgraph cluster_26 { - color=red - 110 [label="Enter function test_3" style="filled" fillcolor=red]; - subgraph cluster_27 { - color=blue - 111 [label="Enter when"]; - subgraph cluster_28 { - color=blue - 112 [label="Enter when branch condition "]; - subgraph cluster_29 { - color=blue - 113 [label="Enter &&"]; - 114 [label="Access variable R|/b|"]; - 115 [label="Exit left part of &&"]; - 116 [label="Enter right part of &&"]; - subgraph cluster_30 { - color=blue - 117 [label="Enter when"]; - 118 [label="Access variable R|/a|"]; - 119 [label="Variable declaration: lval : R|A?|"]; - subgraph cluster_31 { - color=blue - 120 [label="Enter when branch condition "]; - 121 [label="Const: Null(null)"]; - 122 [label="Operator =="]; - 123 [label="Exit when branch condition"]; - } - subgraph cluster_32 { - color=blue - 124 [label="Enter when branch condition else"]; - 125 [label="Exit when branch condition"]; - } - 126 [label="Enter when branch result"]; - subgraph cluster_33 { - color=blue - 127 [label="Enter block"]; - 128 [label="Access variable R|/|"]; - 129 [label="Exit block"]; - } - 130 [label="Exit when branch result"]; - 131 [label="Enter when branch result"]; - subgraph cluster_34 { - color=blue - 132 [label="Enter block"]; - 133 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 134 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 135 [label="Stub" style="filled" fillcolor=gray]; - 136 [label="Exit block" style="filled" fillcolor=gray]; - } - 137 [label="Exit when branch result" style="filled" fillcolor=gray]; - 138 [label="Exit when"]; - } - 139 [label="Function call: when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } -} -.R|/A.foo|()"]; - 140 [label="Exit &&"]; - } - 141 [label="Exit when branch condition"]; - } - 142 [label="Synthetic else branch"]; - 143 [label="Enter when branch result"]; - subgraph cluster_35 { - color=blue - 144 [label="Enter block"]; - 145 [label="Access variable R|/a|"]; - 146 [label="Function call: R|/a|.R|/A.foo|()"]; - 147 [label="Exit block"]; - } - 148 [label="Exit when branch result"]; - 149 [label="Exit when"]; - } - 150 [label="Access variable R|/a|"]; - 151 [label="Function call: R|/a|.#()"]; - 152 [label="Exit function test_3" style="filled" fillcolor=red]; - } - + 107 -> {109 108}; + 108 -> {115}; + 109 -> {110}; 110 -> {111}; 111 -> {112}; 112 -> {113}; 113 -> {114}; 114 -> {115}; - 115 -> {140 116}; + 115 -> {116}; 116 -> {117}; 117 -> {118}; - 118 -> {119}; + + subgraph cluster_26 { + color=red + 119 [label="Enter function test_6" style="filled" fillcolor=red]; + 120 [label="Access variable R|/x|"]; + 121 [label="Check not null: R|/x|!!"]; + 122 [label="Function call: R|/x|!!.R|/A.foo|()"]; + 123 [label="Exit function test_6" style="filled" fillcolor=red]; + } + 119 -> {120}; 120 -> {121}; 121 -> {122}; 122 -> {123}; - 123 -> {131 124}; + + subgraph cluster_27 { + color=red + 124 [label="Enter function test_7" style="filled" fillcolor=red]; + 125 [label="Access variable R|/x|"]; + 126 [label="Check not null: R|/x|!!"]; + 127 [label="Function call: R|/x|!!.R|/A.foo|()"]; + 128 [label="Exit function test_7" style="filled" fillcolor=red]; + } + 124 -> {125}; 125 -> {126}; 126 -> {127}; 127 -> {128}; - 128 -> {129}; - 129 -> {130}; - 130 -> {138}; - 131 -> {132}; - 132 -> {133}; - 133 -> {134}; - 134 -> {152}; - 134 -> {135} [style=dotted]; - 135 -> {136} [style=dotted]; - 136 -> {137} [style=dotted]; - 137 -> {138} [style=dotted]; - 138 -> {139}; - 139 -> {140}; - 140 -> {141}; - 141 -> {143 142}; - 142 -> {149}; - 143 -> {144}; - 144 -> {145}; - 145 -> {146}; - 146 -> {147}; - 147 -> {148}; - 148 -> {149}; - 149 -> {150}; - 150 -> {151}; - 151 -> {152}; - - subgraph cluster_36 { - color=red - 153 [label="Enter function test_4" style="filled" fillcolor=red]; - subgraph cluster_37 { - color=blue - 154 [label="Enter when"]; - subgraph cluster_38 { - color=blue - 155 [label="Enter when branch condition "]; - subgraph cluster_39 { - color=blue - 156 [label="Enter ||"]; - subgraph cluster_40 { - color=blue - 157 [label="Enter when"]; - 158 [label="Access variable R|/a|"]; - 159 [label="Variable declaration: lval : R|A?|"]; - subgraph cluster_41 { - color=blue - 160 [label="Enter when branch condition "]; - 161 [label="Const: Null(null)"]; - 162 [label="Operator =="]; - 163 [label="Exit when branch condition"]; - } - subgraph cluster_42 { - color=blue - 164 [label="Enter when branch condition else"]; - 165 [label="Exit when branch condition"]; - } - 166 [label="Enter when branch result"]; - subgraph cluster_43 { - color=blue - 167 [label="Enter block"]; - 168 [label="Access variable R|/|"]; - 169 [label="Exit block"]; - } - 170 [label="Exit when branch result"]; - 171 [label="Enter when branch result"]; - subgraph cluster_44 { - color=blue - 172 [label="Enter block"]; - 173 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 174 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 175 [label="Stub" style="filled" fillcolor=gray]; - 176 [label="Exit block" style="filled" fillcolor=gray]; - } - 177 [label="Exit when branch result" style="filled" fillcolor=gray]; - 178 [label="Exit when"]; - } - 179 [label="Function call: when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } -} -.R|/A.foo|()"]; - 180 [label="Exit left part of ||"]; - 181 [label="Enter right part of ||"]; - 182 [label="Access variable R|/b|"]; - 183 [label="Exit ||"]; - } - 184 [label="Exit when branch condition"]; - } - 185 [label="Synthetic else branch"]; - 186 [label="Enter when branch result"]; - subgraph cluster_45 { - color=blue - 187 [label="Enter block"]; - 188 [label="Access variable R|/a|"]; - 189 [label="Function call: R|/a|.R|/A.foo|()"]; - 190 [label="Exit block"]; - } - 191 [label="Exit when branch result"]; - 192 [label="Exit when"]; - } - 193 [label="Access variable R|/a|"]; - 194 [label="Function call: R|/a|.R|/A.foo|()"]; - 195 [label="Exit function test_4" style="filled" fillcolor=red]; - } - - 153 -> {154}; - 154 -> {155}; - 155 -> {156}; - 156 -> {157}; - 157 -> {158}; - 158 -> {159}; - 159 -> {160}; - 160 -> {161}; - 161 -> {162}; - 162 -> {163}; - 163 -> {171 164}; - 164 -> {165}; - 165 -> {166}; - 166 -> {167}; - 167 -> {168}; - 168 -> {169}; - 169 -> {170}; - 170 -> {178}; - 171 -> {172}; - 172 -> {173}; - 173 -> {174}; - 174 -> {195}; - 174 -> {175} [style=dotted]; - 175 -> {176} [style=dotted]; - 176 -> {177} [style=dotted]; - 177 -> {178} [style=dotted]; - 178 -> {179}; - 179 -> {180}; - 180 -> {183 181}; - 181 -> {182}; - 182 -> {183}; - 183 -> {184}; - 184 -> {186 185}; - 185 -> {192}; - 186 -> {187}; - 187 -> {188}; - 188 -> {189}; - 189 -> {190}; - 190 -> {191}; - 191 -> {192}; - 192 -> {193}; - 193 -> {194}; - 194 -> {195}; - - subgraph cluster_46 { - color=red - 196 [label="Enter function test_5" style="filled" fillcolor=red]; - subgraph cluster_47 { - color=blue - 197 [label="Enter when"]; - subgraph cluster_48 { - color=blue - 198 [label="Enter when branch condition "]; - subgraph cluster_49 { - color=blue - 199 [label="Enter ||"]; - 200 [label="Access variable R|/b|"]; - 201 [label="Exit left part of ||"]; - 202 [label="Enter right part of ||"]; - subgraph cluster_50 { - color=blue - 203 [label="Enter when"]; - 204 [label="Access variable R|/a|"]; - 205 [label="Variable declaration: lval : R|A?|"]; - subgraph cluster_51 { - color=blue - 206 [label="Enter when branch condition "]; - 207 [label="Const: Null(null)"]; - 208 [label="Operator =="]; - 209 [label="Exit when branch condition"]; - } - subgraph cluster_52 { - color=blue - 210 [label="Enter when branch condition else"]; - 211 [label="Exit when branch condition"]; - } - 212 [label="Enter when branch result"]; - subgraph cluster_53 { - color=blue - 213 [label="Enter block"]; - 214 [label="Access variable R|/|"]; - 215 [label="Exit block"]; - } - 216 [label="Exit when branch result"]; - 217 [label="Enter when branch result"]; - subgraph cluster_54 { - color=blue - 218 [label="Enter block"]; - 219 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 220 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; - 221 [label="Stub" style="filled" fillcolor=gray]; - 222 [label="Exit block" style="filled" fillcolor=gray]; - } - 223 [label="Exit when branch result" style="filled" fillcolor=gray]; - 224 [label="Exit when"]; - } - 225 [label="Function call: when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } -} -.R|/A.foo|()"]; - 226 [label="Exit ||"]; - } - 227 [label="Exit when branch condition"]; - } - 228 [label="Synthetic else branch"]; - 229 [label="Enter when branch result"]; - subgraph cluster_55 { - color=blue - 230 [label="Enter block"]; - 231 [label="Access variable R|/a|"]; - 232 [label="Function call: R|/a|.#()"]; - 233 [label="Exit block"]; - } - 234 [label="Exit when branch result"]; - 235 [label="Exit when"]; - } - 236 [label="Access variable R|/a|"]; - 237 [label="Function call: R|/a|.#()"]; - 238 [label="Exit function test_5" style="filled" fillcolor=red]; - } - - 196 -> {197}; - 197 -> {198}; - 198 -> {199}; - 199 -> {200}; - 200 -> {201}; - 201 -> {226 202}; - 202 -> {203}; - 203 -> {204}; - 204 -> {205}; - 205 -> {206}; - 206 -> {207}; - 207 -> {208}; - 208 -> {209}; - 209 -> {217 210}; - 210 -> {211}; - 211 -> {212}; - 212 -> {213}; - 213 -> {214}; - 214 -> {215}; - 215 -> {216}; - 216 -> {224}; - 217 -> {218}; - 218 -> {219}; - 219 -> {220}; - 220 -> {238}; - 220 -> {221} [style=dotted]; - 221 -> {222} [style=dotted]; - 222 -> {223} [style=dotted]; - 223 -> {224} [style=dotted]; - 224 -> {225}; - 225 -> {226}; - 226 -> {227}; - 227 -> {229 228}; - 228 -> {235}; - 229 -> {230}; - 230 -> {231}; - 231 -> {232}; - 232 -> {233}; - 233 -> {234}; - 234 -> {235}; - 235 -> {236}; - 236 -> {237}; - 237 -> {238}; } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.kt b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.kt index 5dfa1c4d698..50b2badee5d 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.kt +++ b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.kt @@ -40,4 +40,12 @@ fun test_5(a: A?, b: Boolean) { a.foo() } a.foo() +} + +fun test_6(x: X) { + x!!.foo() +} + +fun test_7(x: X?) { + x!!.foo() } \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.txt b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.txt index 3f62be93b68..e8fe8566705 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.txt +++ b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.txt @@ -4,28 +4,12 @@ FILE: bangbang.kt } public final fun test_0(a: R|A?|): R|kotlin/Unit| { - when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } - } - .R|/A.foo|() + R|/a|!!.R|/A.foo|() R|/a|.R|/A.foo|() } public final fun test_1(a: R|A?|): R|kotlin/Unit| { when () { - when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } - } - .R|/A.foo|() -> { + R|/a|!!.R|/A.foo|() -> { R|/a|.R|/A.foo|() } } @@ -34,15 +18,7 @@ FILE: bangbang.kt } public final fun test_2(a: R|A?|, b: R|kotlin/Boolean|): R|kotlin/Unit| { when () { - when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } - } - .R|/A.foo|() && R|/b| -> { + R|/a|!!.R|/A.foo|() && R|/b| -> { R|/a|.R|/A.foo|() } } @@ -51,15 +27,7 @@ FILE: bangbang.kt } public final fun test_3(a: R|A?|, b: R|kotlin/Boolean|): R|kotlin/Unit| { when () { - R|/b| && when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } - } - .R|/A.foo|() -> { + R|/b| && R|/a|!!.R|/A.foo|() -> { R|/a|.R|/A.foo|() } } @@ -68,15 +36,7 @@ FILE: bangbang.kt } public final fun test_4(a: R|A?|, b: R|kotlin/Boolean|): R|kotlin/Unit| { when () { - when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } - } - .R|/A.foo|() || R|/b| -> { + R|/a|!!.R|/A.foo|() || R|/b| -> { R|/a|.R|/A.foo|() } } @@ -85,18 +45,16 @@ FILE: bangbang.kt } public final fun test_5(a: R|A?|, b: R|kotlin/Boolean|): R|kotlin/Unit| { when () { - R|/b| || when (lval : R|A?| = R|/a|) { - ==($subj$, Null(null)) -> { - throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() - } - else -> { - R|/| - } - } - .R|/A.foo|() -> { + R|/b| || R|/a|!!.R|/A.foo|() -> { R|/a|.#() } } R|/a|.#() } + public final fun test_6(x: R|X|): R|kotlin/Unit| { + R|/x|!!.R|/A.foo|() + } + public final fun test_7(x: R|X?|): R|kotlin/Unit| { + R|/x|!!.R|/A.foo|() + }