[FIR] Add tests to catch issues with smartcasting of accesses to functions

and properties (type information is stored for the symbol and ALL accesses
to the same symbol are smartcasted).
This commit is contained in:
Mark Punzalan
2019-12-09 15:38:39 -08:00
committed by Dmitriy Novozhilov
parent f031b47363
commit 4777dd652b
9 changed files with 1738 additions and 671 deletions
@@ -846,7 +846,9 @@ class FirDataFlowAnalyzer(private val components: FirAbstractBodyResolveTransfor
val symbol = fir.resolvedSymbol ?: return null
return when {
fir is FirThisReceiverExpressionImpl -> variableStorage.getOrCreateNewThisRealVariable(symbol)
symbol is FirVariableSymbol<*> -> variableStorage.getOrCreateNewRealVariable(symbol).variableUnderAlias
symbol is FirVariableSymbol<*> ->
// TODO: Fix this for non-local properties.
variableStorage.getOrCreateNewRealVariable(symbol).variableUnderAlias
else -> null
}
}
@@ -867,6 +869,10 @@ class FirDataFlowAnalyzer(private val components: FirAbstractBodyResolveTransfor
return variableStorage[symbol]
}
// TODO: Fix this -- see broken test_3 in compiler/fir/resolve/testData/resolve/smartcasts/notBoundSmartcasts.kt
// If we have multiple local variables (could be value parameter) of the same type, there should be separate DataFlowVariables for
// accesses to a property for each distinct local variable, i.e., DataFlowVariables in storage for properties should be keyed by
// the "chain" of variable/property accesses. We also need to check that the property is not mutable and has no custom getter.
private fun getRealVariablesForSafeCallChain(call: FirExpression): Collection<RealDataFlowVariable> {
val result = mutableListOf<RealDataFlowVariable>()
@@ -0,0 +1,357 @@
digraph notBoundSmartcasts_kt {
graph [splines=ortho nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function foo" style="filled" fillcolor=red];
1 [label="Exit function foo" style="filled" fillcolor=red];
}
0 -> {1};
subgraph cluster_1 {
color=red
2 [label="Enter function foo" style="filled" fillcolor=red];
3 [label="Exit function foo" style="filled" fillcolor=red];
}
2 -> {3};
subgraph cluster_2 {
color=red
4 [label="Enter function getAny" style="filled" fillcolor=red];
5 [label="Const: Null(null)"];
6 [label="Jump: ^getAny Null(null)"];
7 [label="Stub" style="filled" fillcolor=gray];
8 [label="Exit function getAny" style="filled" fillcolor=red];
}
4 -> {5};
5 -> {6};
6 -> {8};
6 -> {7} [style=dotted];
7 -> {8} [style=dotted];
subgraph cluster_3 {
color=red
9 [label="Enter function test_0" style="filled" fillcolor=red];
10 [label="Function call: R|/getAny|()"];
11 [label="Variable declaration: lval a: R|kotlin/Any?|"];
12 [label="Function call: R|/getAny|()"];
13 [label="Variable declaration: lval b: R|kotlin/Any?|"];
14 [label="Access variable R|<local>/a|"];
15 [label="Type operator: a as A"];
16 [label="Access variable R|<local>/a|"];
17 [label="Function call: R|<local>/a|.R|/A.foo|()"];
18 [label="Access variable R|<local>/b|"];
19 [label="Type operator: b as B"];
20 [label="Access variable R|<local>/b|"];
21 [label="Function call: R|<local>/b|.R|/B.foo|()"];
22 [label="Exit function test_0" style="filled" fillcolor=red];
}
9 -> {10};
10 -> {11};
11 -> {12};
12 -> {13};
13 -> {14};
14 -> {15};
15 -> {16};
16 -> {17};
17 -> {18};
18 -> {19};
19 -> {20};
20 -> {21};
21 -> {22};
subgraph cluster_4 {
color=red
23 [label="Enter function test_1" style="filled" fillcolor=red];
24 [label="Function call: R|/getAny|()"];
25 [label="Variable declaration: lval a: R|kotlin/Any?|"];
26 [label="Function call: R|/getAny|()"];
27 [label="Variable declaration: lval b: R|kotlin/Any?|"];
subgraph cluster_5 {
color=blue
28 [label="Enter when"];
subgraph cluster_6 {
color=blue
29 [label="Enter when branch condition "];
subgraph cluster_7 {
color=blue
30 [label="Enter &&"];
31 [label="Access variable R|<local>/a|"];
32 [label="Type operator: a is A"];
33 [label="Exit left part of &&"];
34 [label="Enter right part of &&"];
35 [label="Access variable R|<local>/b|"];
36 [label="Type operator: b is B"];
37 [label="Exit &&"];
}
38 [label="Exit when branch condition"];
}
39 [label="Synthetic else branch"];
40 [label="Enter when branch result"];
subgraph cluster_8 {
color=blue
41 [label="Enter block"];
42 [label="Access variable R|<local>/a|"];
43 [label="Function call: R|<local>/a|.R|/A.foo|()"];
44 [label="Access variable R|<local>/b|"];
45 [label="Function call: R|<local>/b|.R|/B.foo|()"];
46 [label="Exit block"];
}
47 [label="Exit when branch result"];
48 [label="Exit when"];
}
49 [label="Exit function test_1" style="filled" fillcolor=red];
}
23 -> {24};
24 -> {25};
25 -> {26};
26 -> {27};
27 -> {28};
28 -> {29};
29 -> {30};
30 -> {31};
31 -> {32};
32 -> {33};
33 -> {37 34};
34 -> {35};
35 -> {36};
36 -> {37};
37 -> {38};
38 -> {40 39};
39 -> {48};
40 -> {41};
41 -> {42};
42 -> {43};
43 -> {44};
44 -> {45};
45 -> {46};
46 -> {47};
47 -> {48};
48 -> {49};
subgraph cluster_9 {
color=red
50 [label="Enter function <init>" style="filled" fillcolor=red];
51 [label="Exit function <init>" style="filled" fillcolor=red];
}
50 -> {51};
subgraph cluster_10 {
color=red
52 [label="Enter function getter" style="filled" fillcolor=red];
53 [label="Exit function getter" style="filled" fillcolor=red];
}
52 -> {53};
subgraph cluster_11 {
color=red
54 [label="Enter property" style="filled" fillcolor=red];
55 [label="Access variable R|<local>/any|"];
56 [label="Exit property" style="filled" fillcolor=red];
}
54 -> {55};
55 -> {56};
subgraph cluster_12 {
color=red
57 [label="Enter function test_2" style="filled" fillcolor=red];
subgraph cluster_13 {
color=blue
58 [label="Enter when"];
59 [label="Access variable R|<local>/d1|"];
60 [label="Access variable R|/D.any|"];
61 [label="Variable declaration: lval <elvis>: R|kotlin/Any?|"];
subgraph cluster_14 {
color=blue
62 [label="Enter when branch condition "];
63 [label="Const: Null(null)"];
64 [label="Operator =="];
65 [label="Exit when branch condition"];
}
subgraph cluster_15 {
color=blue
66 [label="Enter when branch condition else"];
67 [label="Exit when branch condition"];
}
68 [label="Enter when branch result"];
subgraph cluster_16 {
color=blue
69 [label="Enter block"];
70 [label="Access variable R|<local>/<elvis>|"];
71 [label="Exit block"];
}
72 [label="Exit when branch result"];
73 [label="Enter when branch result"];
subgraph cluster_17 {
color=blue
74 [label="Enter block"];
75 [label="Jump: ^test_2 Unit"];
76 [label="Stub" style="filled" fillcolor=gray];
77 [label="Exit block" style="filled" fillcolor=gray];
}
78 [label="Exit when branch result" style="filled" fillcolor=gray];
79 [label="Exit when"];
}
80 [label="Variable declaration: lval a: R|kotlin/Any|"];
subgraph cluster_18 {
color=blue
81 [label="Enter when"];
82 [label="Access variable R|<local>/d2|"];
83 [label="Access variable R|/D.any|"];
84 [label="Variable declaration: lval <elvis>: R|kotlin/Any|"];
subgraph cluster_19 {
color=blue
85 [label="Enter when branch condition "];
86 [label="Const: Null(null)"];
87 [label="Operator =="];
88 [label="Exit when branch condition"];
}
subgraph cluster_20 {
color=blue
89 [label="Enter when branch condition else"];
90 [label="Exit when branch condition"];
}
91 [label="Enter when branch result"];
subgraph cluster_21 {
color=blue
92 [label="Enter block"];
93 [label="Access variable R|<local>/<elvis>|"];
94 [label="Exit block"];
}
95 [label="Exit when branch result"];
96 [label="Enter when branch result"];
subgraph cluster_22 {
color=blue
97 [label="Enter block"];
98 [label="Jump: ^test_2 Unit"];
99 [label="Stub" style="filled" fillcolor=gray];
100 [label="Exit block" style="filled" fillcolor=gray];
}
101 [label="Exit when branch result" style="filled" fillcolor=gray];
102 [label="Exit when"];
}
103 [label="Variable declaration: lval b: R|kotlin/Any|"];
104 [label="Access variable R|<local>/a|"];
105 [label="Type operator: a as A"];
106 [label="Access variable R|<local>/a|"];
107 [label="Function call: R|<local>/a|.R|/A.foo|()"];
108 [label="Access variable R|<local>/b|"];
109 [label="Type operator: b as B"];
110 [label="Access variable R|<local>/b|"];
111 [label="Function call: R|<local>/b|.R|/B.foo|()"];
112 [label="Exit function test_2" style="filled" fillcolor=red];
}
57 -> {58};
58 -> {59};
59 -> {60};
60 -> {61};
61 -> {62};
62 -> {63};
63 -> {64};
64 -> {65};
65 -> {73 66};
66 -> {67};
67 -> {68};
68 -> {69};
69 -> {70};
70 -> {71};
71 -> {72};
72 -> {79};
73 -> {74};
74 -> {75};
75 -> {112};
75 -> {76} [style=dotted];
76 -> {77} [style=dotted];
77 -> {78} [style=dotted];
78 -> {79} [style=dotted];
79 -> {80};
80 -> {81};
81 -> {82};
82 -> {83};
83 -> {84};
84 -> {85};
85 -> {86};
86 -> {87};
87 -> {88};
88 -> {96 89};
89 -> {90};
90 -> {91};
91 -> {92};
92 -> {93};
93 -> {94};
94 -> {95};
95 -> {102};
96 -> {97};
97 -> {98};
98 -> {112};
98 -> {99} [style=dotted];
99 -> {100} [style=dotted];
100 -> {101} [style=dotted];
101 -> {102} [style=dotted];
102 -> {103};
103 -> {104};
104 -> {105};
105 -> {106};
106 -> {107};
107 -> {108};
108 -> {109};
109 -> {110};
110 -> {111};
111 -> {112};
subgraph cluster_23 {
color=red
113 [label="Enter function test_3" style="filled" fillcolor=red];
114 [label="Access variable R|<local>/d1|"];
115 [label="Enter safe call"];
116 [label="Access variable R|/D.any|"];
117 [label="Exit safe call"];
118 [label="Variable declaration: lval a: R|kotlin/Any?|"];
119 [label="Access variable R|<local>/d2|"];
120 [label="Enter safe call"];
121 [label="Access variable R|/D.any|"];
122 [label="Exit safe call"];
123 [label="Variable declaration: lval b: R|kotlin/Any?|"];
124 [label="Access variable R|<local>/a|"];
125 [label="Type operator: a as A"];
126 [label="Access variable R|<local>/a|"];
127 [label="Function call: R|<local>/a|.R|/A.foo|()"];
128 [label="Access variable R|<local>/b|"];
129 [label="Type operator: b as B"];
130 [label="Access variable R|<local>/b|"];
131 [label="Function call: R|<local>/b|.<Ambiguity: foo, [/A.foo, /B.foo]>#()"];
132 [label="Exit function test_3" style="filled" fillcolor=red];
}
113 -> {114};
114 -> {115 117};
115 -> {116};
116 -> {117};
117 -> {118};
118 -> {119};
119 -> {120 122};
120 -> {121};
121 -> {122};
122 -> {123};
123 -> {124};
124 -> {125};
125 -> {126};
126 -> {127};
127 -> {128};
128 -> {129};
129 -> {130};
130 -> {131};
131 -> {132};
}
@@ -0,0 +1,51 @@
// There would be ambiguities if some expression was smartcasted to (A & B) and foo() was called.
// There was a bug where 2 variables were "bound" together if they are assigned from the same function call or property.
interface A {
fun foo(): Int
}
interface B {
fun foo(): Int
}
fun getAny(): Any? = null
fun test_0() {
val a = getAny()
val b = getAny()
a as A
a.foo()
b as B
b.foo()
}
fun test_1() {
val a = getAny()
val b = getAny()
if (a is A && b is B) {
a.foo()
b.foo()
}
}
class D(val any: Any?)
fun test_2(d1: D, d2: D) {
// Elvis operator is converted into == function call
val a = d1.any ?: return
val b = d2.any ?: return
a as A
a.foo()
b as B
b.foo()
}
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_3(d1: D, d2: D) {
val a = d1?.any
val b = d2?.any
a as A
a.foo()
b as B
// Issue: b incorrectly smartcasted to (A & B)
b.<!AMBIGUITY!>foo<!>() // should be OK
}
@@ -0,0 +1,72 @@
FILE: notBoundSmartcasts.kt
public abstract interface A : R|kotlin/Any| {
public abstract fun foo(): R|kotlin/Int|
}
public abstract interface B : R|kotlin/Any| {
public abstract fun foo(): R|kotlin/Int|
}
public final fun getAny(): R|kotlin/Any?| {
^getAny Null(null)
}
public final fun test_0(): R|kotlin/Unit| {
lval a: R|kotlin/Any?| = R|/getAny|()
lval b: R|kotlin/Any?| = R|/getAny|()
(R|<local>/a| as R|A|)
R|<local>/a|.R|/A.foo|()
(R|<local>/b| as R|B|)
R|<local>/b|.R|/B.foo|()
}
public final fun test_1(): R|kotlin/Unit| {
lval a: R|kotlin/Any?| = R|/getAny|()
lval b: R|kotlin/Any?| = R|/getAny|()
when () {
(R|<local>/a| is R|A|) && (R|<local>/b| is R|B|) -> {
R|<local>/a|.R|/A.foo|()
R|<local>/b|.R|/B.foo|()
}
}
}
public final class D : R|kotlin/Any| {
public constructor(any: R|kotlin/Any?|): R|D| {
super<R|kotlin/Any|>()
}
public final val any: R|kotlin/Any?| = R|<local>/any|
public get(): R|kotlin/Any?|
}
public final fun test_2(d1: R|D|, d2: R|D|): R|kotlin/Unit| {
lval a: R|kotlin/Any| = when (lval <elvis>: R|kotlin/Any?| = R|<local>/d1|.R|/D.any|) {
==($subj$, Null(null)) -> {
^test_2 Unit
}
else -> {
R|<local>/<elvis>|
}
}
lval b: R|kotlin/Any| = when (lval <elvis>: R|kotlin/Any| = R|<local>/d2|.R|/D.any|) {
==($subj$, Null(null)) -> {
^test_2 Unit
}
else -> {
R|<local>/<elvis>|
}
}
(R|<local>/a| as R|A|)
R|<local>/a|.R|/A.foo|()
(R|<local>/b| as R|B|)
R|<local>/b|.R|/B.foo|()
}
public final fun test_3(d1: R|D|, d2: R|D|): R|kotlin/Unit| {
lval a: R|kotlin/Any?| = R|<local>/d1|?.R|/D.any|
lval b: R|kotlin/Any?| = R|<local>/d2|?.R|/D.any|
(R|<local>/a| as R|A|)
R|<local>/a|.R|/A.foo|()
(R|<local>/b| as R|B|)
R|<local>/b|.<Ambiguity: foo, [/A.foo, /B.foo]>#()
}
File diff suppressed because it is too large Load Diff
@@ -15,6 +15,21 @@ interface Q {
fun fdata(): MyData?
}
class QImpl(override val data: MyData?) : Q {
override fun fdata(): MyData? = null
}
class QImplMutable(override var data: MyData?) : Q {
override fun fdata(): MyData? = null
}
class QImplWithCustomGetter : Q {
override val data: MyData?
get() = null
override fun fdata(): MyData? = null
}
// -------------------------------------------------------------------
fun test_1(x: A?) {
@@ -45,19 +60,23 @@ fun test_4(x: A?) {
x.foo()
}
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_5(q: Q?) {
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) {
q.data
q.data.s
q.data.s.inc()
q.data // good
q.data.s // should be bad
q.data.s.inc() // should be bad
}
}
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_6(q: Q?) {
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
q?.data?.s?.inc() ?: return
q.data
q.data.s
q.data.s.inc()
q.data // good
q.data.s // should be bad
q.data.s.inc() // should be bad
}
fun test_7(q: Q?) {
@@ -117,3 +136,44 @@ fun test_10(a: Int?, b: Int?) {
}
b.<!AMBIGUITY!>inc<!>()
}
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_11(q: QImpl?, q2: QImpl) {
// `q.data` is a property with the default getter, so we CAN smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) {
q.data
q.data.s
q.data.s.inc()
// Smartcasting of `q.data` should have no effect on `q2.data`.
// Issue: Smartcasting of QImpl.data affects all instances
q2.data
q2.data.s // should be bad
q2.data.s.inc() // should be bad
if (q2.data != null) {
q2.data.s
q2.data.s.inc()
}
}
}
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_12(q: QImplWithCustomGetter?) {
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) {
q.data // good
q.data.s // should be bad
q.data.s.inc() // should be bad
}
}
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_13(q: QImplMutable?) {
// `q.data` is a property that is mutable, so we can NOT smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) {
q.data // good
q.data.s // should be bad
q.data.s.inc() // should be bad
}
}
@@ -18,6 +18,48 @@ FILE: nullability.kt
public abstract fun fdata(): R|MyData?|
}
public final class QImpl : R|Q| {
public constructor(data: R|MyData?|): R|QImpl| {
super<R|kotlin/Any|>()
}
public final override val data: R|MyData?| = R|<local>/data|
public get(): R|MyData?|
public final override fun fdata(): R|MyData?| {
^fdata Null(null)
}
}
public final class QImplMutable : R|Q| {
public constructor(data: R|MyData?|): R|QImplMutable| {
super<R|kotlin/Any|>()
}
public final override var data: R|MyData?| = R|<local>/data|
public get(): R|MyData?|
public set(value: R|MyData?|): R|kotlin/Unit|
public final override fun fdata(): R|MyData?| {
^fdata Null(null)
}
}
public final class QImplWithCustomGetter : R|Q| {
public constructor(): R|QImplWithCustomGetter| {
super<R|kotlin/Any|>()
}
public final override val data: R|MyData?|
public get(): R|MyData?| {
^ Null(null)
}
public final override fun fdata(): R|MyData?| {
^fdata Null(null)
}
}
public final fun test_1(x: R|A?|): R|kotlin/Unit| {
when () {
@@ -166,3 +208,43 @@ FILE: nullability.kt
R|<local>/b|.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
}
public final fun test_11(q: R|QImpl?|, q2: R|QImpl|): R|kotlin/Unit| {
when () {
!=(R|<local>/q|?.R|/QImpl.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> {
R|<local>/q|.R|/QImpl.data|
R|<local>/q|.R|/QImpl.data|.R|/MyData.s|
R|<local>/q|.R|/QImpl.data|.R|/MyData.s|.R|kotlin/Int.inc|()
R|<local>/q2|.R|/QImpl.data|
R|<local>/q2|.R|/QImpl.data|.R|/MyData.s|
R|<local>/q2|.R|/QImpl.data|.R|/MyData.s|.R|kotlin/Int.inc|()
when () {
!=(R|<local>/q2|.R|/QImpl.data|, Null(null)) -> {
R|<local>/q2|.R|/QImpl.data|.R|/MyData.s|
R|<local>/q2|.R|/QImpl.data|.R|/MyData.s|.R|kotlin/Int.inc|()
}
}
}
}
}
public final fun test_12(q: R|QImplWithCustomGetter?|): R|kotlin/Unit| {
when () {
!=(R|<local>/q|?.R|/QImplWithCustomGetter.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> {
R|<local>/q|.R|/QImplWithCustomGetter.data|
R|<local>/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s|
R|<local>/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s|.R|kotlin/Int.inc|()
}
}
}
public final fun test_13(q: R|QImplMutable?|): R|kotlin/Unit| {
when () {
!=(R|<local>/q|?.R|/QImplMutable.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> {
R|<local>/q|.R|/QImplMutable.data|
R|<local>/q|.R|/QImplMutable.data|.R|/MyData.s|
R|<local>/q|.R|/QImplMutable.data|.R|/MyData.s|.R|kotlin/Int.inc|()
}
}
}
@@ -188,6 +188,11 @@ public class FirDiagnosticsWithCfgTestGenerated extends AbstractFirDiagnosticsWi
runTest("compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.kt");
}
@TestMetadata("notBoundSmartcasts.kt")
public void testNotBoundSmartcasts() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/notBoundSmartcasts.kt");
}
@TestMetadata("nullability.kt")
public void testNullability() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/nullability.kt");
+4 -4
View File
@@ -37,7 +37,7 @@ FILE fqName:<root> fileName:/kt30020.kt
CALL 'public final fun plusAssign <T> (element: T of kotlin.collections.plusAssign): kotlin.Unit [inline,operator] declared in kotlin.collections' type=kotlin.Unit origin=null
<T>: kotlin.Int
$receiver: TYPE_OP type=kotlin.collections.MutableList<kotlin.Int> origin=CAST typeOperand=kotlin.collections.MutableList<kotlin.Int>
CALL 'public abstract fun <get-xs> (): kotlin.collections.MutableList<kotlin.Any> declared in <root>.X' type=kotlin.collections.MutableList<kotlin.Int> origin=null
CALL 'public abstract fun <get-xs> (): kotlin.collections.MutableList<kotlin.Any> declared in <root>.X' type=kotlin.collections.MutableList<kotlin.Any> origin=null
$this: GET_VAR 'x: <root>.X declared in <root>.test' type=<root>.X origin=null
element: CONST Int type=kotlin.Int value=3
CALL 'public final fun plusAssign <T> (element: T of kotlin.collections.plusAssign): kotlin.Unit [inline,operator] declared in kotlin.collections' type=kotlin.Unit origin=null
@@ -48,9 +48,9 @@ FILE fqName:<root> fileName:/kt30020.kt
element: CONST Int type=kotlin.Int value=4
CALL 'public final fun plusAssign <T> (element: T of kotlin.collections.plusAssign): kotlin.Unit [inline,operator] declared in kotlin.collections' type=kotlin.Unit origin=null
<T>: kotlin.Int
$receiver: CALL 'public final fun CHECK_NOT_NULL <T0> (arg0: T0 of kotlin.internal.ir.CHECK_NOT_NULL?): T0 of kotlin.internal.ir.CHECK_NOT_NULL declared in kotlin.internal.ir' type=kotlin.collections.MutableList<kotlin.Int> origin=EXCLEXCL
<T0>: kotlin.collections.MutableList<kotlin.Int>
arg0: CALL 'public abstract fun <get-xs> (): kotlin.collections.MutableList<kotlin.Any> declared in <root>.X' type=kotlin.collections.MutableList<kotlin.Int> origin=null
$receiver: CALL 'public final fun CHECK_NOT_NULL <T0> (arg0: T0 of kotlin.internal.ir.CHECK_NOT_NULL?): T0 of kotlin.internal.ir.CHECK_NOT_NULL declared in kotlin.internal.ir' type=kotlin.collections.MutableList<kotlin.Any> origin=EXCLEXCL
<T0>: kotlin.collections.MutableList<kotlin.Any>
arg0: CALL 'public abstract fun <get-xs> (): kotlin.collections.MutableList<kotlin.Any> declared in <root>.X' type=kotlin.collections.MutableList<kotlin.Any>? origin=null
$this: GET_VAR 'nx: <root>.X? declared in <root>.test' type=<root>.X? origin=null
element: CONST Int type=kotlin.Int value=5
CALL 'public final fun plusAssign <T> (element: T of kotlin.collections.plusAssign): kotlin.Unit [inline,operator] declared in kotlin.collections' type=kotlin.Unit origin=null