Do not rely on descriptors in KTypeParameterImpl.equals/hashCode

Descriptors are cached via weak references in moduleByClassLoader.kt and
can be garbage-collected at any point. So relying on identity of
descriptors in KTypeParameterImpl is dangerous because the same type
parameter can be represented by different descriptors. For example, the
test equalsOnFunctionParameters.kt was flaky before this change because
of this issue, and that could be reproduced by running it a few hundred
times in the same process.

Instead, use the type parameter's container (which is either KClass or
KCallable) and name, in equals/hashCode. KClass and KCallable already
have equals/hashCode independent of descriptors, so this works in case
the descriptor is invalidated.
This commit is contained in:
Alexander Udalov
2020-05-27 15:54:06 +02:00
parent d25f9dee62
commit 55f384cb04
14 changed files with 279 additions and 53 deletions
@@ -0,0 +1,56 @@
// TARGET_BACKEND: JVM
// WITH_REFLECT
// FILE: box.kt
import kotlin.test.assertEquals
inline fun check(message: String, generate: () -> Any?) {
val x1: Any?
val x2: Any?
try {
x1 = generate()
// Force clear the internal maps, as if the weak values in them are garbage-collected.
kotlin.reflect.jvm.internal.ReflectionFactoryImpl.clearCaches()
x2 = generate()
} catch (e: Throwable) {
throw AssertionError("Fail $message", e)
}
assertEquals(x1, x2, "Fail equals $message")
assertEquals(x2, x1, "Fail equals $message")
assertEquals(x1.hashCode(), x2.hashCode(), "Fail hashCode $message")
}
class C<T> {
fun <V> v(): V? = null
fun t(): T? = null
val <U> U.u: U get() = this
}
fun <W> W.w() {}
val <X> X.x: X get() = this
fun box(): String {
check("T from C's typeParameters") { C::class.typeParameters.single() }
check("V from v's typeParameters") { C::class.members.single { it.name == "v" }.typeParameters.single() }
check("V from v's returnType") { C::class.members.single { it.name == "v" }.returnType.classifier }
check("T from t's returnType") { C::class.members.single { it.name == "t" }.returnType.classifier }
check("U from u's parameter type") { C::class.members.single { it.name == "u" }.parameters[1].type.classifier }
check("W from w's parameter type") { Any::w.parameters.single().type.classifier }
check("X from x's parameter type") { Any::x.parameters.single().type.classifier }
check("Z from J's typeParameters") { J::class.typeParameters.single() }
check("Z from z's returnType") { J::class.members.single { it.name == "z" }.returnType.classifier }
return "OK"
}
// FILE: J.java
public interface J<Z> {
Z z();
}
@@ -0,0 +1,28 @@
// TARGET_BACKEND: JVM
// WITH_REFLECT
import kotlin.reflect.KVariance
import kotlin.test.assertEquals
class A<out T> {
inner class B<in U> {
fun test(u: U): T? = null
}
}
fun box(): String {
val fn = A.B::class.members.single { it.name == "test" }
val t = A::class.typeParameters.single()
val u = A.B::class.typeParameters.single()
assertEquals("T", t.name)
assertEquals(KVariance.OUT, t.variance)
assertEquals("U", u.name)
assertEquals(KVariance.IN, u.variance)
assertEquals(t, fn.returnType.classifier)
assertEquals(u, fn.parameters[1].type.classifier)
return "OK"
}
@@ -0,0 +1,30 @@
// TARGET_BACKEND: JVM
// WITH_REFLECT
// FILE: test.kt
import kotlin.reflect.KVariance
import kotlin.test.assertEquals
fun box(): String {
val ctor = J::class.constructors.single()
val ab = ctor.typeParameters
assertEquals(2, ab.size, ab.toString())
assertEquals("A", ab[0].name)
assertEquals(KVariance.INVARIANT, ab[0].variance)
assertEquals("B", ab[1].name)
assertEquals(KVariance.INVARIANT, ab[1].variance)
// TODO: currently fails with "AssertionError: Expected <A>, actual <A>"
// assertEquals(ab[0], ctor.parameters[0].type.classifier)
assertEquals(ab[1], ctor.parameters[1].type.classifier)
return "OK"
}
// FILE: J.java
public class J<A> {
public <B> J(A a, B b) {}
}