diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DataFlowVariable.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DataFlowVariable.kt index 19adc6669aa..d0cac533ffa 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DataFlowVariable.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DataFlowVariable.kt @@ -6,44 +6,77 @@ package org.jetbrains.kotlin.fir.resolve.dfa import org.jetbrains.kotlin.fir.FirElement -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.declarations.FirTypedDeclaration -import org.jetbrains.kotlin.fir.expressions.FirExpression -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.FirStub -import org.jetbrains.kotlin.fir.resolve.transformers.resultType import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.types.FirTypeRef /* * isSynthetic = false for variables that represents actual variables in fir * isSynthetic = true for complex expressions (like when expression) */ -data class DataFlowVariable( - val name: String, - val type: FirTypeRef, - val isSynthetic: Boolean -) { - override fun toString(): String { - return name +sealed class DataFlowVariable(val index: Int) { + abstract val isSynthetic: Boolean + abstract val real: DataFlowVariable + + final override fun hashCode(): Int { + return index + } + + final override fun equals(other: Any?): Boolean { + if (other !is DataFlowVariable) return false + return index == other.index + } + + final override fun toString(): String { + return "d$index" } } -class DataFlowVariableStorage(private val session: FirSession) { +private class RealDataFlowVariable(index: Int) : DataFlowVariable(index) { + override val isSynthetic: Boolean get() = false + + override val real: DataFlowVariable get() = this +} + +private class SyntheticDataFlowVariable(index: Int) : DataFlowVariable(index) { + override val isSynthetic: Boolean get() = true + + override val real: DataFlowVariable get() = this +} + +private class AliasedDataFlowVariable(index: Int, var delegate: DataFlowVariable) : DataFlowVariable(index) { + override val isSynthetic: Boolean get() = delegate.isSynthetic + + override val real: DataFlowVariable get() = delegate.real +} + + +class DataFlowVariableStorage { private val dfi2FirMap: MutableMap = mutableMapOf() private val fir2DfiMap: MutableMap = mutableMapOf() private var counter: Int = 1 - private fun getVarName(): String = "d${counter++}" - fun getOrCreateNewRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable { val fir = symbol.fir get(fir)?.let { return it } - return DataFlowVariable(getVarName(), fir.type, false).also { storeVariable(it, fir) } + return RealDataFlowVariable(counter++).also { storeVariable(it, fir) } } fun getOrCreateNewSyntheticVariable(fir: FirElement): DataFlowVariable { get(fir)?.let { return it } - return DataFlowVariable(getVarName(), fir.type, true).also { storeVariable(it, fir) } + return SyntheticDataFlowVariable(counter++).also { storeVariable(it, fir) } + } + + fun createAliasVariable(symbol: FirBasedSymbol<*>, variable: DataFlowVariable) { + createAliasVariable(symbol.fir, variable) + } + + private fun createAliasVariable(fir: FirElement, variable: DataFlowVariable) { + AliasedDataFlowVariable(counter++, variable).also { storeVariable(it, fir) } + } + + fun rebindAliasVariable(aliasVariable: DataFlowVariable, newVariable: DataFlowVariable) { + val fir = removeVariable(aliasVariable) + requireNotNull(fir) + createAliasVariable(fir, newVariable) } fun removeRealVariable(symbol: FirBasedSymbol<*>) { @@ -85,13 +118,5 @@ class DataFlowVariableStorage(private val session: FirSession) { } @Deprecated("only for debug") - fun getByName(name: String): DataFlowVariable? = dfi2FirMap.keys.firstOrNull { it.name == name } - - private val FirElement.type: FirTypeRef - get() = when (this) { - is FirExpression -> this.resultType - is FirTypedDeclaration -> this.returnTypeRef - is FirStub -> session.builtinTypes.nothingType - else -> TODO(toString()) - } + fun getByIndex(index: Int): DataFlowVariable? = dfi2FirMap.keys.firstOrNull { it.index == index } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt index 1e8b1b82ee5..3da80e9a947 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt @@ -36,12 +36,12 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF private val graphBuilder = ControlFlowGraphBuilder() private val logicSystem = LogicSystem(context) - private val variableStorage = DataFlowVariableStorage(session) + private val variableStorage = DataFlowVariableStorage() private val edges = mutableMapOf, Flow>().withDefault { Flow.EMPTY } override fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): ConeKotlinType? { val symbol: FirBasedSymbol<*> = qualifiedAccessExpression.resolvedSymbol ?: return null - val variable = variableStorage[symbol] ?: return null + val variable = variableStorage[symbol]?.real ?: return null val smartCastTypes = graphBuilder.lastNode.flow.approvedFacts(variable)?.exactType ?: return null val smartCastType = context.myIntersectTypes(smartCastTypes.toList()) ?: return null val originalType = qualifiedAccessExpression.typeRef.coneTypeSafe() ?: return null @@ -421,9 +421,28 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF override fun exitVariableDeclaration(variable: FirVariable<*>) { val node = graphBuilder.exitVariableDeclaration(variable).passFlow(false) try { - val initializerVariable = variableStorage[variable.initializer ?: return] ?: return - val realVariable = getRealVariable(variable.symbol) - node.flow = node.flow.copyNotApprovedFacts(initializerVariable, realVariable) + val initializer = variable.initializer ?: return + + /* + * That part is needed for cases like that: + * + * val b = x is String + * ... + * if (b) { + * x.length + * } + */ + variableStorage[initializer]?.let { initializerVariable -> + assert(initializerVariable.isSynthetic) + val realVariable = getRealVariable(variable.symbol) + node.flow = node.flow.copyNotApprovedFacts(initializerVariable, realVariable) + } + + initializer.resolvedSymbol?.let { initializerSymbol: FirBasedSymbol<*> -> + val rhsVariable = variableStorage[initializerSymbol]?.takeIf { !it.isSynthetic } ?: return + variableStorage.createAliasVariable(variable.symbol, rhsVariable) + } + } finally { node.flow.freeze() } @@ -431,6 +450,9 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF override fun exitVariableAssignment(assignment: FirVariableAssignment) { graphBuilder.exitVariableAssignment(assignment).passFlow() + val lhsVariable = variableStorage[assignment.resolvedSymbol ?: return] ?: return + val rhsVariable = variableStorage[assignment.rValue.resolvedSymbol ?: return]?.takeIf { !it.isSynthetic } ?: return + variableStorage.rebindAliasVariable(lhsVariable, rhsVariable) } override fun exitThrowExceptionNode(throwExpression: FirThrowExpression) { @@ -565,7 +587,7 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF } private fun getSyntheticVariable(fir: FirElement): DataFlowVariable = variableStorage.getOrCreateNewSyntheticVariable(fir) - private fun getRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable = variableStorage.getOrCreateNewRealVariable(symbol) + private fun getRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable = variableStorage.getOrCreateNewRealVariable(symbol).real private fun getVariable(fir: FirElement): DataFlowVariable { val symbol = fir.resolvedSymbol diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.cfg.dot b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.cfg.dot new file mode 100644 index 00000000000..5b1ee533d32 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.cfg.dot @@ -0,0 +1,268 @@ +digraph boundSmartcasts_kt { +subgraph foo { + 0 [shape=box label="Enter function foo"]; + 1 [shape=box label="Exit function foo"]; + + 0 -> {1}; +} + +subgraph bar { + 2 [shape=box label="Enter function bar"]; + 3 [shape=box label="Exit function bar"]; + + 2 -> {3}; +} + +subgraph test_1 { + 4 [shape=box label="Enter function test_1"]; + 5 [shape=box label="Enter block"]; + 6 [shape=box label="Access variable R|/x|"]; + 7 [shape=box label="Variable declaration: lval y: R|kotlin/Any|"]; + 8 [shape=box label="Enter when"]; + 9 [shape=box label="Enter when branch condition "]; + 10 [shape=box label="Access variable R|/x|"]; + 11 [shape=box label="Type operator: x is A"]; + 12 [shape=box label="Exit when branch condition"]; + 13 [shape=box label="Enter block"]; + 14 [shape=box label="Access variable R|/x|"]; + 15 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; + 16 [shape=box label="Access variable R|/y|"]; + 17 [shape=box label="Function call: R|/y|.R|/A.foo|()"]; + 18 [shape=box label="Exit block"]; + 19 [shape=box label="Exit when branch result"]; + 20 [shape=box label="Enter when branch condition else"]; + 21 [shape=box label="Exit when branch condition"]; + 22 [shape=box label="Enter block"]; + 23 [shape=box label="Exit block"]; + 24 [shape=box label="Exit when branch result"]; + 25 [shape=box label="Exit when"]; + 26 [shape=box label="Exit block"]; + 27 [shape=box label="Exit function test_1"]; + + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {8}; + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {12}; + 12 -> {13 20}; + 13 -> {14}; + 14 -> {15}; + 15 -> {16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19}; + 19 -> {25}; + 20 -> {21}; + 21 -> {22}; + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; +} + +subgraph test_2 { + 28 [shape=box label="Enter function test_2"]; + 29 [shape=box label="Enter block"]; + 30 [shape=box label="Access variable R|/x|"]; + 31 [shape=box label="Variable declaration: lval y: R|kotlin/Any|"]; + 32 [shape=box label="Enter when"]; + 33 [shape=box label="Enter when branch condition "]; + 34 [shape=box label="Access variable R|/y|"]; + 35 [shape=box label="Type operator: y is A"]; + 36 [shape=box label="Exit when branch condition"]; + 37 [shape=box label="Enter block"]; + 38 [shape=box label="Access variable R|/x|"]; + 39 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; + 40 [shape=box label="Access variable R|/y|"]; + 41 [shape=box label="Function call: R|/y|.R|/A.foo|()"]; + 42 [shape=box label="Exit block"]; + 43 [shape=box label="Exit when branch result"]; + 44 [shape=box label="Enter when branch condition else"]; + 45 [shape=box label="Exit when branch condition"]; + 46 [shape=box label="Enter block"]; + 47 [shape=box label="Exit block"]; + 48 [shape=box label="Exit when branch result"]; + 49 [shape=box label="Exit when"]; + 50 [shape=box label="Exit block"]; + 51 [shape=box label="Exit function test_2"]; + + 28 -> {29}; + 29 -> {30}; + 30 -> {31}; + 31 -> {32}; + 32 -> {33}; + 33 -> {34}; + 34 -> {35}; + 35 -> {36}; + 36 -> {37 44}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; + 42 -> {43}; + 43 -> {49}; + 44 -> {45}; + 45 -> {46}; + 46 -> {47}; + 47 -> {48}; + 48 -> {49}; + 49 -> {50}; + 50 -> {51}; +} + +subgraph test_3 { + 52 [shape=box label="Enter function test_3"]; + 53 [shape=box label="Enter block"]; + 54 [shape=box label="Access variable R|/x|"]; + 55 [shape=box label="Variable declaration: lvar z: R|kotlin/Any|"]; + 56 [shape=box label="Enter when"]; + 57 [shape=box label="Enter when branch condition "]; + 58 [shape=box label="Access variable R|/x|"]; + 59 [shape=box label="Type operator: x is A"]; + 60 [shape=box label="Exit when branch condition"]; + 61 [shape=box label="Enter block"]; + 62 [shape=box label="Access variable R|/z|"]; + 63 [shape=box label="Function call: R|/z|.R|/A.foo|()"]; + 64 [shape=box label="Exit block"]; + 65 [shape=box label="Exit when branch result"]; + 66 [shape=box label="Enter when branch condition else"]; + 67 [shape=box label="Exit when branch condition"]; + 68 [shape=box label="Enter block"]; + 69 [shape=box label="Exit block"]; + 70 [shape=box label="Exit when branch result"]; + 71 [shape=box label="Exit when"]; + 72 [shape=box label="Access variable R|/y|"]; + 73 [shape=box label="Assignmenet: R|/z|"]; + 74 [shape=box label="Enter when"]; + 75 [shape=box label="Enter when branch condition "]; + 76 [shape=box label="Access variable R|/y|"]; + 77 [shape=box label="Type operator: y is B"]; + 78 [shape=box label="Exit when branch condition"]; + 79 [shape=box label="Enter block"]; + 80 [shape=box label="Access variable R|/z|"]; + 81 [shape=box label="Function call: R|/z|.R|/B.bar|()"]; + 82 [shape=box label="Exit block"]; + 83 [shape=box label="Exit when branch result"]; + 84 [shape=box label="Enter when branch condition else"]; + 85 [shape=box label="Exit when branch condition"]; + 86 [shape=box label="Enter block"]; + 87 [shape=box label="Exit block"]; + 88 [shape=box label="Exit when branch result"]; + 89 [shape=box label="Exit when"]; + 90 [shape=box label="Exit block"]; + 91 [shape=box label="Exit function test_3"]; + + 52 -> {53}; + 53 -> {54}; + 54 -> {55}; + 55 -> {56}; + 56 -> {57}; + 57 -> {58}; + 58 -> {59}; + 59 -> {60}; + 60 -> {61 66}; + 61 -> {62}; + 62 -> {63}; + 63 -> {64}; + 64 -> {65}; + 65 -> {71}; + 66 -> {67}; + 67 -> {68}; + 68 -> {69}; + 69 -> {70}; + 70 -> {71}; + 71 -> {72}; + 72 -> {73}; + 73 -> {74}; + 74 -> {75}; + 75 -> {76}; + 76 -> {77}; + 77 -> {78}; + 78 -> {79 84}; + 79 -> {80}; + 80 -> {81}; + 81 -> {82}; + 82 -> {83}; + 83 -> {89}; + 84 -> {85}; + 85 -> {86}; + 86 -> {87}; + 87 -> {88}; + 88 -> {89}; + 89 -> {90}; + 90 -> {91}; +} + +subgraph test_4 { + 92 [shape=box label="Enter function test_4"]; + 93 [shape=box label="Enter block"]; + 94 [shape=box label="Const: Int(1)"]; + 95 [shape=box label="Variable declaration: lvar x: R|kotlin/Any|"]; + 96 [shape=box label="Access variable R|/x|"]; + 97 [shape=box label="Type operator: x as Int"]; + 98 [shape=box label="Access variable R|/x|"]; + 99 [shape=box label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 100 [shape=box label="Access variable R|/y|"]; + 101 [shape=box label="Assignmenet: R|/x|"]; + 102 [shape=box label="Access variable R|/x|"]; + 103 [shape=box label="Function call: R|/x|.#()"]; + 104 [shape=box label="Enter when"]; + 105 [shape=box label="Enter when branch condition "]; + 106 [shape=box label="Access variable R|/y|"]; + 107 [shape=box label="Type operator: y is A"]; + 108 [shape=box label="Exit when branch condition"]; + 109 [shape=box label="Enter block"]; + 110 [shape=box label="Access variable R|/x|"]; + 111 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; + 112 [shape=box label="Access variable R|/y|"]; + 113 [shape=box label="Function call: R|/y|.R|/A.foo|()"]; + 114 [shape=box label="Exit block"]; + 115 [shape=box label="Exit when branch result"]; + 116 [shape=box label="Enter when branch condition else"]; + 117 [shape=box label="Exit when branch condition"]; + 118 [shape=box label="Enter block"]; + 119 [shape=box label="Exit block"]; + 120 [shape=box label="Exit when branch result"]; + 121 [shape=box label="Exit when"]; + 122 [shape=box label="Exit block"]; + 123 [shape=box label="Exit function test_4"]; + + 92 -> {93}; + 93 -> {94}; + 94 -> {95}; + 95 -> {96}; + 96 -> {97}; + 97 -> {98}; + 98 -> {99}; + 99 -> {100}; + 100 -> {101}; + 101 -> {102}; + 102 -> {103}; + 103 -> {104}; + 104 -> {105}; + 105 -> {106}; + 106 -> {107}; + 107 -> {108}; + 108 -> {109 116}; + 109 -> {110}; + 110 -> {111}; + 111 -> {112}; + 112 -> {113}; + 113 -> {114}; + 114 -> {115}; + 115 -> {121}; + 116 -> {117}; + 117 -> {118}; + 118 -> {119}; + 119 -> {120}; + 120 -> {121}; + 121 -> {122}; + 122 -> {123}; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.kt b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.kt new file mode 100644 index 00000000000..bfcd44a0aa4 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.kt @@ -0,0 +1,46 @@ +interface A { + fun foo() +} + +interface B { + fun bar() +} + +fun test_1(x: Any) { + val y = x + if (x is A) { + x.foo() + y.foo() + } +} + +fun test_2(x: Any) { + val y = x + if (y is A) { + x.foo() + y.foo() + } +} + +fun test_3(x: Any, y: Any) { + var z = x + if (x is A) { + z.foo() + } + z = y + if (y is B) { + z.bar() + } +} + +fun test_4(y: Any) { + var x: Any = 1 + x as Int + x.inc() + x = y + x.inc() + if (y is A) { + x.foo() + y.foo() + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.txt b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.txt new file mode 100644 index 00000000000..b4720e4118a --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.txt @@ -0,0 +1,69 @@ +FILE: boundSmartcasts.kt + public abstract interface A : R|kotlin/Any| { + public abstract fun foo(): R|kotlin/Unit| + + } + public abstract interface B : R|kotlin/Any| { + public abstract fun bar(): R|kotlin/Unit| + + } + public final fun test_1(x: R|kotlin/Any|): R|kotlin/Unit| { + lval y: R|kotlin/Any| = R|/x| + when () { + (R|/x| is R|A|) -> { + R|/x|.R|/A.foo|() + R|/y|.R|/A.foo|() + } + else -> { + } + } + + } + public final fun test_2(x: R|kotlin/Any|): R|kotlin/Unit| { + lval y: R|kotlin/Any| = R|/x| + when () { + (R|/y| is R|A|) -> { + R|/x|.R|/A.foo|() + R|/y|.R|/A.foo|() + } + else -> { + } + } + + } + public final fun test_3(x: R|kotlin/Any|, y: R|kotlin/Any|): R|kotlin/Unit| { + lvar z: R|kotlin/Any| = R|/x| + when () { + (R|/x| is R|A|) -> { + R|/z|.R|/A.foo|() + } + else -> { + } + } + + R|/z| = R|/y| + when () { + (R|/y| is R|B|) -> { + R|/z|.R|/B.bar|() + } + else -> { + } + } + + } + public final fun test_4(y: R|kotlin/Any|): R|kotlin/Unit| { + lvar x: R|kotlin/Any| = Int(1) + (R|/x| as R|kotlin/Int|) + R|/x|.R|kotlin/Int.inc|() + R|/x| = R|/y| + R|/x|.#() + when () { + (R|/y| is R|A|) -> { + R|/x|.R|/A.foo|() + R|/y|.R|/A.foo|() + } + else -> { + } + } + + } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/when.cfg.dot b/compiler/fir/resolve/testData/resolve/smartcasts/when.cfg.dot index e5c6a9d4804..7a0bacee690 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/when.cfg.dot +++ b/compiler/fir/resolve/testData/resolve/smartcasts/when.cfg.dot @@ -300,7 +300,7 @@ subgraph test_3 { 146 [shape=box label="Exit when branch condition"]; 147 [shape=box label="Enter block"]; 148 [shape=box label="Access variable R|/x|"]; - 149 [shape=box label="Function call: R|/x|.#()"]; + 149 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; 150 [shape=box label="Access variable R|/y|"]; 151 [shape=box label="Function call: R|/y|.R|/A.foo|()"]; 152 [shape=box label="Exit block"]; @@ -310,7 +310,7 @@ subgraph test_3 { 156 [shape=box label="Exit when branch condition"]; 157 [shape=box label="Enter block"]; 158 [shape=box label="Access variable R|/x|"]; - 159 [shape=box label="Function call: R|/x|.#()"]; + 159 [shape=box label="Function call: R|/x|.R|/B.bar|()"]; 160 [shape=box label="Access variable R|/y|"]; 161 [shape=box label="Function call: R|/y|.R|/B.bar|()"]; 162 [shape=box label="Exit block"]; @@ -335,7 +335,7 @@ subgraph test_3 { 181 [shape=box label="Exit when branch condition"]; 182 [shape=box label="Enter block"]; 183 [shape=box label="Access variable R|/x|"]; - 184 [shape=box label="Function call: R|/x|.#()"]; + 184 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; 185 [shape=box label="Access variable R|/y|"]; 186 [shape=box label="Function call: R|/y|.R|/A.foo|()"]; 187 [shape=box label="Exit block"]; @@ -345,11 +345,11 @@ subgraph test_3 { 191 [shape=box label="Exit when branch condition"]; 192 [shape=box label="Enter block"]; 193 [shape=box label="Access variable R|/x|"]; - 194 [shape=box label="Function call: R|/x|.#()"]; + 194 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; 195 [shape=box label="Access variable R|/x|"]; - 196 [shape=box label="Function call: R|/x|.#()"]; + 196 [shape=box label="Function call: R|/x|.R|/B.bar|()"]; 197 [shape=box label="Access variable R|/x|"]; - 198 [shape=box label="Function call: R|/x|.#()"]; + 198 [shape=box label="Function call: R|/x|.R|kotlin/Int.inc|()"]; 199 [shape=box label="Access variable R|/y|"]; 200 [shape=box label="Function call: R|/y|.R|/A.foo|()"]; 201 [shape=box label="Access variable R|/y|"]; @@ -362,9 +362,9 @@ subgraph test_3 { 208 [shape=box label="Exit when branch condition"]; 209 [shape=box label="Enter block"]; 210 [shape=box label="Access variable R|/x|"]; - 211 [shape=box label="Function call: R|/x|.#()"]; + 211 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; 212 [shape=box label="Access variable R|/x|"]; - 213 [shape=box label="Function call: R|/x|.#()"]; + 213 [shape=box label="Function call: R|/x|.R|/B.bar|()"]; 214 [shape=box label="Access variable R|/y|"]; 215 [shape=box label="Function call: R|/y|.R|/A.foo|()"]; 216 [shape=box label="Access variable R|/y|"]; diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/when.txt b/compiler/fir/resolve/testData/resolve/smartcasts/when.txt index 398d56f733d..0a2bd3e26bd 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/when.txt +++ b/compiler/fir/resolve/testData/resolve/smartcasts/when.txt @@ -70,11 +70,11 @@ FILE: when.kt public final fun test_3(x: R|kotlin/Any?|): R|kotlin/Unit| { when (lval y: R|kotlin/Any?| = R|/x|) { ($subj$ is R|A|) -> { - R|/x|.#() + R|/x|.R|/A.foo|() R|/y|.R|/A.foo|() } ($subj$ is R|B|) -> { - R|/x|.#() + R|/x|.R|/B.bar|() R|/y|.R|/B.bar|() } else -> { @@ -85,20 +85,20 @@ FILE: when.kt ($subj$ !is R|A|) -> { } ($subj$ !is R|B|) -> { - R|/x|.#() + R|/x|.R|/A.foo|() R|/y|.R|/A.foo|() } ($subj$ is R|kotlin/Int|) -> { - R|/x|.#() - R|/x|.#() - R|/x|.#() + R|/x|.R|/A.foo|() + R|/x|.R|/B.bar|() + R|/x|.R|kotlin/Int.inc|() R|/y|.R|/A.foo|() R|/y|.R|/B.bar|() R|/y|.R|kotlin/Int.inc|() } else -> { - R|/x|.#() - R|/x|.#() + R|/x|.R|/A.foo|() + R|/x|.R|/B.bar|() R|/y|.R|/A.foo|() R|/y|.R|/B.bar|() } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java index aea185ee899..6344e489da8 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java @@ -94,6 +94,11 @@ public class FirCfgBuildingTestGenerated extends AbstractFirCfgBuildingTest { runTest("compiler/fir/resolve/testData/resolve/smartcasts/booleanOperators.kt"); } + @TestMetadata("boundSmartcasts.kt") + public void testBoundSmartcasts() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.kt"); + } + @TestMetadata("casts.kt") public void testCasts() throws Exception { runTest("compiler/fir/resolve/testData/resolve/smartcasts/casts.kt");