[FIR] Add bound smartcasts

This commit is contained in:
Dmitriy Novozhilov
2019-08-23 17:00:33 +03:00
parent e6c04c0454
commit e937e4b261
8 changed files with 484 additions and 49 deletions
@@ -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<DataFlowVariable, FirElement> = mutableMapOf()
private val fir2DfiMap: MutableMap<FirElement, DataFlowVariable> = 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 }
}
@@ -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<CFGNode<*>, 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<ConeKotlinType>() ?: 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
@@ -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|<local>/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|<local>/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|<local>/x|"];
15 [shape=box label="Function call: R|<local>/x|.R|/A.foo|()"];
16 [shape=box label="Access variable R|<local>/y|"];
17 [shape=box label="Function call: R|<local>/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|<local>/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|<local>/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|<local>/x|"];
39 [shape=box label="Function call: R|<local>/x|.R|/A.foo|()"];
40 [shape=box label="Access variable R|<local>/y|"];
41 [shape=box label="Function call: R|<local>/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|<local>/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|<local>/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|<local>/z|"];
63 [shape=box label="Function call: R|<local>/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|<local>/y|"];
73 [shape=box label="Assignmenet: R|<local>/z|"];
74 [shape=box label="Enter when"];
75 [shape=box label="Enter when branch condition "];
76 [shape=box label="Access variable R|<local>/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|<local>/z|"];
81 [shape=box label="Function call: R|<local>/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|<local>/x|"];
97 [shape=box label="Type operator: x as Int"];
98 [shape=box label="Access variable R|<local>/x|"];
99 [shape=box label="Function call: R|<local>/x|.R|kotlin/Int.inc|()"];
100 [shape=box label="Access variable R|<local>/y|"];
101 [shape=box label="Assignmenet: R|<local>/x|"];
102 [shape=box label="Access variable R|<local>/x|"];
103 [shape=box label="Function call: R|<local>/x|.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
104 [shape=box label="Enter when"];
105 [shape=box label="Enter when branch condition "];
106 [shape=box label="Access variable R|<local>/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|<local>/x|"];
111 [shape=box label="Function call: R|<local>/x|.R|/A.foo|()"];
112 [shape=box label="Access variable R|<local>/y|"];
113 [shape=box label="Function call: R|<local>/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};
}
}
@@ -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()
}
}
@@ -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|<local>/x|
when () {
(R|<local>/x| is R|A|) -> {
R|<local>/x|.R|/A.foo|()
R|<local>/y|.R|/A.foo|()
}
else -> {
}
}
}
public final fun test_2(x: R|kotlin/Any|): R|kotlin/Unit| {
lval y: R|kotlin/Any| = R|<local>/x|
when () {
(R|<local>/y| is R|A|) -> {
R|<local>/x|.R|/A.foo|()
R|<local>/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|<local>/x|
when () {
(R|<local>/x| is R|A|) -> {
R|<local>/z|.R|/A.foo|()
}
else -> {
}
}
R|<local>/z| = R|<local>/y|
when () {
(R|<local>/y| is R|B|) -> {
R|<local>/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|<local>/x| as R|kotlin/Int|)
R|<local>/x|.R|kotlin/Int.inc|()
R|<local>/x| = R|<local>/y|
R|<local>/x|.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
when () {
(R|<local>/y| is R|A|) -> {
R|<local>/x|.R|/A.foo|()
R|<local>/y|.R|/A.foo|()
}
else -> {
}
}
}
@@ -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|<local>/x|"];
149 [shape=box label="Function call: R|<local>/x|.<Unresolved name: foo>#()"];
149 [shape=box label="Function call: R|<local>/x|.R|/A.foo|()"];
150 [shape=box label="Access variable R|<local>/y|"];
151 [shape=box label="Function call: R|<local>/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|<local>/x|"];
159 [shape=box label="Function call: R|<local>/x|.<Unresolved name: bar>#()"];
159 [shape=box label="Function call: R|<local>/x|.R|/B.bar|()"];
160 [shape=box label="Access variable R|<local>/y|"];
161 [shape=box label="Function call: R|<local>/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|<local>/x|"];
184 [shape=box label="Function call: R|<local>/x|.<Unresolved name: foo>#()"];
184 [shape=box label="Function call: R|<local>/x|.R|/A.foo|()"];
185 [shape=box label="Access variable R|<local>/y|"];
186 [shape=box label="Function call: R|<local>/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|<local>/x|"];
194 [shape=box label="Function call: R|<local>/x|.<Unresolved name: foo>#()"];
194 [shape=box label="Function call: R|<local>/x|.R|/A.foo|()"];
195 [shape=box label="Access variable R|<local>/x|"];
196 [shape=box label="Function call: R|<local>/x|.<Unresolved name: bar>#()"];
196 [shape=box label="Function call: R|<local>/x|.R|/B.bar|()"];
197 [shape=box label="Access variable R|<local>/x|"];
198 [shape=box label="Function call: R|<local>/x|.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
198 [shape=box label="Function call: R|<local>/x|.R|kotlin/Int.inc|()"];
199 [shape=box label="Access variable R|<local>/y|"];
200 [shape=box label="Function call: R|<local>/y|.R|/A.foo|()"];
201 [shape=box label="Access variable R|<local>/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|<local>/x|"];
211 [shape=box label="Function call: R|<local>/x|.<Unresolved name: foo>#()"];
211 [shape=box label="Function call: R|<local>/x|.R|/A.foo|()"];
212 [shape=box label="Access variable R|<local>/x|"];
213 [shape=box label="Function call: R|<local>/x|.<Unresolved name: bar>#()"];
213 [shape=box label="Function call: R|<local>/x|.R|/B.bar|()"];
214 [shape=box label="Access variable R|<local>/y|"];
215 [shape=box label="Function call: R|<local>/y|.R|/A.foo|()"];
216 [shape=box label="Access variable R|<local>/y|"];
+8 -8
View File
@@ -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|<local>/x|) {
($subj$ is R|A|) -> {
R|<local>/x|.<Unresolved name: foo>#()
R|<local>/x|.R|/A.foo|()
R|<local>/y|.R|/A.foo|()
}
($subj$ is R|B|) -> {
R|<local>/x|.<Unresolved name: bar>#()
R|<local>/x|.R|/B.bar|()
R|<local>/y|.R|/B.bar|()
}
else -> {
@@ -85,20 +85,20 @@ FILE: when.kt
($subj$ !is R|A|) -> {
}
($subj$ !is R|B|) -> {
R|<local>/x|.<Unresolved name: foo>#()
R|<local>/x|.R|/A.foo|()
R|<local>/y|.R|/A.foo|()
}
($subj$ is R|kotlin/Int|) -> {
R|<local>/x|.<Unresolved name: foo>#()
R|<local>/x|.<Unresolved name: bar>#()
R|<local>/x|.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
R|<local>/x|.R|/A.foo|()
R|<local>/x|.R|/B.bar|()
R|<local>/x|.R|kotlin/Int.inc|()
R|<local>/y|.R|/A.foo|()
R|<local>/y|.R|/B.bar|()
R|<local>/y|.R|kotlin/Int.inc|()
}
else -> {
R|<local>/x|.<Unresolved name: foo>#()
R|<local>/x|.<Unresolved name: bar>#()
R|<local>/x|.R|/A.foo|()
R|<local>/x|.R|/B.bar|()
R|<local>/y|.R|/A.foo|()
R|<local>/y|.R|/B.bar|()
}
@@ -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");