diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt index 37fdf524328..784602775e7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt @@ -741,7 +741,7 @@ private class ConstantExpressionEvaluatorVisitor( override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, expectedType: KotlinType?): CompileTimeConstant<*>? { val type = trace.getType(expression)!! if (type.isError) return null - return KClassValue(type).wrap() + return factory.createKClassValue(type).wrap() } private fun resolveArguments(valueArguments: List, expectedType: KotlinType): List?> { diff --git a/compiler/serialization/src/org/jetbrains/kotlin/serialization/AnnotationSerializer.kt b/compiler/serialization/src/org/jetbrains/kotlin/serialization/AnnotationSerializer.kt index 5c0f2869a49..8072bdf6e8e 100644 --- a/compiler/serialization/src/org/jetbrains/kotlin/serialization/AnnotationSerializer.kt +++ b/compiler/serialization/src/org/jetbrains/kotlin/serialization/AnnotationSerializer.kt @@ -97,9 +97,12 @@ class AnnotationSerializer(private val stringTable: StringTable) { intValue = value.value.toLong() } - override fun visitKClassValue(value: KClassValue?, data: Unit?) { - // TODO: support class literals - throw UnsupportedOperationException("Class literal annotation arguments are not yet supported: $value") + override fun visitKClassValue(value: KClassValue, data: Unit) { + val descriptor = value.value.constructor.declarationDescriptor as? ClassDescriptor + ?: throw UnsupportedOperationException("Class literal annotation argument should be a class: $value") + + type = Type.CLASS + classId = stringTable.getFqNameIndex(descriptor) } override fun visitLongValue(value: LongValue, data: Unit) { diff --git a/compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.kt b/compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.kt new file mode 100644 index 00000000000..0bc7e5e5d6a --- /dev/null +++ b/compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.kt @@ -0,0 +1,20 @@ +// ALLOW_AST_ACCESS + +package test + +import kotlin.reflect.KClass + +@Target(AnnotationTarget.TYPE) +annotation class Ann(val klass: KClass<*>) + +class A { + fun simple(s: @Ann(Simple::class) String) {} + fun generic(s: @Ann(Generic::class) String) {} + fun innerGeneric(s: @Ann(InnerGeneric.Inner::class) String) {} +} + +class Simple +class Generic +class InnerGeneric { + inner class Inner +} diff --git a/compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.txt b/compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.txt new file mode 100644 index 00000000000..09eb060647d --- /dev/null +++ b/compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.txt @@ -0,0 +1,30 @@ +package test + +public final class A { + /*primary*/ public constructor A() + public final fun generic(/*0*/ s: @test.Ann(klass = test.Generic<*>::class) kotlin.String): kotlin.Unit + public final fun innerGeneric(/*0*/ s: @test.Ann(klass = test.InnerGeneric<*, *>.Inner<*, *>::class) kotlin.String): kotlin.Unit + public final fun simple(/*0*/ s: @test.Ann(klass = test.Simple::class) kotlin.String): kotlin.Unit +} + +@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPE}) public final annotation class Ann : kotlin.Annotation { + /*primary*/ public constructor Ann(/*0*/ klass: kotlin.reflect.KClass<*>) + public final val klass: kotlin.reflect.KClass<*> + public final fun (): kotlin.reflect.KClass<*> +} + +public final class Generic { + /*primary*/ public constructor Generic() +} + +public final class InnerGeneric { + /*primary*/ public constructor InnerGeneric() + + public final inner class Inner /*captured type parameters: /*2*/ A, /*3*/ B*/ { + /*primary*/ public constructor Inner() + } +} + +public final class Simple { + /*primary*/ public constructor Simple() +} diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java index 2957380f002..6a71e9766b6 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java @@ -2250,6 +2250,12 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/types"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("ClassLiteralArgument.kt") + public void testClassLiteralArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.kt"); + doTestCompiledKotlin(fileName); + } + @TestMetadata("ReceiverParameter.kt") public void testReceiverParameter() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ReceiverParameter.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadKotlinWithTypeTableTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadKotlinWithTypeTableTestGenerated.java index 033e43bc6ec..c088dfff1cf 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadKotlinWithTypeTableTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadKotlinWithTypeTableTestGenerated.java @@ -457,6 +457,12 @@ public class LoadKotlinWithTypeTableTestGenerated extends AbstractLoadKotlinWith KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/types"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("ClassLiteralArgument.kt") + public void testClassLiteralArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.kt"); + doTest(fileName); + } + @TestMetadata("ReceiverParameter.kt") public void testReceiverParameter() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ReceiverParameter.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/javac/LoadJavaUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/javac/LoadJavaUsingJavacTestGenerated.java index 518ea5e7376..ece6ef967fb 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/javac/LoadJavaUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/javac/LoadJavaUsingJavacTestGenerated.java @@ -2250,6 +2250,12 @@ public class LoadJavaUsingJavacTestGenerated extends AbstractLoadJavaUsingJavacT KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/types"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("ClassLiteralArgument.kt") + public void testClassLiteralArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.kt"); + doTestCompiledKotlin(fileName); + } + @TestMetadata("ReceiverParameter.kt") public void testReceiverParameter() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ReceiverParameter.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java index b97fbbb0f56..519c91d8b04 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java @@ -459,6 +459,12 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/types"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("ClassLiteralArgument.kt") + public void testClassLiteralArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.kt"); + doTest(fileName); + } + @TestMetadata("ReceiverParameter.kt") public void testReceiverParameter() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ReceiverParameter.kt"); diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt index bf3d0e13f80..465afc082c0 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl +import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name @@ -31,11 +32,9 @@ import org.jetbrains.kotlin.serialization.ProtoBuf.Annotation import org.jetbrains.kotlin.serialization.ProtoBuf.Annotation.Argument import org.jetbrains.kotlin.serialization.ProtoBuf.Annotation.Argument.Value import org.jetbrains.kotlin.serialization.ProtoBuf.Annotation.Argument.Value.Type -import org.jetbrains.kotlin.types.ErrorUtils -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.SimpleType -import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf +import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections class AnnotationDeserializer(private val module: ModuleDescriptor, private val notFoundClasses: NotFoundClasses) { private val builtIns: KotlinBuiltIns @@ -85,8 +84,7 @@ class AnnotationDeserializer(private val module: ModuleDescriptor, private val n factory.createStringValue(nameResolver.getString(value.stringValue)) } Type.CLASS -> { - // TODO: support class literals - error("Class literal annotation arguments are not supported yet (${nameResolver.getClassId(value.classId)})") + resolveClassLiteralValue(nameResolver.getClassId(value.classId)) } Type.ENUM -> { resolveEnumValue(nameResolver.getClassId(value.classId), nameResolver.getName(value.enumValueId)) @@ -132,6 +130,15 @@ class AnnotationDeserializer(private val module: ModuleDescriptor, private val n } } + private fun resolveClassLiteralValue(classId: ClassId): ConstantValue<*> { + // If value refers to a class named test.Foo.Bar where both Foo and Bar have generic type parameters, + // we're constructing a type `KClass.Bar<*>>` below + val starProjectedType = resolveClass(classId).defaultType.replaceArgumentsWithStarProjections() + val kClass = resolveClass(ClassId.topLevel(KotlinBuiltIns.FQ_NAMES.kClass.toSafe())) + val type = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, kClass, listOf(TypeProjectionImpl(starProjectedType))) + return factory.createKClassValue(type) + } + // NOTE: see analogous code in BinaryClassAnnotationAndConstantLoaderImpl private fun resolveEnumValue(enumClassId: ClassId, enumEntryName: Name): ConstantValue<*> { val enumClass = resolveClass(enumClassId) diff --git a/idea/tests/org/jetbrains/kotlin/idea/decompiler/stubBuilder/LoadJavaClsStubTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/decompiler/stubBuilder/LoadJavaClsStubTestGenerated.java index 5f750e83f3e..724ff6c6097 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/decompiler/stubBuilder/LoadJavaClsStubTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/decompiler/stubBuilder/LoadJavaClsStubTestGenerated.java @@ -457,6 +457,12 @@ public class LoadJavaClsStubTestGenerated extends AbstractLoadJavaClsStubTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/types"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("ClassLiteralArgument.kt") + public void testClassLiteralArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.kt"); + doTestCompiledKotlin(fileName); + } + @TestMetadata("ReceiverParameter.kt") public void testReceiverParameter() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ReceiverParameter.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java index 49cc92c8e23..060cf8524f2 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/stubs/ResolveByStubTestGenerated.java @@ -457,6 +457,12 @@ public class ResolveByStubTestGenerated extends AbstractResolveByStubTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlin/annotations/types"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("ClassLiteralArgument.kt") + public void testClassLiteralArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ClassLiteralArgument.kt"); + doTest(fileName); + } + @TestMetadata("ReceiverParameter.kt") public void testReceiverParameter() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlin/annotations/types/ReceiverParameter.kt");