Generate function type parameter annotations and type annotations on their bounds into bytecode

Generate type parameter annotation by default for `-Xjvm-target 1.8` and above

 #KT-46539
 #KT-13228
 #KT-46545 Fixed
This commit is contained in:
Mikhael Bogdanov
2021-05-04 09:41:26 +02:00
committed by TeamCityServer
parent 209ec68591
commit cbe3c66156
23 changed files with 691 additions and 2 deletions
@@ -0,0 +1,23 @@
// 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)
class Kotlin {
fun foo(s: @TypeAnn("1") String) {
}
fun <T : @TypeAnn("Ant") Any> bar(p: T): T {
return p
}
}
@@ -0,0 +1,19 @@
public final class foo/Kotlin : java/lang/Object {
public void <init>()
<T:Ljava/lang/Object;>(TT;)TT;
public final java.lang.Object bar(java.lang.Object p)
@Lorg/jetbrains/annotations/NotNull;([]) // invisible
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
public final void foo(java.lang.String s)
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
}
public abstract interface foo/TypeAnn : java/lang/Object, java/lang/annotation/Annotation {
public abstract java.lang.String name()
}
public abstract interface foo/TypeParameterAnn : java/lang/Object, java/lang/annotation/Annotation {
public abstract java.lang.String name()
}
@@ -0,0 +1,62 @@
// 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)
annotation class ClassTypeAnn(val name: String)
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.BINARY)
annotation class TypeAnnBinary
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.SOURCE)
annotation class TypeAnnSource
@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 Generic<Z>
class GenericClass<Z>
class B<Y>
class Kotlin {
fun <@TypeParameterAnn("TP1") @TypeParameterAnnBinary @TypeParameterAnnSource T, @TypeParameterAnn("TP2") @TypeParameterAnnBinary @TypeParameterAnnSource T2> typeParameter() {
}
fun <@TypeParameterAnn("TP") T> genericParameterAndReturn(s: @TypeAnn("1") @TypeAnnBinary @TypeAnnSource T): @TypeAnn("2") @TypeAnnBinary @TypeAnnSource T {
return s
}
fun <@TypeParameterAnn("Y") Y, @TypeParameterAnn("T") T: @TypeAnn("Generic") @TypeAnnBinary @TypeAnnSource Generic<@TypeAnn("Generic Argument") @TypeAnnBinary @TypeAnnSource Y>> genericInterfaceBound() {
}
fun <@TypeParameterAnn("Y") Y, @TypeParameterAnn("T") T: @TypeAnn("Generic") @TypeAnnBinary @TypeAnnSource GenericClass<@TypeAnn("Generic Argument") @TypeAnnBinary @TypeAnnSource Y>> genericClassBound() {
}
fun <@TypeParameterAnn("Y") Y, @TypeParameterAnn("T") T: @TypeAnn("Generic") Generic<@TypeAnn("Generic Argument") Y>> whereClause() where T : @ClassTypeAnn("Any") Any {
}
fun <@TypeParameterAnn("Y") Y, @TypeParameterAnn("T") T: @TypeAnn("Y as Bound") @TypeAnnBinary @TypeAnnSource Y> typeParameterTypeParameterBound() {
}
// Second annotation is missed: see KT-46483, remove this test after prohibition of such cases
fun <@TypeParameterAnn("T") T: Any> whereClauseWithAnnotation() where @TypeParameterAnn("Additional") T : Generic<String> {
}
}
@@ -0,0 +1,98 @@
<Y:Ljava/lang/Object;>Ljava/lang/Object;
public final class foo/B : java/lang/Object {
public void <init>()
}
public abstract interface foo/ClassTypeAnn : java/lang/Object, java/lang/annotation/Annotation {
public abstract java.lang.String name()
}
<Z:Ljava/lang/Object;>Ljava/lang/Object;
public abstract interface foo/Generic : java/lang/Object {
}
<Z:Ljava/lang/Object;>Ljava/lang/Object;
public final class foo/GenericClass : java/lang/Object {
public void <init>()
}
public final class foo/Kotlin : java/lang/Object {
public void <init>()
<Y:Ljava/lang/Object;T:Lfoo/GenericClass<TY;>;>()V
public final void genericClassBound()
@Lfoo/TypeParameterAnn;([name="Y"]) : METHOD_TYPE_PARAMETER 0, null
@Lfoo/TypeParameterAnn;([name="T"]) : METHOD_TYPE_PARAMETER 1, null
@Lfoo/TypeAnn;([name="Generic"]) : METHOD_TYPE_PARAMETER_BOUND 1, 0, null
@Lfoo/TypeAnn;([name="Generic Argument"]) : METHOD_TYPE_PARAMETER_BOUND 1, 0, 0;
@Lfoo/TypeAnnBinary;([]) : METHOD_TYPE_PARAMETER_BOUND 1, 0, null // invisible
@Lfoo/TypeAnnBinary;([]) : METHOD_TYPE_PARAMETER_BOUND 1, 0, 0; // invisible
<Y:Ljava/lang/Object;T::Lfoo/Generic<TY;>;>()V
public final void genericInterfaceBound()
@Lfoo/TypeParameterAnn;([name="Y"]) : METHOD_TYPE_PARAMETER 0, null
@Lfoo/TypeParameterAnn;([name="T"]) : METHOD_TYPE_PARAMETER 1, null
@Lfoo/TypeAnn;([name="Generic"]) : METHOD_TYPE_PARAMETER_BOUND 1, 1, null
@Lfoo/TypeAnn;([name="Generic Argument"]) : METHOD_TYPE_PARAMETER_BOUND 1, 1, 0;
@Lfoo/TypeAnnBinary;([]) : METHOD_TYPE_PARAMETER_BOUND 1, 1, null // invisible
@Lfoo/TypeAnnBinary;([]) : METHOD_TYPE_PARAMETER_BOUND 1, 1, 0; // invisible
<T:Ljava/lang/Object;>(TT;)TT;
public final java.lang.Object genericParameterAndReturn(java.lang.Object s)
@Lfoo/TypeAnn;([name="2"]) : METHOD_RETURN, null
@Lfoo/TypeParameterAnn;([name="TP"]) : METHOD_TYPE_PARAMETER 0, null
@Lfoo/TypeAnn;([name="1"]) : METHOD_FORMAL_PARAMETER 0, null
@Lfoo/TypeAnnBinary;([]) : METHOD_RETURN, null // invisible
@Lfoo/TypeAnnBinary;([]) : METHOD_FORMAL_PARAMETER 0, null // invisible
<T:Ljava/lang/Object;T2:Ljava/lang/Object;>()V
public final void typeParameter()
@Lfoo/TypeParameterAnn;([name="TP1"]) : METHOD_TYPE_PARAMETER 0, null
@Lfoo/TypeParameterAnn;([name="TP2"]) : METHOD_TYPE_PARAMETER 1, null
@Lfoo/TypeParameterAnnBinary;([]) : METHOD_TYPE_PARAMETER 0, null // invisible
@Lfoo/TypeParameterAnnBinary;([]) : METHOD_TYPE_PARAMETER 1, null // invisible
<Y:Ljava/lang/Object;T::TY;>()V
public final void typeParameterTypeParameterBound()
@Lfoo/TypeParameterAnn;([name="Y"]) : METHOD_TYPE_PARAMETER 0, null
@Lfoo/TypeParameterAnn;([name="T"]) : METHOD_TYPE_PARAMETER 1, null
@Lfoo/TypeAnn;([name="Y as Bound"]) : METHOD_TYPE_PARAMETER_BOUND 1, 0, null
@Lfoo/TypeAnnBinary;([]) : METHOD_TYPE_PARAMETER_BOUND 1, 0, null // invisible
<Y:Ljava/lang/Object;T:Ljava/lang/Object;:Lfoo/Generic<TY;>;>()V
public final void whereClause()
@Lfoo/TypeParameterAnn;([name="Y"]) : METHOD_TYPE_PARAMETER 0, null
@Lfoo/TypeParameterAnn;([name="T"]) : METHOD_TYPE_PARAMETER 1, null
@Lfoo/TypeAnn;([name="Generic"]) : METHOD_TYPE_PARAMETER_BOUND 1, 1, null
@Lfoo/TypeAnn;([name="Generic Argument"]) : METHOD_TYPE_PARAMETER_BOUND 1, 1, 0;
@Lfoo/ClassTypeAnn;([name="Any"]) : METHOD_TYPE_PARAMETER_BOUND 1, 0, null
<T:Ljava/lang/Object;:Lfoo/Generic<Ljava/lang/String;>;>()V
public final void whereClauseWithAnnotation()
@Lfoo/TypeParameterAnn;([name="T"]) : METHOD_TYPE_PARAMETER 0, null
}
public abstract interface foo/TypeAnn : java/lang/Object, java/lang/annotation/Annotation {
public abstract java.lang.String name()
}
public abstract interface foo/TypeAnnBinary : java/lang/Object, java/lang/annotation/Annotation {
}
public abstract interface foo/TypeAnnSource : java/lang/Object, java/lang/annotation/Annotation {
}
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 {
}
@@ -52,5 +52,7 @@ class Kotlin {
fun <T> fooGenericOut(s: @Ann Bar<out @Ann2 T>) {
}
fun <T: Bar<Outer.Inner<@Ann Outer>>> innerClassInBound() {
}
}
@@ -58,6 +58,9 @@ public final class foo/Kotlin : java/lang/Object {
public final void fooGenericOut(foo.Bar s)
@Lfoo/Ann;([]) : METHOD_FORMAL_PARAMETER 0, null
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
<T:Lfoo/Bar<Lfoo/Outer$Inner<Lfoo/Outer;>;>;>()V
public final void innerClassInBound()
}
<T:Ljava/lang/Object;>Ljava/lang/Object;
@@ -70,4 +73,4 @@ public final class foo/Outer$Inner : java/lang/Object {
public final class foo/Outer : java/lang/Object {
public void <init>()
}
}
@@ -0,0 +1,21 @@
// 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()
interface Simple
class Kotlin {
var <@TypeParameterAnn T: @TypeAnn("Simple") Simple> T.z: T?
get() = null
set(value) {}
}
@@ -0,0 +1,29 @@
public final class foo/Kotlin : java/lang/Object {
public void <init>()
<T::Lfoo/Simple;>(TT;)TT;
public final foo.Simple getZ(foo.Simple $this$z)
@Lorg/jetbrains/annotations/Nullable;([]) // invisible
@Lfoo/TypeParameterAnn;([]) : METHOD_TYPE_PARAMETER 0, null
@Lfoo/TypeAnn;([name="Simple"]) : METHOD_TYPE_PARAMETER_BOUND 0, 1, null
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
<T::Lfoo/Simple;>(TT;TT;)V
public final void setZ(foo.Simple $this$z, foo.Simple value)
@Lfoo/TypeParameterAnn;([]) : METHOD_TYPE_PARAMETER 0, null
@Lfoo/TypeAnn;([name="Simple"]) : METHOD_TYPE_PARAMETER_BOUND 0, 1, null
@Lorg/jetbrains/annotations/NotNull;([]) // invisible, parameter 0
@Lorg/jetbrains/annotations/Nullable;([]) // invisible, parameter 1
}
public abstract interface foo/Simple : java/lang/Object {
}
public abstract interface foo/TypeAnn : java/lang/Object, java/lang/annotation/Annotation {
public abstract java.lang.String name()
}
public abstract interface foo/TypeParameterAnn : java/lang/Object, java/lang/annotation/Annotation {
}
@@ -0,0 +1,17 @@
// IGNORE_BACKEND: JVM
// RENDER_ANNOTATIONS
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
package foo
@Target(AnnotationTarget.TYPE_PARAMETER)
annotation class TypeParameterAnn(val name: String)
class Kotlin {
fun <@TypeParameterAnn("T") T> bar(p: T): T {
return p
}
}
@@ -0,0 +1,11 @@
public final class foo/Kotlin : java/lang/Object {
public void <init>()
<T:Ljava/lang/Object;>(TT;)TT;
public final java.lang.Object bar(java.lang.Object p)
@Lfoo/TypeParameterAnn;([name="T"]) : METHOD_TYPE_PARAMETER 0, null
}
public abstract interface foo/TypeParameterAnn : java/lang/Object, java/lang/annotation/Annotation {
public abstract java.lang.String name()
}
@@ -0,0 +1,15 @@
// RENDER_ANNOTATIONS
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.6
package foo
@Target(AnnotationTarget.TYPE_PARAMETER)
annotation class TypeParameterAnn(val name: String)
class Kotlin {
fun <@TypeParameterAnn("T") T> bar(p: T): T {
return p
}
}
@@ -0,0 +1,10 @@
public final class foo/Kotlin : java/lang/Object {
public void <init>()
<T:Ljava/lang/Object;>(TT;)TT;
public final java.lang.Object bar(java.lang.Object p)
}
public abstract interface foo/TypeParameterAnn : java/lang/Object, java/lang/annotation/Annotation {
public abstract java.lang.String name()
}
@@ -0,0 +1,46 @@
// 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 Kotlin {
fun <T> interfaceClassBound() where T : @TypeAnn("Interface") SimpleInterface, T : @TypeAnn("Class") SimpleClass {
}
fun <T> classInterfaceBound() where T : @TypeAnn("Class") SimpleClass, T : @TypeAnn("Interface") SimpleInterface {
}
}
fun box() : String {
val interfaceBounds = Kotlin::class.functions.single { it.name == "interfaceClassBound" }.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 = Kotlin::class.functions.single { it.name == "classInterfaceBound" }.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,183 @@
// 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>
class Kotlin {
fun <@TypeParameterAnn @TypeParameterAnnBinary T> foo(s: T) : T {
return s;
}
fun <@TypeParameterAnn T : @TypeAnn("Simple") Simple> interfaceBound(s: T) : T {
return s;
}
fun <@TypeParameterAnn T : @TypeAnn("Simple") SimpleClass> classBound(s: T) : T {
return s;
}
fun <T : @TypeAnn("Generic") Generic<@TypeAnn("Simple") Simple>> interfaceBoundGeneric(s: T) : T {
return s;
}
fun <T : @TypeAnn("GenericClass") GenericClass<@TypeAnn("SimpleClass") SimpleClass>> classBoundGeneric(s: T) : T {
return s;
}
fun <Y, @TypeParameterAnn T : @TypeAnn("Y as Bound") Y> typeParameterTypeParameterBound(s: T) : T {
return s;
}
}
fun box(): String {
//foo
checkTypeParameterAnnotation(
Kotlin::class.java.methods.single{it.name == "foo"}.typeParameters.single(),
"T",
"@foo.TypeParameterAnn()",
"foo"
)
//interfaceBound
val interfaceBound = Kotlin::class.java.methods.single { it.name == "interfaceBound" }
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 = Kotlin::class.java.methods.single { it.name == "classBound" }
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 = Kotlin::class.java.methods.single { it.name == "interfaceBoundGeneric" }
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 = Kotlin::class.java.methods.single { it.name == "classBoundGeneric" }
// 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 = Kotlin::class.java.methods.single { it.name == "typeParameterTypeParameterBound" }
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()