[NI] Commonize type-conversions (SAM/Suspend)
- Allow participating subtypes of functional types in conversions - Fix several subtle inconsistencies - Place logic about conversions at one place Now conversions operations have two stages: before usual subtyping check and after one. This is needed to support conversions of subtypes (of functional types, for example). First, the compiler checks if it possible to resolve an argument without conversion and only then it tries to perform conversion. Note that it'd be incorrect to perform conversion eagerly as it can change resolve (Runnable & () -> Unit <: KRunnable), plus we can't guess whether conversion is needed at all as it's important not to look into supertypes if resolution doesn't actually needed it #KT-36448 Fixed #KT-37574 Fixed #KT-38604 Fixed
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
// !LANGUAGE: -NewInference
|
||||
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
// !LANGUAGE: +SuspendConversion
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// IGNORE_BACKEND: JVM, NATIVE, JS, JS_IR
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_LIGHT_ANALYSIS
|
||||
|
||||
import helpers.*
|
||||
import kotlin.coroutines.*
|
||||
import kotlin.coroutines.intrinsics.*
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
suspend fun useSuspendFun(fn : suspend () -> String) = fn()
|
||||
suspend fun useSuspendFunInt(fn: suspend (Int) -> String) = fn(42)
|
||||
|
||||
suspend fun <T> testIntersection(x: T): String where T : () -> String, T : (Int) -> String {
|
||||
val a = useSuspendFun(x)
|
||||
val b = useSuspendFunInt(x)
|
||||
return a + b
|
||||
}
|
||||
|
||||
class Test : () -> String, (Int) -> String {
|
||||
override fun invoke(): String = "OKEmpty"
|
||||
override fun invoke(p: Int) = "OK$p"
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var test = "Failed"
|
||||
builder {
|
||||
test = testIntersection(Test())
|
||||
}
|
||||
|
||||
if (test != "OKEmptyOK42") return "failed: $test"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// !LANGUAGE: +SuspendConversion
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// IGNORE_BACKEND: JVM, NATIVE, JS, JS_IR
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_LIGHT_ANALYSIS
|
||||
|
||||
import helpers.*
|
||||
import kotlin.coroutines.*
|
||||
import kotlin.coroutines.intrinsics.*
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
suspend fun useSuspendFun(fn : suspend () -> String) = fn()
|
||||
suspend fun useSuspendFunInt(fn: suspend (Int) -> String) = fn(42)
|
||||
|
||||
class Test : () -> String, (Int) -> String {
|
||||
override fun invoke(): String = "OKEmpty"
|
||||
override fun invoke(p: Int) = "OK$p"
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var test = "Failed"
|
||||
builder {
|
||||
test = useSuspendFun(Test())
|
||||
}
|
||||
|
||||
if (test != "OKEmpty") return "failed 1"
|
||||
|
||||
builder {
|
||||
test = useSuspendFunInt(Test())
|
||||
}
|
||||
|
||||
if (test != "OK42") return "failed 2"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_BACKEND: JS, JS_IR
|
||||
|
||||
fun interface KRunnable {
|
||||
fun invoke()
|
||||
}
|
||||
|
||||
fun interface KBoolean {
|
||||
fun invoke(b: Boolean)
|
||||
}
|
||||
|
||||
fun useFunInterface(fn: KRunnable) {
|
||||
fn.invoke()
|
||||
}
|
||||
fun useFunInterfacePredicate(fn: KBoolean) {
|
||||
fn.invoke(true)
|
||||
}
|
||||
|
||||
fun <T> testIntersection(x: T) where T : () -> Unit, T : (Boolean) -> Unit {
|
||||
useFunInterface(x)
|
||||
useFunInterfacePredicate(x)
|
||||
}
|
||||
|
||||
var result = ""
|
||||
|
||||
object Test : () -> Unit, (Boolean) -> Unit {
|
||||
override fun invoke() {
|
||||
result += "O"
|
||||
}
|
||||
|
||||
override fun invoke(p1: Boolean) {
|
||||
if (p1) result += "K"
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
testIntersection(Test)
|
||||
return result
|
||||
}
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_BACKEND: JS, JS_IR
|
||||
|
||||
fun interface KRunnable {
|
||||
fun invoke()
|
||||
}
|
||||
|
||||
object OK : () -> Unit {
|
||||
override fun invoke() {
|
||||
result = "OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun foo(k: KRunnable) {
|
||||
k.invoke()
|
||||
}
|
||||
|
||||
var result: String = ""
|
||||
|
||||
fun box(): String {
|
||||
foo(OK)
|
||||
return result
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
// !LANGUAGE: -NewInference
|
||||
// WITH_RUNTIME
|
||||
// FILE: Base.java
|
||||
import java.lang.Runnable;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
|
||||
// FILE: a.kt
|
||||
|
||||
object IntMapper : (Int) -> String {
|
||||
override fun invoke(value: Int): String {
|
||||
return value.toString()
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: Sam.java
|
||||
|
||||
public interface Sam<T, R> {
|
||||
R apply(T t);
|
||||
}
|
||||
|
||||
// FILE: Foo.java
|
||||
|
||||
public class Foo {
|
||||
public static String bar1(IntMapper mapper) {
|
||||
return mapper.invoke(0);
|
||||
}
|
||||
|
||||
public static <T, R> R bar2(Sam<? super T, ? extends R> mapper, T input) {
|
||||
return mapper.apply(input);
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: test.kt
|
||||
|
||||
fun box(): String {
|
||||
val a = Foo.bar1(IntMapper)
|
||||
if (a != "0") return "Failed 0: $a"
|
||||
|
||||
val b = Foo.bar2(IntMapper, 1)
|
||||
if (b != "1") return "Failed 1: $b"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user