Use captured type approximation from new inference in ExpressionCodegen

This is needed in order to avoid star projections being expanded into
`out Any?`, which is visible for users of `typeOf` since 1.4.

 #KT-30278 Fixed
This commit is contained in:
Alexander Udalov
2020-02-03 17:41:26 +01:00
parent 4d1937b92d
commit 9f9eef44b1
16 changed files with 153 additions and 195 deletions
+5 -4
View File
@@ -1,6 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// !LANGUAGE: +NewInference
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JS, JS_IR, NATIVE, JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
package test
@@ -25,12 +26,12 @@ fun box(): String {
check("test.C?", typeOf<C?>())
check("kotlin.collections.List<kotlin.String>", typeOf<List<String>>())
check("kotlin.collections.Map<in kotlin.Number, kotlin.Any?>?", typeOf<Map<in Number, *>?>())
check("kotlin.Enum<out kotlin.Enum<*>>", typeOf<Enum<*>>())
check("kotlin.collections.Map<in kotlin.Number, *>?", typeOf<Map<in Number, *>?>())
check("kotlin.Enum<*>", typeOf<Enum<*>>())
check("kotlin.Enum<kotlin.annotation.AnnotationRetention>", typeOf<Enum<AnnotationRetention>>())
check("kotlin.Array<kotlin.Any>", typeOf<Array<Any>>())
check("kotlin.Array<out kotlin.Any?>", typeOf<Array<*>>())
check("kotlin.Array<*>", typeOf<Array<*>>())
check("kotlin.Array<kotlin.IntArray>", typeOf<Array<IntArray>>())
check("kotlin.Array<in kotlin.Array<test.C>?>", typeOf<Array<in Array<C>?>>())
@@ -1,42 +0,0 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_REFLECT
package test
import kotlin.reflect.KType
import kotlin.reflect.typeOf
import kotlin.test.assertEquals
class C
fun check(expected: String, actual: KType) {
assertEquals(expected, actual.toString())
}
fun box(): String {
check("kotlin.Any", typeOf<Any>())
check("kotlin.String", typeOf<String>())
check("kotlin.String?", typeOf<String?>())
check("kotlin.Unit", typeOf<Unit>())
check("test.C", typeOf<C>())
check("test.C?", typeOf<C?>())
check("kotlin.collections.List<kotlin.String>", typeOf<List<String>>())
check("kotlin.collections.Map<in kotlin.Number, *>?", typeOf<Map<in Number, *>?>())
check("kotlin.Enum<*>", typeOf<Enum<*>>())
check("kotlin.Enum<kotlin.annotation.AnnotationRetention>", typeOf<Enum<AnnotationRetention>>())
check("kotlin.Array<kotlin.Any>", typeOf<Array<Any>>())
check("kotlin.Array<*>", typeOf<Array<*>>())
check("kotlin.Array<kotlin.IntArray>", typeOf<Array<IntArray>>())
check("kotlin.Array<in kotlin.Array<test.C>?>", typeOf<Array<in Array<C>?>>())
check("kotlin.Int", typeOf<Int>())
check("kotlin.Int?", typeOf<Int?>())
check("kotlin.Boolean", typeOf<Boolean>())
return "OK"
}
@@ -0,0 +1,42 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// !LANGUAGE: +NewInference
// TARGET_BACKEND: JS
// WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
import kotlin.reflect.typeOf
import kotlin.reflect.KClass
import kotlin.reflect.KType
// TODO: deduplicate this test with ../typeOfCapturedStar.kt after fixing KType.toString in JS to return qualified name instead of simple
interface KFunction<out R>
val <T : Any> KClass<T>.primaryConstructor0: KFunction<T>
get() = object : KFunction<T> {}
interface A<out T : Any> {
val t: T
}
inline fun <reified T : KFunction<E>, E : Any> bar(w: A<E>): Pair<KType, KFunction<E>> {
val q: KFunction<E> = w.t::class.primaryConstructor0
return typeOf<T>() to q
}
inline fun <reified Q> typeOfValue(q: Q): KType {
return typeOf<Q>()
}
fun box(): String {
val q: A<*> = object : A<CharSequence> {
override val t: CharSequence
get() = ""
}
val (w, f) = bar(q) // T should be inferred to KFunction<Captured(*)> and should be approximated to KFunction<Any>, not KFunction<*>
val expected = "KFunction<Any>"
if (w.toString() != expected) return "Fail 1: $w"
if (typeOfValue(f).toString() != expected) return "Fail 2: $f"
return "OK"
}
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// !LANGUAGE: +NewInference
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
package test
@@ -26,12 +26,12 @@ fun box(): String {
check("test.C?", typeOf<C?>())
check("java.util.List<java.lang.String>", typeOf<List<String>>())
check("java.util.Map<in java.lang.Number, java.lang.Object?>?", typeOf<Map<in Number, *>?>())
check("java.lang.Enum<out java.lang.Enum<*>>", typeOf<Enum<*>>())
check("java.util.Map<in java.lang.Number, *>?", typeOf<Map<in Number, *>?>())
check("java.lang.Enum<*>", typeOf<Enum<*>>())
check("java.lang.Enum<kotlin.annotation.AnnotationRetention>", typeOf<Enum<AnnotationRetention>>())
check("kotlin.Array<java.lang.Object>", typeOf<Array<Any>>())
check("kotlin.Array<out java.lang.Object?>", typeOf<Array<*>>())
check("kotlin.Array<*>", typeOf<Array<*>>())
check("kotlin.Array<kotlin.IntArray>", typeOf<Array<IntArray>>())
check("kotlin.Array<in kotlin.Array<test.C>?>", typeOf<Array<in Array<C>?>>())
@@ -1,43 +0,0 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_RUNTIME
// Separate test is needed for IR because type arguments of typeOf are computed differently.
package test
import kotlin.reflect.KType
import kotlin.reflect.typeOf
import kotlin.test.assertEquals
class C
fun check(expected: String, actual: KType) {
assertEquals(expected + " (Kotlin reflection is not available)", actual.toString())
}
fun box(): String {
check("java.lang.Object", typeOf<Any>())
check("java.lang.String", typeOf<String>())
check("java.lang.String?", typeOf<String?>())
check("kotlin.Unit", typeOf<Unit>())
check("test.C", typeOf<C>())
check("test.C?", typeOf<C?>())
check("java.util.List<java.lang.String>", typeOf<List<String>>())
check("java.util.Map<in java.lang.Number, *>?", typeOf<Map<in Number, *>?>())
check("java.lang.Enum<*>", typeOf<Enum<*>>())
check("java.lang.Enum<kotlin.annotation.AnnotationRetention>", typeOf<Enum<AnnotationRetention>>())
check("kotlin.Array<java.lang.Object>", typeOf<Array<Any>>())
check("kotlin.Array<*>", typeOf<Array<*>>())
check("kotlin.Array<kotlin.IntArray>", typeOf<Array<IntArray>>())
check("kotlin.Array<in kotlin.Array<test.C>?>", typeOf<Array<in Array<C>?>>())
check("int", typeOf<Int>())
check("java.lang.Integer?", typeOf<Int?>())
check("boolean", typeOf<Boolean>())
return "OK"
}
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// !LANGUAGE: +NewInference
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
package test
@@ -41,7 +41,7 @@ fun box(): String {
equal<Array<Any>, Array<Any>>()
equal<Array<IntArray>, Array<IntArray>>()
equal<Array<*>, Array<out Any?>>() // This is subject to change if we retain star projections in typeOf
equal<Array<*>, Array<*>>()
equal<Int, Int>()
equal<Int?, Int?>()
@@ -1,56 +0,0 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_RUNTIME
// Separate test is needed for IR because of different ways type arguments of typeOf are calculated.
package test
import kotlin.reflect.KType
import kotlin.reflect.typeOf
class C
fun assertEqual(a: KType, b: KType) {
if (a != b || b != a) throw AssertionError("Fail equals: $a != $b")
if (a.hashCode() != b.hashCode()) throw AssertionError("Fail hashCode: $a != $b")
}
fun assertNotEqual(a: KType, b: KType) {
if (a == b || b == a) throw AssertionError("Fail equals: $a == $b")
}
inline fun <reified A, reified B> equal() {
assertEqual(typeOf<A>(), typeOf<B>())
}
inline fun <reified A, reified B> notEqual() {
assertNotEqual(typeOf<A>(), typeOf<B>())
}
fun box(): String {
equal<Any, Any>()
equal<Any?, Any?>()
equal<String, String>()
equal<C, C>()
equal<C?, C?>()
equal<List<String>, List<String>>()
equal<Enum<AnnotationRetention>, Enum<AnnotationRetention>>()
equal<Array<Any>, Array<Any>>()
equal<Array<IntArray>, Array<IntArray>>()
equal<Array<*>, Array<*>>()
equal<Int, Int>()
equal<Int?, Int?>()
notEqual<Any, Any?>()
notEqual<Any, String>()
notEqual<List<Any>, List<Any?>>()
notEqual<Map<in Number, BooleanArray>, Map<out Number, BooleanArray>>()
notEqual<Array<IntArray>, Array<Array<Int>>>()
return "OK"
}
@@ -0,0 +1,40 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// !LANGUAGE: +NewInference
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JS, JS_IR
// WITH_REFLECT
import kotlin.reflect.typeOf
import kotlin.reflect.KClass
import kotlin.reflect.KType
interface KFunction<out R>
val <T : Any> KClass<T>.primaryConstructor0: KFunction<T>
get() = object : KFunction<T> {}
interface A<out T : Any> {
val t: T
}
inline fun <reified T : KFunction<E>, E : Any> bar(w: A<E>): Pair<KType, KFunction<E>> {
val q: KFunction<E> = w.t::class.primaryConstructor0
return typeOf<T>() to q
}
inline fun <reified Q> typeOfValue(q: Q): KType {
return typeOf<Q>()
}
fun box(): String {
val q: A<*> = object : A<CharSequence> {
override val t: CharSequence
get() = ""
}
val (w, f) = bar(q) // T should be inferred to KFunction<Captured(*)> and should be approximated to KFunction<Any>, not KFunction<*>
val expected = "KFunction<kotlin.Any>"
if (w.toString() != expected) return "Fail 1: $w"
if (typeOfValue(f).toString() != expected) return "Fail 2: $f"
return "OK"
}