From 77e197d46a230e457b2f961ecd253148cacc97dc Mon Sep 17 00:00:00 2001 From: Sergej Jaskiewicz Date: Wed, 23 Nov 2022 14:22:31 +0100 Subject: [PATCH] [IR] Add documentation comments for IdSignatures --- .../jetbrains/kotlin/ir/util/IdSignature.kt | 299 ++++++++++++++++-- 1 file changed, 280 insertions(+), 19 deletions(-) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IdSignature.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IdSignature.kt index 173a01f08a0..70d680bd36e 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IdSignature.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IdSignature.kt @@ -11,8 +11,219 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.ir.symbols.IrFileSymbol import org.jetbrains.kotlin.name.FqName +/** + * [IdSignature] is a unique key that corresponds to each Kotlin Declaration. It is used to reference declarations in klib. + * It has to be stable and unique. It’s a bit similar to JVM signature but more Kotlin-specific. + * + * In general, distinguishable declarations in Kotlin have to have different signatures. + * + * Some way to look at klib could be a map of [IdSignature] as a key and [org.jetbrains.kotlin.ir.declarations.IrDeclaration] as a value. + * + * There is a bunch of existing signatures which are used to reference different kinds of declaration. + * Some of them may look weird because of some Kotlin specifics on one hand, and on the other hand, klib structure now is something similar + * to a russian doll, where an inner declaration is nested inside its parent (a member inside a class), so we need a way to figure out + * from a signature what the signature of its parent is. Moving to a flat structure could simplify signatures. + * + * One important definition needed is a *publicly-accessible declaration*, which means that there is a unique reference to a declaration `D` + * outside the file in which the declaration `D` is declared, and the declaration `D` could be navigated across different files + * (and modules) using that reference. + * + * ## Signature computation + * First, let's consider a top-level non-private class `C` in a package `foo.bar`: + * ```kotlin + * package foo.bar + * public class C { + * fun qux() { ... } + * val p + * } + * ``` + * + * The signatures of the class `C` and its type parameters `TPn` are going to be: + * ``` + * CommonSignature { + * packageFqn = 'foo.bar' + * classFqn = 'C' + * hashCode = null + * flags = 0 + * } + * + * CompositeSignature { // TP1 + * container = CommonSignature { + * packageFqn = 'foo.bar' + * classFqn = 'C' + * hashCode = null + * flags = 0 + * } + * inner = LocalSignature { + * localFqn = '' + * hashCode = 0 + * } + * } + * + * CompositeSignature { // TP2 + * container = CommonSignature { + * packageFqn = 'foo.bar' + * classFqn = 'C' + * hashCode = null + * flags = 0 + * } + * inner = LocalSignature { + * localFqn = '' + * hashCode = 1 + * } + * } + * ``` + * + * The signature of the method `qux()` is: + * ``` + * CommonSignature { + * packageFqn = 'foo.bar' + * classFqn = 'C.qux' + * hashCode = 0x..... // some hash code corresponding to the qux's signature + * flags = 0 + * } + * ``` + * + * The signature of the property `p` and the get-accessor of `p` are: + * ``` + * CommonSignature { + * packageFqn = 'foo.bar' + * classFqn = 'C.p' + * hashCode = 0x..... // some hash code corresponding to the p's signature + * flags = 0 + * } + * + * AccessorSignature { + * propertySignature = CommonSignature { + * packageFqn = 'foo.bar' + * classFqn = 'C.p' + * hashCode = 0x..... // some hash code corresponding to the p's signature + * flags = 0 + * } + * accessorSignature = CommonSignature { + * packageFqn = 'foo.bar' + * classFqn = 'C.p.' + * hashCode = 0x..... // some hash code corresponding to the p.get's signature + * flags = 0 + * } + * } + * + * CompositeSignature { + * container = CommonSignature { + * packageFqn = 'foo.bar' + * classFqn = 'C.p' + * hashCode = 0x..... // some hash code corresponding to the p's signature + * flags = 0 + * } + * inner = LocalSignature { + * localFqn = '' + * hashCode = null + * } + * } + * ``` + * + * So, most of the cases are covered with [CommonSignature] and [AccessorSignature]. + * Now let take a look into less trivial cases. + * + * ```kotlin + * // f1.kt + * package foo.bar + * + * private class P { + * + * } + * + * private fun qux() { ... } + * + * // f2.kt + * package foo.bar + * + * private fun qux() { ... } + * + * private class P { + * + * } + * ``` + * + * Here the signatures of `P` and `qux()` in `f1.kt` are going to be: + * ``` + * CompositeSignature { + * container = FileSignature { + * file = f1.kt + * } + * inner = CommonSignature { + * packageFqn = 'foo.bar' + * classFqn = 'P' + * hashCode = null + * flags = 0 + * } + * } + * + * + * CompositeSignature { + * container = FileSignature { + * file = f1.kt + * } + * inner = CommonSignature { + * packageFqn = 'foo.bar' + * classFqn = 'qux' + * hashCode = 0x.... + * flags = 0 + * } + * } + * ``` + * + * So, here we need a special factor `file` that allows us to distinguish signatures with the same FQN in different files. + * In a klib such file-private declarations can’t be referenced from files other than the file where the private declaration is declared in. + * + * The last case is a local declaration: + * ```kotlin + * package foo.bar + * fun qux() { + * class L { + * fun meh() { .. } + * } + * } + * ``` + * + * Here the signatures are going to be: + * ``` + * CommonSignature { // qux() + * packageFqn = 'foo.bar' + * classFqn = 'qux' + * hashCode = 0x.... + * flags = 0 + * } + * + * FileLocalSignature { // class L + * container = CommonSignature { // qux() + * packageFqn = 'foo.bar' + * classFqn = 'qux' + * hashCode = 0x.... + * flags = 0 + * } + * id = 0 + * } + * + * FileLocalSignature { // L.meh() + * container = FileLocalSignature { // class L + * container = CommonSignature { // qux() + * packageFqn = 'foo.bar' + * classFqn = 'qux' + * hashCode = 0x.... + * flags = 0 + * } + * id = 0 + * } + * id = 0x123... // hash of L.meh() signature + * } + * ``` + */ sealed class IdSignature { + /** + * Used for some special kinds of declarations like `expect`, or synthetic Java accessors. + */ enum class Flags(val recursive: Boolean) { IS_EXPECT(true), IS_JAVA_FOR_KOTLIN_OVERRIDE_PROPERTY(false), @@ -24,7 +235,7 @@ sealed class IdSignature { } /** - * Means that signature has cross-module visibility. In other words referencing declaration could found in klib if property is `true` + * Whether the signature has cross-module visibility. */ abstract val isPubliclyVisible: Boolean @@ -53,6 +264,9 @@ sealed class IdSignature { override fun toString(): String = "${if (isLocal) "local " else ""}${render()}" + /** + * This signature corresponds to a publicly accessible Kotlin declaration. + */ class CommonSignature(val packageFqName: String, val declarationFqName: String, val id: Long?, val mask: Long) : IdSignature() { override val isPubliclyVisible: Boolean get() = true @@ -103,6 +317,9 @@ sealed class IdSignature { override fun hashCode(): Int = hashCode } + /** + * This signature is a container that contains 2 signatures ([container] and [inner] parts) + */ class CompositeSignature(val container: IdSignature, val inner: IdSignature) : IdSignature() { override val isPubliclyVisible: Boolean get() = true @@ -143,6 +360,15 @@ sealed class IdSignature { } + /** + * This is a special signature to reference accessors of a property. + * It differs from [CommonSignature] because otherwise it’s not clear how to compute the container. + * + * TODO: Could be replaced with [CompositeSignature] + * + * @property propertySignature A signature of the property that this accessor corresponds to. + * @property accessorSignature A signature of the accessor function for the property. + */ class AccessorSignature(val propertySignature: IdSignature, val accessorSignature: CommonSignature) : IdSignature() { override val isPubliclyVisible: Boolean get() = true @@ -167,6 +393,16 @@ sealed class IdSignature { override fun hashCode(): Int = hashCode } + /** + * Used to represent a file which is required in case of top-level private declarations. + * This signature is needed because 2 different top-level private declarations can have the same FQN. + * + * This signature is not navigatable through files. + * + * @property id A unique object to differentiate two file signatures with the same names. For example, an [IrFileSymbol]. + * @property fqName The name of the package this file belongs to. + * @property fileName The name/path of the file entry. + */ class FileSignature( private val id: Any, private val fqName: FqName, @@ -205,6 +441,11 @@ sealed class IdSignature { get() = false } + /** + * This signature represents some internal part of a declaration, like a backing field or a type parameter. + * + * This signature is not navigatable through files. + */ class LocalSignature(val localFqn: String, val hashSig: Long?, val description: String?) : IdSignature() { override val isPubliclyVisible: Boolean get() = false @@ -251,23 +492,28 @@ sealed class IdSignature { } } - // KT-42020 - // This special signature is required to disambiguate fake overrides 'foo(x: T)[T = String]' and 'foo(x: String)' in the code below: - // - // open class Base { - // fun foo(x: T) {} - // fun foo(x: String) {} - // } - // - // class Derived : Base() - // - // (NB similar clash is possible for generic member extension properties as well) - // - // For each fake override 'foo' we collect non-fake overrides overridden by 'foo' - // such that their value parameter types contain type parameters of 'Base', - // sorted by the fully-qualified name of the containing class. - // - // NB this special case of IdSignature is JVM-specific. + /** + * [KT-42020](https://youtrack.jetbrains.com/issue/KT-42020) + * + * This special signature is required to disambiguate fake overrides 'foo(x: T)[T = String]' and 'foo(x: String)' in the code below: + * + * ```kotlin + * open class Base { + * fun foo(x: T) {} + * fun foo(x: String) {} + * } + * + * class Derived : Base() + * ``` + * + * NB: A similar clash is possible for generic member extension properties as well + * + * For each fake override `foo` we collect non-fake overrides overridden by `foo` + * such that their value parameter types contain type parameters of 'Base', + * sorted by the fully-qualified name of the containing class. + * + * NB: this special case of [IdSignature] is JVM-specific. + */ class SpecialFakeOverrideSignature( val memberSignature: IdSignature, val overriddenSignatures: List @@ -307,6 +553,14 @@ sealed class IdSignature { override fun hashCode(): Int = hashCode } + /** + * A deprecated signature used for local classes and their members to make it possible to build fake overridden declarations for such + * local classes. + * + * This signature is not navigatable through files. + * + * @property id A hash of the member’s signature, not an ordered index, because it has to be stable. + */ class FileLocalSignature(val container: IdSignature, val id: Long, val description: String? = null) : IdSignature() { override val isPubliclyVisible: Boolean get() = false @@ -338,7 +592,11 @@ sealed class IdSignature { override fun hashCode(): Int = hashCode } - // Used to reference local variable and value parameters in function + /** + * Used to reference local declarations like a variable, value parameters, or anonymous initializers. + * + * This signature is not navigatable through files. + */ class ScopeLocalDeclaration(val id: Int, _description: String? = null) : IdSignature() { val description: String = _description ?: "" @@ -364,6 +622,9 @@ sealed class IdSignature { override fun hashCode(): Int = id } + /** + * A special signature to reference lowered declarations in PIR incremental cache. + */ class LoweredDeclarationSignature(val original: IdSignature, val stage: Int, val index: Int) : IdSignature() { override val isPubliclyVisible: Boolean get() = original.isPubliclyVisible