FIR CFG: do not assume all arguments to inline funs are called in place

Only values for non-noinline, non-crossinline, functional type
parameters qualify.
This commit is contained in:
pyos
2022-11-23 16:02:11 +01:00
committed by teamcity
parent 39f88b9e87
commit d66be3f82d
17 changed files with 165 additions and 112 deletions
@@ -0,0 +1,55 @@
FILE: inlineLambdasAreInplace.kt
public final inline fun callsInPlaceInline(x: R|() -> kotlin/Unit|): R|kotlin/Unit| {
^callsInPlaceInline R|<local>/x|.R|SubstitutionOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
public final inline fun <T> any(x: R|T|): R|T| {
^any R|<local>/x|
}
public final inline fun noinline(noinline x: R|() -> kotlin/Unit|): R|() -> kotlin/Unit| {
^noinline R|<local>/x|
}
public final inline fun crossinline(crossinline x: R|() -> kotlin/Unit|): R|() -> kotlin/Unit| {
^crossinline fun <anonymous>(): R|kotlin/Unit| <inline=Unknown> {
R|<local>/x|.R|SubstitutionOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
}
public final fun testInline(): R|kotlin/Unit| {
lvar x: R|kotlin/String?| = Null(null)
R|/callsInPlaceInline|(<L> = callsInPlaceInline@fun <anonymous>(): R|kotlin/Unit| <inline=Inline, kind=UNKNOWN> {
R|<local>/x| = Null(null)
}
)
R|<local>/x| = String()
R|<local>/x|.R|kotlin/String.length|
}
public final fun testGeneric(): R|kotlin/Unit| {
lvar x: R|kotlin/String?| = String()
lval lambda: R|() -> kotlin/Unit| = R|/any|<R|() -> kotlin/Unit|>(<L> = any@fun <anonymous>(): R|kotlin/Unit| <inline=Inline> {
R|<local>/x| = Null(null)
}
)
R|<local>/x| = String()
R|<local>/lambda|.R|SubstitutionOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
R|<local>/x|.<Inapplicable(UNSTABLE_SMARTCAST): kotlin/String.length>#
}
public final fun testNoinline(): R|kotlin/Unit| {
lvar x: R|kotlin/String?| = String()
lval lambda: R|() -> kotlin/Unit| = R|/noinline|(<L> = noinline@fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|<local>/x| = Null(null)
}
)
R|<local>/x| = String()
R|<local>/lambda|.R|SubstitutionOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
R|<local>/x|.<Inapplicable(UNSTABLE_SMARTCAST): kotlin/String.length>#
}
public final fun testCrossinline(): R|kotlin/Unit| {
lvar x: R|kotlin/String?| = String()
lval lambda: R|() -> kotlin/Unit| = R|/crossinline|(<L> = crossinline@fun <anonymous>(): R|kotlin/Unit| <inline=CrossInline> {
R|<local>/x| = Null(null)
}
)
R|<local>/x| = String()
R|<local>/lambda|.R|SubstitutionOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
R|<local>/x|.<Inapplicable(UNSTABLE_SMARTCAST): kotlin/String.length>#
}
@@ -0,0 +1,38 @@
inline fun callsInPlaceInline(x: () -> Unit) = x()
<!NOTHING_TO_INLINE!>inline<!> fun <T> any(x: T) = x
<!NOTHING_TO_INLINE!>inline<!> fun noinline(noinline x: () -> Unit) = x
inline fun crossinline(crossinline x: () -> Unit) = { x() }
fun testInline() {
var x: String? = null
callsInPlaceInline { x = null }
x = ""
x.length // ok
}
fun testGeneric() {
var x: String? = ""
val lambda = any { x = null }
x = ""
lambda()
<!SMARTCAST_IMPOSSIBLE!>x<!>.length // bad
}
fun testNoinline() {
var x: String? = ""
val lambda = noinline { x = null }
x = ""
lambda()
<!SMARTCAST_IMPOSSIBLE!>x<!>.length // bad
}
fun testCrossinline() {
var x: String? = ""
val lambda = crossinline { x = null }
x = ""
lambda()
<!SMARTCAST_IMPOSSIBLE!>x<!>.length // bad
}
@@ -50,13 +50,11 @@ digraph postponedLambdas_kt {
8 -> {9 13 10};
8 -> {13} [style=dashed];
9 -> {10};
9 -> {8} [color=green style=dashed];
10 -> {11};
11 -> {12};
13 -> {14};
14 -> {15};
15 -> {16};
16 -> {17};
17 -> {9};
}
@@ -2,7 +2,7 @@ FILE: postponedLambdas.kt
public final inline fun foo(vararg x: R|kotlin/Array<out kotlin/Any>|): R|kotlin/Unit| {
}
public final fun test(a: R|kotlin/Any|, b: R|kotlin/Any|, c: R|kotlin/Any|): R|kotlin/Unit| {
R|/foo|(vararg(R|<local>/a|, foo@fun <anonymous>(): R|kotlin/String| <inline=Unknown, kind=UNKNOWN> {
R|/foo|(vararg(R|<local>/a|, foo@fun <anonymous>(): R|kotlin/String| <inline=Unknown> {
^ String()
}
, R|<local>/b|))
@@ -47,13 +47,13 @@ FILE: lambdaWithoutExpectedType.kt
public abstract interface XdIssueFolder : R|kotlin/Any| {
}
public final fun test(): R|kotlin/Unit| {
lval array: R|kotlin/Array<kotlin/Any>| = R|kotlin/arrayOf|<R|kotlin/Any|>(vararg(String(Project), arrayOf@fun <anonymous>(): R|XdProject?| <inline=Unknown, kind=UNKNOWN> {
lval array: R|kotlin/Array<kotlin/Any>| = R|kotlin/arrayOf|<R|kotlin/Any|>(vararg(String(Project), arrayOf@fun <anonymous>(): R|XdProject?| <inline=Unknown> {
^ Q|XdProject|.R|/filter|<R|XdProject|>(<L> = filter@fun R|FilteringContext|.<anonymous>(it: R|XdProject|): R|XdSearchingNode| <inline=NoInline> {
^ (this@R|special/anonymous|, R|<local>/it|.R|/XdProject.name|).R|/FilteringContext.eq|<R|kotlin/String|>(String(foo))
}
).R|/firstOrNull|<R|XdProject|>()
}
, arrayOf@fun <anonymous>(<unused var>: R|XdIssue|, <unused var>: R|XdIssueFolder|): R|kotlin/Unit| <inline=Unknown, kind=UNKNOWN> {
, arrayOf@fun <anonymous>(<unused var>: R|XdIssue|, <unused var>: R|XdIssueFolder|): R|kotlin/Unit| <inline=Unknown> {
^@arrayOf Unit
}
))