diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index cc1192d96e8..4e1c87dfc7c 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -6840,6 +6840,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/reflectTopLevelFunctionOtherFile.kt"); } + @Test + @TestMetadata("repeatableAnnotation.kt") + public void testRepeatableAnnotation() throws Exception { + runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt"); + } + @Test @TestMetadata("sealedClass.kt") public void testSealedClass() throws Exception { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/AnnotationsLoader.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/AnnotationsLoader.kt index f2dc7882f8c..7306d9fe105 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/AnnotationsLoader.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/AnnotationsLoader.kt @@ -9,28 +9,30 @@ import org.jetbrains.kotlin.SpecialJvmAnnotations import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind -import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall -import org.jetbrains.kotlin.fir.expressions.FirClassReferenceExpression -import org.jetbrains.kotlin.fir.expressions.FirExpression -import org.jetbrains.kotlin.fir.expressions.buildUnaryArgumentList +import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* import org.jetbrains.kotlin.fir.java.createConstantOrError import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference import org.jetbrains.kotlin.fir.references.impl.FirReferencePlaceholderForResolvedAnnotations -import org.jetbrains.kotlin.fir.resolve.symbolProvider import org.jetbrains.kotlin.fir.resolve.providers.getClassDeclaredPropertySymbols +import org.jetbrains.kotlin.fir.resolve.symbolProvider import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl +import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.constructClassType +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass +import org.jetbrains.kotlin.load.kotlin.findKotlinClass import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.constants.ClassLiteralValue -internal class AnnotationsLoader(private val session: FirSession) { +internal class AnnotationsLoader(private val session: FirSession, private val kotlinClassFinder: KotlinClassFinder) { private fun loadAnnotation( annotationClassId: ClassId, result: MutableList, ): KotlinJvmBinaryClass.AnnotationArgumentVisitor { @@ -141,6 +143,11 @@ internal class AnnotationsLoader(private val session: FirSession) { } override fun visitEnd() { + // Do not load the @java.lang.annotation.Repeatable annotation instance generated automatically by the compiler for + // Kotlin-repeatable annotation classes. Otherwise the reference to the implicit nested "Container" class cannot be + // resolved, since that class is only generated in the backend, and is not visible to the frontend. + if (isRepeatableWithImplicitContainer(lookupTag, argumentMap)) return + result += buildAnnotationCall { annotationTypeRef = lookupTag.toDefaultResolvedTypeRef() argumentList = buildArgumentList { @@ -162,6 +169,21 @@ internal class AnnotationsLoader(private val session: FirSession) { } } + private fun isRepeatableWithImplicitContainer(lookupTag: ConeClassLikeLookupTag, argumentMap: Map): Boolean { + if (lookupTag.classId != SpecialJvmAnnotations.JAVA_LANG_ANNOTATION_REPEATABLE) return false + + val getClassCall = argumentMap[Name.identifier("value")] as? FirGetClassCall ?: return false + val classReference = getClassCall.argument as? FirClassReferenceExpression ?: return false + val containerType = classReference.classTypeRef.coneType as? ConeClassLikeType ?: return false + val classId = containerType.lookupTag.classId + if (classId.outerClassId == null || + classId.shortClassName.asString() != JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME + ) return false + + val klass = kotlinClassFinder.findKotlinClass(classId) + return klass != null && SpecialJvmAnnotations.isAnnotatedWithContainerMetaAnnotation(klass) + } + internal fun loadAnnotationIfNotSpecial( annotationClassId: ClassId, result: MutableList, ): KotlinJvmBinaryClass.AnnotationArgumentVisitor? { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmBinaryAnnotationDeserializer.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmBinaryAnnotationDeserializer.kt index eb9e6392ae9..6ce9790cdf7 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmBinaryAnnotationDeserializer.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmBinaryAnnotationDeserializer.kt @@ -33,14 +33,14 @@ class JvmBinaryAnnotationDeserializer( byteContent: ByteArray? ) : AbstractAnnotationDeserializer(session) { private val annotationInfo by lazy(LazyThreadSafetyMode.PUBLICATION) { - session.loadMemberAnnotations(kotlinBinaryClass, byteContent) + session.loadMemberAnnotations(kotlinBinaryClass, byteContent, kotlinClassFinder) } private val annotationInfoForDefaultImpls by lazy(LazyThreadSafetyMode.PUBLICATION) { val defaultImplsClassId = kotlinBinaryClass.classId.createNestedClassId(Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME)) val (defaultImplsClass, defaultImplsByteContent) = kotlinClassFinder.findKotlinClassOrContent(defaultImplsClassId) as? KotlinClassFinder.Result.KotlinClass ?: return@lazy null - session.loadMemberAnnotations(defaultImplsClass, defaultImplsByteContent) + session.loadMemberAnnotations(defaultImplsClass, defaultImplsByteContent, kotlinClassFinder) } override fun inheritAnnotationInfo(parent: AbstractAnnotationDeserializer) { @@ -288,9 +288,13 @@ class JvmBinaryAnnotationDeserializer( private data class MemberAnnotations(val memberAnnotations: MutableMap>) // TODO: better to be in KotlinDeserializedJvmSymbolsProvider? -private fun FirSession.loadMemberAnnotations(kotlinBinaryClass: KotlinJvmBinaryClass, byteContent: ByteArray?): MemberAnnotations { +private fun FirSession.loadMemberAnnotations( + kotlinBinaryClass: KotlinJvmBinaryClass, + byteContent: ByteArray?, + kotlinClassFinder: KotlinClassFinder, +): MemberAnnotations { val memberAnnotations = hashMapOf>() - val annotationsLoader = AnnotationsLoader(this) + val annotationsLoader = AnnotationsLoader(this, kotlinClassFinder) kotlinBinaryClass.visitMembers(object : KotlinJvmBinaryClass.MemberVisitor { override fun visitMethod(name: Name, desc: String): KotlinJvmBinaryClass.MethodAnnotationVisitor { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt index 413b173b766..95f05ea664a 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt @@ -9,15 +9,17 @@ import com.intellij.openapi.progress.ProcessCanceledException import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.ThreadSafeMutableState -import org.jetbrains.kotlin.fir.caches.* -import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.caches.createCache +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.declarations.getDeprecationInfos import org.jetbrains.kotlin.fir.deserialization.* -import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.java.JavaSymbolProvider import org.jetbrains.kotlin.fir.java.topLevelName import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider -import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.load.java.JavaClassFinder import org.jetbrains.kotlin.load.kotlin.* import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader @@ -44,7 +46,7 @@ class KotlinDeserializedJvmSymbolsProvider( javaClassFinder: JavaClassFinder, ) : AbstractFirDeserializedSymbolsProvider(session, moduleDataProvider, kotlinScopeProvider) { private val knownNameInPackageCache = KnownNameInPackageCache(session, javaClassFinder) - private val annotationsLoader = AnnotationsLoader(session) + private val annotationsLoader = AnnotationsLoader(session, kotlinClassFinder) override fun computePackagePartsInfos(packageFqName: FqName): List { return packagePartProvider.findPackageParts(packageFqName.asString()).mapNotNull { partName -> diff --git a/compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt b/compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt new file mode 100644 index 00000000000..04e4a00260f --- /dev/null +++ b/compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt @@ -0,0 +1,23 @@ +// TARGET_BACKEND: JVM_IR +// IGNORE_BACKEND_MULTI_MODULE: JVM_MULTI_MODULE_IR_AGAINST_OLD +// WITH_RUNTIME +// FULL_JDK +// JVM_TARGET: 1.8 +// MODULE: lib +// FILE: A.kt + +@Repeatable +annotation class A(val value: String) + +// MODULE: main(lib) +// FILE: box.kt + +class C { + @A("O") @A("K") + fun f() {} +} + +fun box(): String { + val a = C::class.java.getDeclaredMethod("f").getAnnotationsByType(A::class.java) + return a[0].value + a[1].value +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index b271a828a86..be6cbfd0c2d 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -6840,6 +6840,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/reflectTopLevelFunctionOtherFile.kt"); } + @Test + @TestMetadata("repeatableAnnotation.kt") + public void testRepeatableAnnotation() throws Exception { + runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt"); + } + @Test @TestMetadata("sealedClass.kt") public void testSealedClass() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmIrAgainstOldBoxTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmIrAgainstOldBoxTestGenerated.java index 3479586843c..448986a6689 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmIrAgainstOldBoxTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmIrAgainstOldBoxTestGenerated.java @@ -397,6 +397,12 @@ public class JvmIrAgainstOldBoxTestGenerated extends AbstractJvmIrAgainstOldBoxT runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/reflectTopLevelFunctionOtherFile.kt"); } + @Test + @TestMetadata("repeatableAnnotation.kt") + public void testRepeatableAnnotation() throws Exception { + runTest("compiler/testData/codegen/box/compileKotlinAgainstKotlin/repeatableAnnotation.kt"); + } + @Test @TestMetadata("sealedClass.kt") public void testSealedClass() throws Exception { diff --git a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.kt b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.kt index ae1c2f8cca5..0215955ed59 100644 --- a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.kt +++ b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.kt @@ -47,6 +47,7 @@ object JvmAbi { const val IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS = "-impl" const val REPEATABLE_ANNOTATION_CONTAINER_NAME = "Container" + val REPEATABLE_ANNOTATION_CONTAINER_META_ANNOTATION = ClassId.fromString("kotlin/jvm/internal/RepeatableContainer") /** * @param baseName JVM name of the property getter since Kotlin 1.4, or Kotlin name of the property otherwise. diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt index 49f2bba93b1..72c9bafdde8 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt @@ -39,7 +39,7 @@ import java.util.* abstract class AbstractBinaryClassAnnotationAndConstantLoader( storageManager: StorageManager, - private val kotlinClassFinder: KotlinClassFinder + protected val kotlinClassFinder: KotlinClassFinder ) : AnnotationAndConstantLoader { private val storage = storageManager.createMemoizedFunction> { kotlinClass -> loadAnnotationsAndInitializers(kotlinClass) diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl.kt index b5fdb62a9bc..faac6c3aeec 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl.kt @@ -16,9 +16,12 @@ package org.jetbrains.kotlin.load.kotlin +import org.jetbrains.kotlin.SpecialJvmAnnotations import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor import org.jetbrains.kotlin.metadata.ProtoBuf @@ -29,7 +32,6 @@ import org.jetbrains.kotlin.resolve.constants.* import org.jetbrains.kotlin.serialization.deserialization.AnnotationDeserializer import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.utils.compact -import java.util.* class BinaryClassAnnotationAndConstantLoaderImpl( private val module: ModuleDescriptor, @@ -143,7 +145,13 @@ class BinaryClassAnnotationAndConstantLoaderImpl( } override fun visitEnd() { - result.add(AnnotationDescriptorImpl(annotationClass.defaultType, arguments, source)) + val annotation = AnnotationDescriptorImpl(annotationClass.defaultType, arguments, source) + // Do not load the @java.lang.annotation.Repeatable annotation instance generated automatically by the compiler for + // Kotlin-repeatable annotation classes. Otherwise the reference to the implicit nested "Container" class cannot be + // resolved, since that class is only generated in the backend, and is not visible to the frontend. + if (!isRepeatableWithImplicitContainer(annotation)) { + result.add(annotation) + } } private fun createConstant(name: Name?, value: Any?): ConstantValue<*> { @@ -156,4 +164,18 @@ class BinaryClassAnnotationAndConstantLoaderImpl( private fun resolveClass(classId: ClassId): ClassDescriptor { return module.findNonGenericClassAcrossDependencies(classId, notFoundClasses) } + + private fun isRepeatableWithImplicitContainer(annotation: AnnotationDescriptor): Boolean { + if (annotation.fqName != JvmAnnotationNames.REPEATABLE_ANNOTATION) return false + + val containerKClassValue = annotation.allValueArguments[Name.identifier("value")] as? KClassValue ?: return false + val normalClass = containerKClassValue.value as? KClassValue.Value.NormalClass ?: return false + val classId = normalClass.classId + if (classId.outerClassId == null || + classId.shortClassName.asString() != JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME + ) return false + + val klass = kotlinClassFinder.findKotlinClass(classId) + return klass != null && SpecialJvmAnnotations.isAnnotatedWithContainerMetaAnnotation(klass) + } } diff --git a/core/deserialization.common.jvm/src/org/jetbrains/kotlin/SpecialJvmAnnotations.kt b/core/deserialization.common.jvm/src/org/jetbrains/kotlin/SpecialJvmAnnotations.kt index f2f2247ccfa..a20f4a7b42e 100644 --- a/core/deserialization.common.jvm/src/org/jetbrains/kotlin/SpecialJvmAnnotations.kt +++ b/core/deserialization.common.jvm/src/org/jetbrains/kotlin/SpecialJvmAnnotations.kt @@ -5,9 +5,11 @@ package org.jetbrains.kotlin +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName object SpecialJvmAnnotations { val SPECIAL_ANNOTATIONS: Set = listOf( @@ -18,4 +20,22 @@ object SpecialJvmAnnotations { JvmAnnotationNames.RETENTION_ANNOTATION, JvmAnnotationNames.DOCUMENTED_ANNOTATION ).mapTo(mutableSetOf(), ClassId::topLevel) + + val JAVA_LANG_ANNOTATION_REPEATABLE = ClassId.topLevel(JvmAnnotationNames.REPEATABLE_ANNOTATION) + + fun isAnnotatedWithContainerMetaAnnotation(klass: KotlinJvmBinaryClass): Boolean { + var result = false + klass.loadClassAnnotations(object : KotlinJvmBinaryClass.AnnotationVisitor { + override fun visitAnnotation(classId: ClassId, source: SourceElement): KotlinJvmBinaryClass.AnnotationArgumentVisitor? { + if (classId == JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_META_ANNOTATION) { + result = true + } + return null + } + + override fun visitEnd() { + } + }, null) + return result + } }