KT-27524 Don't box (some) inline classes in suspend fun return

If an inline class is mapped to a reference type (or an array), it's Ok
to treat JVM view on a suspend function as returning a value of
corresponding inline class (although in reality it returns 'Any?'
because of COROUTINE_SUSPENDED).
This commit is contained in:
Dmitry Petrov
2020-03-26 20:37:29 +03:00
parent d8bc29e6c6
commit 042424d599
22 changed files with 1220 additions and 7 deletions
@@ -0,0 +1,97 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
@Suppress("UNSUPPORTED_FEATURE")
inline class IC(val s: Any)
class Test1() {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(ss: IC): IC = IC(ss.s)
suspend fun <T> quz(t: T): T = t
suspend fun bar(): IC {
return foo(qux(quz(IC("OK"))))
}
suspend fun test(): Any = bar().s
}
class Test2 {
suspend fun foo(value: IC): IC {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(s: String): IC = IC(s)
suspend fun quz(): String = "OK"
suspend fun bar(): IC {
return foo(qux(quz()))
}
suspend fun test() = bar().s
}
class Test3 {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun bar(): IC {
return foo(IC("OK"))
}
suspend fun test() = bar().s
}
fun box(): String {
var result: Any = "FAIL"
builder {
result = Test1().test()
}
if (result != "OK") return "FAIL 1 $result"
result = "FAIL2"
builder {
result = Test2().test()
}
if (result != "OK") return "FAIL 2 $result"
result = "FAIL 3"
builder {
result = Test3().test()
}
return result as String
}
@@ -0,0 +1,100 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
@Suppress("UNSUPPORTED_FEATURE")
inline class I0(val x: Any)
@Suppress("UNSUPPORTED_FEATURE")
inline class IC(val s: I0)
class Test1() {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(ss: IC): IC = IC(ss.s)
suspend fun <T> quz(t: T): T = t
suspend fun bar(): IC {
return foo(qux(quz(IC(I0("OK")))))
}
suspend fun test() = bar().s.x
}
class Test2 {
suspend fun foo(value: IC): IC {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(s: String): IC = IC(I0(s))
suspend fun quz(): String = "OK"
suspend fun bar(): IC {
return foo(qux(quz()))
}
suspend fun test() = bar().s.x
}
class Test3 {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun bar(): IC {
return foo(IC(I0("OK")))
}
suspend fun test() = bar().s.x
}
fun box(): String {
var result: Any = "FAIL"
builder {
result = Test1().test()
}
if (result != "OK") return "FAIL 1 $result"
result = "FAIL2"
builder {
result = Test2().test()
}
if (result != "OK") return "FAIL 2 $result"
result = "FAIL 3"
builder {
result = Test3().test()
}
return result as String
}
@@ -0,0 +1,103 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
@Suppress("UNSUPPORTED_FEATURE")
inline class I0(val x: Int)
@Suppress("UNSUPPORTED_FEATURE")
inline class IC(val s: I0)
class Test1() {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(ss: IC): IC = IC(ss.s)
suspend fun <T> quz(t: T): T = t
suspend fun bar(): IC {
return foo(qux(quz(IC(I0(42)))))
}
suspend fun test() = bar().s.x
}
class Test2 {
suspend fun foo(value: IC): IC {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(s: Int): IC = IC(I0(s))
suspend fun quz() = 42
suspend fun bar(): IC {
return foo(qux(quz()))
}
suspend fun test() = bar().s.x
}
class Test3 {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun bar(): IC {
return foo(IC(I0(42)))
}
suspend fun test() = bar().s.x
}
fun Int.toBoxResult() =
if (this == 42) "OK" else toString()
fun box(): String {
var result: Any = "FAIL"
builder {
result = Test1().test().toBoxResult()
}
if (result != "OK") return "FAIL 1 $result"
result = "FAIL2"
builder {
result = Test2().test().toBoxResult()
}
if (result != "OK") return "FAIL 2 $result"
result = "FAIL 3"
builder {
result = Test3().test().toBoxResult()
}
return result as String
}
@@ -0,0 +1,100 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
@Suppress("UNSUPPORTED_FEATURE")
inline class IC(val s: Int)
class Test1() {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(ss: IC): IC = IC(ss.s)
suspend fun <T> quz(t: T): T = t
suspend fun bar(): IC {
return foo(qux(quz(IC(42))))
}
suspend fun test() = bar().s
}
class Test2 {
suspend fun foo(value: IC): IC {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(s: Int): IC = IC(s)
suspend fun quz(): Int = 42
suspend fun bar(): IC {
return foo(qux(quz()))
}
suspend fun test() = bar().s
}
class Test3 {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun bar(): IC {
return foo(IC(42))
}
suspend fun test() = bar().s
}
fun Int.toBoxResult() =
if (this == 42) "OK" else toString()
fun box(): String {
var result: String = "FAIL"
builder {
result = Test1().test().toBoxResult()
}
if (result != "OK") return "FAIL 1 $result"
result = "FAIL2"
builder {
result = Test2().test().toBoxResult()
}
if (result != "OK") return "FAIL 2 $result"
result = "FAIL 3"
builder {
result = Test3().test().toBoxResult()
}
return result as String
}
@@ -0,0 +1,100 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
@Suppress("UNSUPPORTED_FEATURE")
inline class IC(val s: Long)
class Test1() {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(ss: IC): IC = IC(ss.s)
suspend fun <T> quz(t: T): T = t
suspend fun bar(): IC {
return foo(qux(quz(IC(42L))))
}
suspend fun test() = bar().s
}
class Test2 {
suspend fun foo(value: IC): IC {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(s: Long): IC = IC(s)
suspend fun quz() = 42L
suspend fun bar(): IC {
return foo(qux(quz()))
}
suspend fun test() = bar().s
}
class Test3 {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun bar(): IC {
return foo(IC(42L))
}
suspend fun test() = bar().s
}
fun Long.toBoxResult() =
if (this == 42L) "OK" else toString()
fun box(): String {
var result: String = "FAIL"
builder {
result = Test1().test().toBoxResult()
}
if (result != "OK") return "FAIL 1 $result"
result = "FAIL2"
builder {
result = Test2().test().toBoxResult()
}
if (result != "OK") return "FAIL 2 $result"
result = "FAIL 3"
builder {
result = Test3().test().toBoxResult()
}
return result as String
}
@@ -0,0 +1,97 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
@Suppress("UNSUPPORTED_FEATURE")
inline class IC(val s: Any?)
class Test1() {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(ss: IC): IC = IC(ss.s)
suspend fun <T> quz(t: T): T = t
suspend fun bar(): IC {
return foo(qux(quz(IC("OK"))))
}
suspend fun test() = bar().s
}
class Test2 {
suspend fun foo(value: IC): IC {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(s: String): IC = IC(s)
suspend fun quz(): String = "OK"
suspend fun bar(): IC {
return foo(qux(quz()))
}
suspend fun test() = bar().s
}
class Test3 {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun bar(): IC {
return foo(IC("OK"))
}
suspend fun test() = bar().s
}
fun box(): String {
var result: Any? = "FAIL"
builder {
result = Test1().test()
}
if (result != "OK") return "FAIL 1 $result"
result = "FAIL2"
builder {
result = Test2().test()
}
if (result != "OK") return "FAIL 2 $result"
result = "FAIL 3"
builder {
result = Test3().test()
}
return result as String
}
@@ -0,0 +1,103 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
@Suppress("UNSUPPORTED_FEATURE")
inline class IC(val s: String)
class Test1() {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(ss: IC): IC = IC(ss.s)
suspend fun <T> quz(t: T): T = t
suspend fun bar(): IC {
run {
return foo(qux(quz(IC("OK"))))
}
}
suspend fun test() = bar().s
}
class Test2 {
suspend fun foo(value: IC): IC {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(s: String): IC = IC(s)
suspend fun quz(): String = "OK"
suspend fun bar(): IC {
run {
return foo(qux(quz()))
}
}
suspend fun test() = bar().s
}
class Test3 {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun bar(): IC {
run {
return foo(IC("OK"))
}
}
suspend fun test() = bar().s
}
fun box(): String {
var result = "FAIL"
builder {
result = Test1().test()
}
if (result != "OK") return "FAIL 1 $result"
result = "FAIL2"
builder {
result = Test2().test()
}
if (result != "OK") return "FAIL 2 $result"
result = "FAIL 3"
builder {
result = Test3().test()
}
return result
}
@@ -0,0 +1,49 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
@Suppress("UNSUPPORTED_FEATURE")
inline class IC(val s: String)
fun suspendFunId(block: suspend () -> IC) = block
class Test1() {
suspend fun <T> foo(value: T): T {
return suspendCoroutineUninterceptedOrReturn {
it.resume(value)
COROUTINE_SUSPENDED
}
}
suspend fun qux(ss: IC): IC = IC(ss.s)
suspend fun <T> quz(t: T): T = t
suspend fun bar(): IC {
return suspendFunId { foo(qux(quz(IC("OK")))) }()
}
suspend fun test() = bar().s
}
fun box(): String {
var result = "FAIL"
builder {
result = Test1().test()
}
if (result != "OK") return "FAIL 1 $result"
return result
}
@@ -0,0 +1,20 @@
// IGNORE_BACKEND: JVM_IR
inline class ICInt(val x: Int) // unbox-impl in generated 'equals'
suspend fun suspendICInt(): ICInt = ICInt(1) // box-impl
suspend fun suspendAny(): Any = ICInt(1) // box-impl
suspend fun <T> suspendGeneric(x: T): T = x
fun useICInt(x: ICInt) {}
fun useAny(x: Any) {}
suspend fun test() {
useICInt(suspendICInt()) // unbox-impl
useICInt(suspendGeneric(ICInt(1))) // box-impl, unbox-impl
useAny(suspendAny())
useAny(suspendICInt())
}
// 3 INVOKESTATIC ICInt\.box-impl
// 3 INVOKEVIRTUAL ICInt\.unbox-impl
@@ -0,0 +1,26 @@
// IGNORE_BACKEND: JVM_IR
inline class ICAny(val x: Any)
suspend fun suspendICAny(): ICAny = ICAny("")
suspend fun suspendAny(): Any = ICAny("")
suspend fun <T> suspendGeneric(x: T): T = x
fun useICAny(x: ICAny) {}
fun useAny(x: Any) {}
suspend fun test() {
useICAny(suspendICAny())
useICAny(suspendGeneric(ICAny("")))
useAny(suspendAny())
useAny(suspendICAny())
}
// -- 1 in 'suspendAny(): Any = ICAny("")'
// -- 1 in 'useAny(suspendICAny())'
// -- 1 in 'suspendGeneric(ICAny(""))'
// 3 INVOKESTATIC ICAny\.box-impl
// -- 1 in 'useICAny(suspendGeneric(ICAny("")))
// -- 1 in 'equals-impl' for ICAny
// 2 INVOKEVIRTUAL ICAny\.unbox-impl
@@ -0,0 +1,24 @@
// IGNORE_BACKEND: JVM_IR
inline class IC0(val x: Any) // IC0.unbox-impl in generated 'equals'
inline class IC1(val x: IC0) // IC1.unbox-impl in generated 'equals'
suspend fun suspendIC(): IC1 = IC1(IC0(""))
suspend fun suspendAny(): Any = IC1(IC0("")) // IC1.box-impl
suspend fun <T> suspendGeneric(x: T): T = x
fun useIC(x: IC1) {}
fun useAny(x: Any) {}
suspend fun test() {
useIC(suspendIC())
useIC(suspendGeneric(IC1(IC0("")))) // IC1.box-impl, IC1.unbox-impl
useAny(suspendAny())
useAny(suspendIC()) // IC1.box-impl
}
// 0 INVOKESTATIC IC0\.box-impl
// 3 INVOKESTATIC IC1\.box-impl
// 1 INVOKEVIRTUAL IC0\.unbox-impl
// 2 INVOKEVIRTUAL IC1\.unbox-impl