Support instantiation of annotations with type parameters

By ignoring type parameters. Since type parameters in annotations are a
very limited feature, their sole use is to be able to specify them as
KClass argument: annotation class Foo<T: Any>(val bar: KClass<T>).
Since we can encounter type param only as a KClass type argument (and
never as a property type), simple approach of ignoring them works fine.
In that case, since we simply copy property types to synthetic
implementation class, its properties in IR start look like this:
annotation class FooImpl(override val bar: KClass<T of Foo>). This IR
seems to be not completely correct, since FooImpl.bar type contains T of
Foo param, which is out of its scope. However, so far I didn't
encounter any problems with this during testing and after MR discussion
this approach has been considered possible.

#KT-59558 Fixed
#KT-59036 Fixed
This commit is contained in:
Leonid Startsev
2023-07-13 12:29:36 +02:00
committed by Space Team
parent 8c795462e1
commit 648c1f4599
23 changed files with 322 additions and 7 deletions
@@ -1,6 +1,6 @@
// IGNORE_BACKEND: JVM
// (supported: JVM_IR, JS_IR(_E6))
// (supported: JVM_IR, JS_IR(_ES6), NATIVE)
// Regular JS works too, but without proper hashCode or equals
// WITH_STDLIB
@@ -0,0 +1,38 @@
// IGNORE_BACKEND: JVM
// DONT_TARGET_EXACT_BACKEND: JS
// IGNORE_BACKEND: WASM
// WASM ticket: KT-59032
// supported: JVM_IR, JS_IR(_ES6), NATIVE
// WITH_STDLIB
// !LANGUAGE: +InstantiationOfAnnotationClasses
package test
import kotlin.reflect.KClass
import kotlin.test.assertEquals
import kotlin.test.assertTrue as assert
annotation class One<T>()
annotation class Two<K, V>(val s: String)
annotation class Nesting(val a1: One<Int> = One(), val a2: Two<String, List<String>> = Two("two"))
annotation class WithKClass<K: Any>(val k: KClass<K>)
fun box(): String {
val a = One<String>()
assert(a.toString().endsWith("test.One()"))
val t = Two<String, Int>("two")
assertEquals("two", t.s)
val n = Nesting()
assertEquals("two", n.a2.s)
val wk = WithKClass(String::class)
assert(String::class == wk.k)
// type parameters don't affect equals
assert(Two<String, Int>("two") == Two<Int, String>("two"))
assert(WithKClass(Int::class) != WithKClass(String::class))
return "OK"
}
@@ -0,0 +1,36 @@
// TARGET_BACKEND: JVM_IR
// WITH_REFLECT
// !LANGUAGE: +InstantiationOfAnnotationClasses
import kotlin.reflect.KClass
import kotlin.test.assertEquals
import kotlin.test.assertTrue as assert
annotation class WithKClass<K: Any>(val k: KClass<K>)
// Following can't be called on JS/Native, as on these platforms only `Array::class : KClass<Array<*>>` exists.
annotation class WithArray<A>(val a: KClass<Array<A>>)
annotation class Combined<T: Any>(val t: KClass<T>, val w: WithKClass<T>, val wa: WithArray<T>)
class X
fun box(): String {
val wa = WithArray(Array<String>::class)
assert(wa.a.java.isArray)
assert(wa.a.java.componentType == String::class.java)
assertEquals(WithArray(Array<String>::class), wa)
assert(WithArray(Array<Int>::class) != wa)
val typeParams = WithKClass(X::class).annotationClass.typeParameters
assertEquals(1, typeParams.size)
val c = Combined(X::class, WithKClass(X::class), WithArray(Array<X>::class))
assertEquals(X::class, c.t)
assertEquals(X::class, c.w.k)
assertEquals(X::class.java, c.wa.a.java.componentType)
return "OK"
}
@@ -0,0 +1,36 @@
// IGNORE_BACKEND: JVM
// DONT_TARGET_EXACT_BACKEND: JS
// IGNORE_DEXING
// WITH_STDLIB
// !LANGUAGE: +InstantiationOfAnnotationClasses
// MODULE: lib
// FILE: lib.kt
package a
import kotlin.reflect.KClass
annotation class Two<K, V>(val s: String)
annotation class Nesting(val a2: Two<String, List<String>> = Two("two"))
annotation class WithKClass<K: Any>(val k: KClass<K>)
// MODULE: app(lib)
// FILE: app.kt
package test
import a.*
import kotlin.test.*
fun box(): String {
val t = Two<String, Int>("two")
assertEquals("two", t.s)
val n = Nesting()
assertEquals("two", n.a2.s)
val wk = WithKClass(String::class)
assertTrue(String::class == wk.k)
return "OK"
}