[PL] Fix: Restore removal of unusable annotations directly in IR files
Now, given that the PL engine does not visit IR modules and IR files
anymore, it is not possible to remove unusable annotations if such
happen to appear directly in `IrFile`.
With this fix, it becomes possible.
^KT-73511
diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinker.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinker.kt
index 528887d..45fc71e 100644
--- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinker.kt
+++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinker.kt
@@ -7,6 +7,7 @@
import org.jetbrains.kotlin.backend.common.overrides.IrLinkerFakeOverrideProvider
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
+import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.SymbolTable
@@ -26,6 +27,14 @@
fun shouldBeSkipped(declaration: IrDeclaration): Boolean
/**
+ * Enqueue the file for processing by the partial linkage engine.
+ *
+ * Note: Enqueueing the file does not mean any declarations in this file are also enqueued.
+ * For enqueueing declarations it is always required to make explicit [enqueueDeclaration] calls.
+ */
+ fun enqueueFile(file: IrFile)
+
+ /**
* Enqueue the declaration for processing by the partial linkage engine.
*/
fun enqueueDeclaration(declaration: IrDeclaration)
@@ -61,6 +70,7 @@
val DISABLED = object : PartialLinkageSupportForLinker {
override val isEnabled get() = false
override fun shouldBeSkipped(declaration: IrDeclaration) = true
+ override fun enqueueFile(file: IrFile) = Unit
override fun enqueueDeclaration(declaration: IrDeclaration) = Unit
override fun exploreClassifiers(fakeOverrideBuilder: IrLinkerFakeOverrideProvider) = Unit
override fun exploreClassifiersInInlineLazyIrFunction(function: IrFunction) = Unit
diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinkerImpl.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinkerImpl.kt
index 3401f58..2d62164 100644
--- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinkerImpl.kt
+++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartialLinkageSupportForLinkerImpl.kt
@@ -10,6 +10,7 @@
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
+import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageConfig
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageLogLevel
@@ -33,12 +34,27 @@
private val stubGenerator = MissingDeclarationStubGenerator(builtIns)
private val classifierExplorer = ClassifierExplorer(builtIns, stubGenerator)
private val patcher = PartiallyLinkedIrTreePatcher(builtIns, classifierExplorer, stubGenerator, logger)
+
+ /**
+ * The queue of IR files to remove unusable annotations.
+ *
+ * Note: The fact that an IR file is in this queue does not automatically mean that
+ * the declarations of this file are going to be processed/patched by the PL engine.
+ * To process the declarations, they need to be explicitly added to the appropriate queue: [declarationsEnqueuedForProcessing].
+ */
+ private val filesEnqueuedForProcessing = hashSetOf<IrFile>()
+
+ /** The queue of IR declarations to be processed/patched by the PL engine. */
private val declarationsEnqueuedForProcessing = hashSetOf<IrDeclaration>()
override val isEnabled get() = true
override fun shouldBeSkipped(declaration: IrDeclaration) = patcher.shouldBeSkipped(declaration)
+ override fun enqueueFile(file: IrFile) {
+ filesEnqueuedForProcessing += file
+ }
+
override fun enqueueDeclaration(declaration: IrDeclaration) {
declarationsEnqueuedForProcessing += declaration
}
@@ -68,6 +84,9 @@
stubGenerator.getDeclaration(symbol)
}
+ // Patch IR files (without visiting contained declarations).
+ patcher.removeUnusableAnnotationsFromFiles(filesEnqueuedForProcessing.getCopyAndClear())
+
// Patch all IR declarations scheduled so far.
patcher.patchDeclarations(declarationsEnqueuedForProcessing.getCopyAndClear())
diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt
index 25c2ac3..3ddb810 100644
--- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt
+++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt
@@ -58,6 +58,18 @@
fun shouldBeSkipped(declaration: IrDeclaration): Boolean = PLModule.determineModuleFor(declaration).shouldBeSkipped
+ fun removeUnusableAnnotationsFromFiles(files: Collection<IrFile>) {
+ for (file in files) {
+ val currentFile: PLFile = PLFile.IrBased(file)
+
+ // Optimization: Don't patch declarations from stdlib/built-ins.
+ if (currentFile.module.shouldBeSkipped)
+ continue
+
+ with(ExpressionTransformer(currentFile)) { file.filterUnusableAnnotations() }
+ }
+ }
+
fun patchDeclarations(declarations: Collection<IrDeclaration>) {
val declarationsGroupedByDirectParent = declarations.groupBy { it.parent }
@@ -872,7 +884,7 @@
null
}
- private fun <T> T.filterUnusableAnnotations() where T : IrMutableAnnotationContainer, T : IrSymbolOwner {
+ fun <T> T.filterUnusableAnnotations() where T : IrMutableAnnotationContainer, T : IrSymbolOwner {
if (annotations.isNotEmpty()) {
annotations = annotations.filterTo(ArrayList(annotations.size)) { annotation ->
// Visit the annotation as an expression.
diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt
index ae83751..44688c4 100644
--- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt
+++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt
@@ -207,7 +207,14 @@
while (filesWithPendingTopLevels.isNotEmpty()) {
val pendingFileDeserializationState = filesWithPendingTopLevels.first()
- pendingFileDeserializationState.fileDeserializer.deserializeFileImplicitDataIfFirstUse()
+ if (pendingFileDeserializationState.fileDeserializer.deserializeFileImplicitDataIfFirstUse()) {
+ // Schedule the IR file for processing by the PL engine only when the implicit file data
+ // is deserialized for the first time.
+ //
+ // Note: Enqueueing the file does not mean all top-level declarations in this file are
+ // also enqueued. This is done separately in `FileDeserializationState.deserializeAllFileReachableTopLevel()`.
+ linker.partialLinkageSupport.enqueueFile(pendingFileDeserializationState.file)
+ }
pendingFileDeserializationState.deserializeAllFileReachableTopLevel()
filesWithPendingTopLevels.remove(pendingFileDeserializationState)
diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt
index 0ff43a1..852f35c 100644
--- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt
+++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt
@@ -37,7 +37,8 @@
) {
val reversedSignatureIndex = fileProto.declarationIdList.associateBy { symbolDeserializer.deserializeIdSignature(it) }
- private var annotations: List<ProtoConstructorCall>? = fileProto.annotationList
+ /** Once deserialized this property is set to `null`. */
+ private var protoAnnotationsPendingDeserialization: List<ProtoConstructorCall>? = fileProto.annotationList
fun deserializeDeclaration(idSig: IdSignature): IrDeclaration {
return declarationDeserializer.deserializeDeclaration(loadTopLevelDeclarationProto(idSig)).also {
@@ -50,11 +51,23 @@
return libraryFile.declaration(idSigIndex)
}
- fun deserializeFileImplicitDataIfFirstUse() {
- annotations?.let {
+ /**
+ * Deserializes file-level annotations for the current [IrFile].
+ *
+ * The actual deserialization happens just once on the first invocation.
+ * The subsequent invocations have no effect.
+ *
+ * @return If the annotations have been actually deserialized on this invocation.
+ */
+ fun deserializeFileImplicitDataIfFirstUse(): Boolean {
+ protoAnnotationsPendingDeserialization?.let {
file.annotations += declarationDeserializer.deserializeAnnotations(it)
- annotations = null
+ protoAnnotationsPendingDeserialization = null
+
+ return true
}
+
+ return false
}
}