Generate type annotations on class type parameters bounds

#KT-13228
 #KT-46539 Fixed
This commit is contained in:
Mikhael Bogdanov
2021-05-07 09:40:14 +02:00
committed by TeamCityServer
parent f574f89f78
commit cd7841ceed
13 changed files with 505 additions and 3 deletions
@@ -0,0 +1,79 @@
// IGNORE_BACKEND: JVM
// KOTLIN_CONFIGURATION_FLAGS: +JVM.EMIT_JVM_TYPE_ANNOTATIONS
// RENDER_ANNOTATIONS
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
package foo
@Target(AnnotationTarget.TYPE)
annotation class TypeAnn(val name: String)
@Target( AnnotationTarget.TYPE_PARAMETER)
annotation class TypeParameterAnn(val name: String)
@Target(AnnotationTarget.TYPE_PARAMETER)
@Retention(AnnotationRetention.BINARY)
annotation class TypeParameterAnnBinary
@Target(AnnotationTarget.TYPE_PARAMETER)
@Retention(AnnotationRetention.SOURCE)
annotation class TypeParameterAnnSource
interface SimpleInterface
interface SimpleInterface2
open class SimpleClass
interface GenericInterface<Z>
open class GenericClass<Z>
@Suppress("UNSUPPORTED")
class Simple<@TypeParameterAnn("T") @TypeParameterAnnBinary @TypeParameterAnnSource T> {
fun test(p: T) : T {
return p
}
}
class TypeBound<Y, T: @foo.TypeAnn("Y") Y> {
fun test(p: T) : T {
return p
}
}
class InterfaceBound<T: @foo.TypeAnn("Interface") SimpleInterface> {
fun test(p: T) : T {
return p
}
}
class ClassBound<T: @foo.TypeAnn("Class") SimpleClass> {
fun test(p: T) : T {
return p
}
}
class ClassBoundGeneric<T: @foo.TypeAnn("Class") GenericClass<@foo.TypeAnn("SimpleClass") SimpleClass>> {
fun test(p: T) : T {
return p
}
}
class InterfaceBoundGeneric<T: @foo.TypeAnn("Interface") GenericInterface<@foo.TypeAnn("SimpleInterface") SimpleInterface>> {
fun test(p: T) : T {
return p
}
}
class ClassInterfaceBound<T: @foo.TypeAnn("Class") SimpleClass> where T : @foo.TypeAnn("Interface") SimpleInterface, T : @foo.TypeAnn("Interface2") SimpleInterface2 {
fun test(p: T) : T {
return p
}
}
class InterfaceClassBound<T: @foo.TypeAnn("Interface") SimpleInterface > where T : @foo.TypeAnn("Class") SimpleClass, T : @foo.TypeAnn("Interface2") SimpleInterface2 {
fun test(p: T) : T {
return p
}
}
@@ -0,0 +1,133 @@
<T:Lfoo/SimpleClass;>Ljava/lang/Object;
public final class foo/ClassBound : java/lang/Object {
@Lfoo/TypeAnn;([name="Class"]) : CLASS_TYPE_PARAMETER_BOUND 0, 0, null
public void <init>()
(TT;)TT;
public final foo.SimpleClass test(foo.SimpleClass p)
@Lorg/jetbrains/annotations/NotNull;([]) // invisible
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
}
<T:Lfoo/GenericClass<Lfoo/SimpleClass;>;>Ljava/lang/Object;
public final class foo/ClassBoundGeneric : java/lang/Object {
@Lfoo/TypeAnn;([name="Class"]) : CLASS_TYPE_PARAMETER_BOUND 0, 0, null
@Lfoo/TypeAnn;([name="SimpleClass"]) : CLASS_TYPE_PARAMETER_BOUND 0, 0, 0;
public void <init>()
(TT;)TT;
public final foo.GenericClass test(foo.GenericClass p)
@Lorg/jetbrains/annotations/NotNull;([]) // invisible
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
}
<T:Lfoo/SimpleClass;:Lfoo/SimpleInterface;:Lfoo/SimpleInterface2;>Ljava/lang/Object;
public final class foo/ClassInterfaceBound : java/lang/Object {
@Lfoo/TypeAnn;([name="Class"]) : CLASS_TYPE_PARAMETER_BOUND 0, 0, null
@Lfoo/TypeAnn;([name="Interface"]) : CLASS_TYPE_PARAMETER_BOUND 0, 1, null
@Lfoo/TypeAnn;([name="Interface2"]) : CLASS_TYPE_PARAMETER_BOUND 0, 2, null
public void <init>()
(TT;)TT;
public final foo.SimpleClass test(foo.SimpleClass p)
@Lorg/jetbrains/annotations/NotNull;([]) // invisible
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
}
<Z:Ljava/lang/Object;>Ljava/lang/Object;
public class foo/GenericClass : java/lang/Object {
public void <init>()
}
<Z:Ljava/lang/Object;>Ljava/lang/Object;
public abstract interface foo/GenericInterface : java/lang/Object {
}
<T::Lfoo/SimpleInterface;>Ljava/lang/Object;
public final class foo/InterfaceBound : java/lang/Object {
@Lfoo/TypeAnn;([name="Interface"]) : CLASS_TYPE_PARAMETER_BOUND 0, 1, null
public void <init>()
(TT;)TT;
public final foo.SimpleInterface test(foo.SimpleInterface p)
@Lorg/jetbrains/annotations/NotNull;([]) // invisible
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
}
<T::Lfoo/GenericInterface<Lfoo/SimpleInterface;>;>Ljava/lang/Object;
public final class foo/InterfaceBoundGeneric : java/lang/Object {
@Lfoo/TypeAnn;([name="Interface"]) : CLASS_TYPE_PARAMETER_BOUND 0, 1, null
@Lfoo/TypeAnn;([name="SimpleInterface"]) : CLASS_TYPE_PARAMETER_BOUND 0, 1, 0;
public void <init>()
(TT;)TT;
public final foo.GenericInterface test(foo.GenericInterface p)
@Lorg/jetbrains/annotations/NotNull;([]) // invisible
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
}
<T:Lfoo/SimpleClass;:Lfoo/SimpleInterface;:Lfoo/SimpleInterface2;>Ljava/lang/Object;
public final class foo/InterfaceClassBound : java/lang/Object {
@Lfoo/TypeAnn;([name="Interface"]) : CLASS_TYPE_PARAMETER_BOUND 0, 1, null
@Lfoo/TypeAnn;([name="Class"]) : CLASS_TYPE_PARAMETER_BOUND 0, 0, null
@Lfoo/TypeAnn;([name="Interface2"]) : CLASS_TYPE_PARAMETER_BOUND 0, 2, null
public void <init>()
(TT;)TT;
public final foo.SimpleClass test(foo.SimpleClass p)
@Lorg/jetbrains/annotations/NotNull;([]) // invisible
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
}
<T:Ljava/lang/Object;>Ljava/lang/Object;
public final class foo/Simple : java/lang/Object {
public void <init>()
(TT;)TT;
public final java.lang.Object test(java.lang.Object p)
}
public class foo/SimpleClass : java/lang/Object {
public void <init>()
}
public abstract interface foo/SimpleInterface : java/lang/Object {
}
public abstract interface foo/SimpleInterface2 : java/lang/Object {
}
public abstract interface foo/TypeAnn : java/lang/Object, java/lang/annotation/Annotation {
public abstract java.lang.String name()
}
<Y:Ljava/lang/Object;T::TY;>Ljava/lang/Object;
public final class foo/TypeBound : java/lang/Object {
@Lfoo/TypeAnn;([name="Y"]) : CLASS_TYPE_PARAMETER_BOUND 1, 0, null
public void <init>()
(TT;)TT;
public final java.lang.Object test(java.lang.Object p)
}
public abstract interface foo/TypeParameterAnn : java/lang/Object, java/lang/annotation/Annotation {
public abstract java.lang.String name()
}
public abstract interface foo/TypeParameterAnnBinary : java/lang/Object, java/lang/annotation/Annotation {
}
public abstract interface foo/TypeParameterAnnSource : java/lang/Object, java/lang/annotation/Annotation {
}
@@ -56,3 +56,5 @@ class Kotlin {
}
}
class WithBound<T: Bar<Outer.Inner<@Ann Outer>>>()
@@ -73,4 +73,9 @@ public final class foo/Outer$Inner : java/lang/Object {
public final class foo/Outer : java/lang/Object {
public void <init>()
}
<T:Lfoo/Bar<Lfoo/Outer$Inner<Lfoo/Outer;>;>;>Ljava/lang/Object;
public final class foo/WithBound : java/lang/Object {
public void <init>()
}
@@ -0,0 +1,43 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// EMIT_JVM_TYPE_ANNOTATIONS
// JVM_TARGET: 1.8
// WITH_REFLECT
// FULL_JDK
package foo
import kotlin.reflect.KClass
import kotlin.reflect.full.functions
@Target(AnnotationTarget.TYPE)
annotation class TypeAnn(val name: String)
interface SimpleInterface
open class SimpleClass
class InterfaceClassBound<T>() where T : @TypeAnn("Interface") SimpleInterface, T : @TypeAnn("Class") SimpleClass {
}
class ClassInterfaceBound<T>() where T : @TypeAnn("Class") SimpleClass, T : @TypeAnn("Interface") SimpleInterface {
}
fun box() : String {
val interfaceBounds = InterfaceClassBound::class.typeParameters.single()
if (interfaceBounds.upperBounds[0].annotations.joinToString() != "@foo.TypeAnn(name=Interface)") return "fail 1: ${interfaceBounds.upperBounds[0].annotations.joinToString()}"
if ((interfaceBounds.upperBounds[0].classifier as KClass<*>).simpleName != "SimpleInterface") return "fail 1.1: ${interfaceBounds.upperBounds[0].classifier}"
if (interfaceBounds.upperBounds[1].annotations.joinToString() != "@foo.TypeAnn(name=Class)") return "fail 2: ${interfaceBounds.upperBounds[1].annotations.joinToString()}"
if ((interfaceBounds.upperBounds[1].classifier as KClass<*>).simpleName != "SimpleClass") return "fail 2.1: ${interfaceBounds.upperBounds[1].classifier}"
val classBounds = ClassInterfaceBound::class.typeParameters.single()
if (classBounds.upperBounds[0].annotations.joinToString() != "@foo.TypeAnn(name=Class)") return "fail 3: ${classBounds.upperBounds[0].annotations.joinToString()}"
if ((classBounds.upperBounds[0].classifier as KClass<*>).simpleName != "SimpleClass") return "fail 3.1: ${classBounds.upperBounds[0].classifier}"
if (classBounds.upperBounds[1].annotations.joinToString() != "@foo.TypeAnn(name=Interface)") return "fail 4: ${classBounds.upperBounds[1].annotations.joinToString()}"
if ((classBounds.upperBounds[1].classifier as KClass<*>).simpleName != "SimpleInterface") return "fail 4.1: ${classBounds.upperBounds[1].classifier}"
return "OK"
}
@@ -0,0 +1,173 @@
// IGNORE_BACKEND: JVM
// IGNORE_LIGHT_ANALYSIS
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// EMIT_JVM_TYPE_ANNOTATIONS
// JVM_TARGET: 1.8
// WITH_REFLECT
// FULL_JDK
package foo
import java.lang.reflect.AnnotatedType
import java.lang.reflect.TypeVariable
import java.lang.reflect.AnnotatedParameterizedType
import kotlin.reflect.jvm.javaMethod
import kotlin.test.fail
@Target(AnnotationTarget.TYPE)
annotation class TypeAnn(val name: String)
@Target(AnnotationTarget.TYPE_PARAMETER)
annotation class TypeParameterAnn
@Target(AnnotationTarget.TYPE_PARAMETER)
@Retention(AnnotationRetention.BINARY)
annotation class TypeParameterAnnBinary
interface Simple
class SimpleClass
interface Generic<G>
class GenericClass<G>
@Suppress("UNSUPPORTED")
class SimpleParameter<@TypeParameterAnn @TypeParameterAnnBinary T> {}
@Suppress("UNSUPPORTED")
class InterfaceBound<@TypeParameterAnn T : @TypeAnn("Simple") Simple> {}
@Suppress("UNSUPPORTED")
class ClassBound<@TypeParameterAnn T : @TypeAnn("Simple") SimpleClass>
class InterfaceBoundGeneric<T : @TypeAnn("Generic") Generic<@TypeAnn("Simple") Simple>> {}
class ClassBoundGeneric<T : @TypeAnn("GenericClass") GenericClass<@TypeAnn("SimpleClass") SimpleClass>>
@Suppress("UNSUPPORTED")
class TypeParameterAsBound<Y, @TypeParameterAnn T : @TypeAnn("Y as Bound") Y>
fun box(): String {
//foo
// checkTypeParameterAnnotation(
// SimpleParameter::class.java.typeParameters.single(),
// "T",
// "@foo.TypeParameterAnn()",
// "foo"
// )
//interfaceBound
val interfaceBound = InterfaceBound::class.java
// checkTypeParameterAnnotation(
// InterfaceBound.typeParameters.single(),
// "T",
// "@foo.TypeParameterAnn()",
// "interfaceBound type parameter"
// )
checkTypeAnnotation(
interfaceBound.typeParameters.single().annotatedBounds.single(),
"interface foo.Simple",
"@foo.TypeAnn(name=Simple)",
"interfaceBound bound"
)
//classBound
val classBound = ClassBound::class.java
// checkTypeParameterAnnotation(
// classBound.typeParameters.single(),
// "T",
// "@foo.TypeParameterAnn()",
// "classBound type parameter"
// )
checkTypeAnnotation(
classBound.typeParameters.single().annotatedBounds.single(),
"class foo.SimpleClass",
"@foo.TypeAnn(name=Simple)",
"classBound bound"
)
//interfaceBoundGeneric
val interfaceBoundGeneric = InterfaceBoundGeneric::class.java
// checkTypeAnnotation(
// interfaceBoundGeneric.typeParameters.single().annotatedBounds.single(),
// "foo.Generic<foo.Simple>",
// "@foo.TypeAnn(name=Generic)",
// "interfaceBoundGeneric bound"
// )
checkTypeAnnotation(
(interfaceBoundGeneric.typeParameters.single().annotatedBounds.single() as AnnotatedParameterizedType).getAnnotatedActualTypeArguments().single(),
"interface foo.Simple",
"@foo.TypeAnn(name=Simple)",
"interfaceBoundGeneric bound parameter"
)
//classBoundGeneric
val classBoundGeneric = ClassBoundGeneric::class.java
// Works on JDK 15
// checkTypeAnnotation(
// classBoundGeneric.typeParameters.single().annotatedBounds.single(),
// "foo.GenericClass<foo.SimpleClass>",
// "@foo.TypeAnn(name=GenericClass)",
// "classBoundGeneric bound"
// )
checkTypeAnnotation(
(classBoundGeneric.typeParameters.single().annotatedBounds.single() as AnnotatedParameterizedType).getAnnotatedActualTypeArguments().single(),
"class foo.SimpleClass",
"@foo.TypeAnn(name=SimpleClass)",
"classBoundGeneric bound parameter"
)
//typeParameterTypeParameterBound
val typeParameterTypeParameterBound = TypeParameterAsBound::class.java
// checkTypeParameterAnnotation(
// typeParameterTypeParameterBound.typeParameters[1]!!,
// "T",
// "@foo.TypeParameterAnn()",
// "typeParameterTypeParameterBound type parameter"
// )
// Works on JDK 15
// checkTypeAnnotation(
// typeParameterTypeParameterBound.typeParameters[1]!!.annotatedBounds.single(),
// "Y",
// "@foo.TypeAnn(name=Y as Bound)",
// "typeParameterTypeParameterBound bound"
// )
return "OK"
}
fun checkTypeParameterAnnotation(
typeParameter: TypeVariable<*>,
type: String,
annotations: String,
message: String
) {
if (typeParameter.annotation() != annotations) fail("check $message (1): ${typeParameter.annotation()} != $annotations")
if (typeParameter.toString() != type) fail("check $message (2): ${typeParameter.toString()} != $type")
}
fun checkTypeAnnotation(
annotatedType: AnnotatedType,
type: String,
annotations: String,
message: String
) {
if (annotatedType.annotation() != annotations &&
//JDK11+
annotatedType . annotation () != annotations.replace("=", "=\"").replace(")", "\")")
) fail("check $message (1): ${annotatedType.annotation()} != $annotations")
if (annotatedType.type.toString() != type) fail("check $message (2): ${annotatedType.type} != $type")
}
fun AnnotatedType.annotation() = annotations.joinToString()
fun TypeVariable<*>.annotation() = annotations.joinToString()