From aaf30c269517769b1585ba575ce67f367cbefb27 Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Wed, 11 Jan 2023 09:58:14 +0200 Subject: [PATCH] [FIR] Warn about unexpanded in extractDeprecationAnnotationInfoPerUseSite This code may be called during status resolution, but it can also be called during `COMPILER_REQUIRED_ANNOTATIONS`. In the latter case we can't expand typealiases. If we do try to expand typealiases if we can, and leave them unexpanded otherwise, then we still may have a problem. Consider the FirBlackBoxCodegenTestGenerated.Reflection.Annotations.Repeatable .testKotlinAnnotation test. In this test we get the following cycle: ``` import kotlin.annotation.AnnotationTarget.* // <- requires TYPEALIAS @SinceKotlin("1.1") // <- requires String AnnotationTarget.TYPEALIAS @IntrinsicConstEvaluation // <- fully expanding this requires fun String.compareTo() // building it @Target(AnnotationTarget.CONSTRUCTOR) // <- requires annotation class IntrinsicConstEvaluation // AnnotationTarget // in general // The cycle repeats ``` Or in words if the picture isn't clear: - Transform `import kotlin.annotation.AnnotationTarget.*` - Find and deserialize class `kotlin/annotation/AnnotationTarget` - Load the `TYPEALIAS` field - Load its annotation `@SinceKotlin("1.1")` - Find and deserialize `kotlin/String` for `"1.1"` - Load `String.compareTo` - Load its annotation `kotlin/internal/IntrinsicConstEvaluation` >>> At this moment the `extractDeprecationAnnotationInfoPerUseSite` function is called, and ideally we'd want to fullyExpand `IntrinsicConstEvaluation` - While calling `toAnnotationClassId` for it, we find and deserialize `IntrinsicConstEvaluation` - `IntrinsicConstEvaluation` has `@Target` - At this moment we find and deserialize `AnnotationTarget` again. But This time loading `@SinceKotlin("1.1")` succeeds, because when loading `kotlin/String` we first create the symbol, then cache it, then call `postCompute`. It does not happen with `AnnotationTarget`, because it calls `findAndDeserializeClass` during `createValue`, not `postCompute`. - Thus, we have duplicate `AnnotationClass`-es, and they fail in fir2ir with "symbol is already bound" We can't move `findAndDeserializeClass` into `postCompute`, because during this process when we try to access the already created and cached symbol for `AnnotationTarget` (with yet empty fir) we are trying to build the declared member scope by this `symbol.fir`. --- .../org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt index c3933801062..c94a49df5c8 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt @@ -181,6 +181,10 @@ private fun FirAnnotation.getDeprecationLevel(): DeprecationLevelValue? { private fun List.extractDeprecationAnnotationInfoPerUseSite( session: FirSession, fromJava: Boolean ): DeprecationAnnotationInfoPerUseSiteStorage { + // NB: We can't expand typealiases (`toAnnotationClassId`), because it + // requires `lookupTag.tySymbol()`, but we can have cycles in annotations. + // See the commit message for an example. + @Suppress("RemoveExplicitTypeArguments") val annotations = buildList> { mapAnnotationsWithClassIdTo(StandardClassIds.Annotations.Deprecated, this) { it to false }