[PL] Deep rework of the partial linkage

1. Leaving no unbound symbols in the IR tree, KT-54491:
   - All unbound symbols are bound to synthetic stub declarations
   - Improved detection of the root cause for every partially linked classifier
   - Improved error messages

2. Visibility valiation, KT-54469

3. Always check deserialized symbols:
   If the deserialized symbol mismatches the symbol kind at the call site in the deserializer then generate and reference another symbol with the same signature. In case PL is off, just throw IrSymbolTypeMismatchException.

4. Handle class inheritance violation:
   - Detect illegal inheritance (ex: inheriting from a final class)
   - Detect invalid constructor delegation (ex: delegating to another class than the direct superclass)
   - Simplification: Reduce the number of PartialLinkageCase subclasses
   - Reworked error message generation to have shorter and clearer messages

5. Handle class transformations and all known side-effects, examples:
   - nested <-> inner
   - class <-> enum/object
   - adding/removing subclasses of sealed class
   - adding/removing enum entries

6. Check direct instantiation of abstract class.
   Such instantiation could be possible if a class was non-abstract in the previous version of a library.

7. Handle unlinked annotations on declarations.
   Such annotations are removed from the IR. The appropriate compiler error message is produced for every individual case.

8. Handle value argument count mismatch at call sites

9. Handle calling suspend function from non-suspend context.
   This could happen if a suspen function was non-suspend in the previous version of a library.

10. Handle overriding inline callables.
    Only the leaf final callable can be marked with `inline`.

11. Detect illegal non-local returns from noinline/crossinline lambdas.
This commit is contained in:
Dmitriy Dolovov
2022-10-17 18:57:58 +02:00
committed by Space Team
parent 974ee3139c
commit 2a4d880037
69 changed files with 3526 additions and 1397 deletions
@@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization.linkerissues
package org.jetbrains.kotlin.backend.common.linkage.issues
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
import org.jetbrains.kotlin.ir.symbols.IrSymbol
@@ -3,13 +3,13 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization.linkerissues
package org.jetbrains.kotlin.backend.common.linkage.issues
import org.jetbrains.kotlin.analyzer.CompilationErrorException
import org.jetbrains.kotlin.backend.common.serialization.IrModuleDeserializer
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.PotentialConflictKind.*
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.PotentialConflictKind.Companion.mostSignificantConflictKind
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.PotentialConflictReason.Companion.mostSignificantConflictReasons
import org.jetbrains.kotlin.backend.common.linkage.issues.PotentialConflictKind.*
import org.jetbrains.kotlin.backend.common.linkage.issues.PotentialConflictKind.Companion.mostSignificantConflictKind
import org.jetbrains.kotlin.backend.common.linkage.issues.PotentialConflictReason.Companion.mostSignificantConflictReasons
import org.jetbrains.kotlin.ir.linkage.KotlinIrLinkerInternalException
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.IdSignature
@@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization.linkerissues
package org.jetbrains.kotlin.backend.common.linkage.issues
import org.jetbrains.kotlin.backend.common.serialization.IrModuleDeserializer
import org.jetbrains.kotlin.backend.common.serialization.IrModuleDeserializerKind
@@ -3,41 +3,18 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization.linkerissues
package org.jetbrains.kotlin.backend.common.linkage.issues
import org.jetbrains.kotlin.backend.common.serialization.KotlinIrLinker
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.allUnbound
import org.jetbrains.kotlin.ir.util.irMessageLogger
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@OptIn(ExperimentalContracts::class)
internal inline fun <reified T : IrSymbol> checkSymbolType(symbol: IrSymbol): T {
contract {
returns() implies (symbol is T)
}
if (symbol !is T) throw IrSymbolTypeMismatchException(T::class.java, symbol) else return symbol
}
@OptIn(ExperimentalContracts::class)
internal inline fun <reified T : IrAnnotationContainer> checkErrorNodesAllowed(errorNodesAllowed: Boolean) {
contract {
returns() implies errorNodesAllowed
}
if (!errorNodesAllowed) throw IrDisallowedErrorNode(T::class.java)
}
// N.B. Checks for absence of unbound symbols only when unbound symbols are not allowed.
fun KotlinIrLinker.checkNoUnboundSymbols(symbolTable: SymbolTable, whenDetected: String) {
if (!partialLinkageSupport.partialLinkageEnabled)
messageLogger.checkNoUnboundSymbols(symbolTable, whenDetected)
}
fun KotlinIrLinker.checkNoUnboundSymbols(symbolTable: SymbolTable, whenDetected: String): Unit =
messageLogger.checkNoUnboundSymbols(symbolTable, whenDetected)
// N.B. Always checks for absence of unbound symbols. The condition whether this check should be applied is controlled outside.
fun IrMessageLogger.checkNoUnboundSymbols(symbolTable: SymbolTable, whenDetected: String) {
@@ -0,0 +1,309 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.linkage.partial
import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageUtils.isEffectivelyMissingLazyIrDeclaration
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.StandardNames.BUILT_INS_PACKAGE_FQ_NAME
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.linkage.partial.ExploredClassifier
import org.jetbrains.kotlin.ir.linkage.partial.ExploredClassifier.Unusable
import org.jetbrains.kotlin.ir.linkage.partial.ExploredClassifier.Unusable.*
import org.jetbrains.kotlin.ir.linkage.partial.ExploredClassifier.Usable
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.Module as PLModule
internal class ClassifierExplorer(private val builtIns: IrBuiltIns, private val stubGenerator: MissingDeclarationStubGenerator) {
private val exploredSymbols = ExploredClassifiers()
private val permittedAnnotationArrayParameterSymbols: Set<IrClassSymbol> by lazy {
setOf(
builtIns.stringClass, // kotlin.String
builtIns.kClassClass // kotlin.reflect.KClass
)
}
private val permittedAnnotationParameterSymbols: Set<IrClassSymbol> by lazy {
buildSet {
this += permittedAnnotationArrayParameterSymbols
PrimitiveType.values().forEach {
addIfNotNull(builtIns.findClass(it.typeName, BUILT_INS_PACKAGE_FQ_NAME)) // kotlin.<primitive>
addIfNotNull(builtIns.findClass(it.arrayTypeName, BUILT_INS_PACKAGE_FQ_NAME)) // kotlin.<primitive>Array
}
UnsignedType.values().forEach {
addIfNotNull(builtIns.findClass(it.typeName, BUILT_INS_PACKAGE_FQ_NAME)) // kotlin.U<signed>
addIfNotNull(builtIns.findClass(it.arrayClassId.shortClassName, BUILT_INS_PACKAGE_FQ_NAME)) // kotlin.U<signed>Array
}
}
}
private val stdlibModule by lazy { PLModule.determineModuleFor(builtIns.anyClass.owner) }
fun exploreType(type: IrType): Unusable? = type.exploreType(visitedSymbols = hashSetOf()).asUnusable()
fun exploreSymbol(symbol: IrClassifierSymbol): Unusable? = symbol.exploreSymbol(visitedSymbols = hashSetOf()).asUnusable()
fun exploreIrElement(element: IrElement) {
element.acceptChildrenVoid(IrElementExplorer { it.exploreType(visitedSymbols = hashSetOf()) })
}
/** Explore the IR type to find the first cause why this type should be considered as unusable. */
private fun IrType.exploreType(visitedSymbols: MutableSet<IrClassifierSymbol>): ExploredClassifier {
return when (this) {
is IrSimpleType -> classifier.exploreSymbol(visitedSymbols).asUnusable()
?: arguments.firstUnusable { it.typeOrNull?.exploreType(visitedSymbols) }
?: Usable
is IrDynamicType -> Usable
else -> throw IllegalArgumentException("Unsupported IR type: ${this::class.java}, $this")
}
}
/** Explore the IR classifier symbol to find the first cause why this symbol should be considered as unusable. */
private fun IrClassifierSymbol.exploreSymbol(visitedSymbols: MutableSet<IrClassifierSymbol>): ExploredClassifier {
exploredSymbols[this]?.let { result ->
// Already explored and registered symbol.
return result
}
if (!isBound) {
stubGenerator.getDeclaration(this) // Generate a stub and bind the symbol immediately.
return exploredSymbols.registerUnusable(this, MissingClassifier(this))
}
(owner as? IrLazyClass)?.let { lazyIrClass ->
val isEffectivelyMissingClassifier =
/* Lazy IR declaration is present but wraps a special "not found" class descriptor. */
lazyIrClass.descriptor is NotFoundClasses.MockClassDescriptor
/* The outermost class containing the lazy IR declaration is private, which normally should not happen
* because the declaration is exported from the module. */
|| lazyIrClass.isEffectivelyMissingLazyIrDeclaration()
if (isEffectivelyMissingClassifier)
return exploredSymbols.registerUnusable(this, MissingClassifier(this))
}
if (!visitedSymbols.add(this)) {
return Usable // Recursion avoidance.
}
val cause: Unusable? = when (val classifier = owner) {
is IrClass -> when (PLModule.determineModuleFor(owner as IrClass)) {
is PLModule.MissingDeclarations -> return exploredSymbols.registerUnusable(this, MissingClassifier(this))
stdlibModule, PLModule.SyntheticBuiltInFunctions -> {
// Don't run any additional checks if the class is from stdlib.
null
}
else -> {
// Class from non-stdlib module.
val directSuperTypeSymbols = hashSetOf<IrClassSymbol>()
classifier.annotationConstructorsIfApplicable?.firstUnusable { it.exploreAnnotationConstructor(visitedSymbols) }
?: classifier.outerClassSymbolIfApplicable?.exploreSymbol(visitedSymbols).asUnusable()
?: classifier.typeParameters.firstUnusable { it.symbol.exploreSymbol(visitedSymbols) }
?: classifier.superTypes.firstUnusable { superType ->
directSuperTypeSymbols.addIfNotNull(superType.asSimpleType()?.classifier as? IrClassSymbol)
superType.exploreType(visitedSymbols)
}
?: classifier.exploreSuperClasses(directSuperTypeSymbols) // Check them all at once.
}
}
is IrTypeParameter -> classifier.superTypes.firstUnusable { it.exploreType(visitedSymbols) }
else -> null
}
val rootCause = when {
cause == null -> return exploredSymbols.registerUsable(this)
cause.symbol == this -> return exploredSymbols.registerUnusable(this, cause)
else -> when (cause) {
is DueToOtherClassifier -> cause.rootCause
is CanBeRootCause -> cause
}
}
return exploredSymbols.registerUnusable(this, DueToOtherClassifier(this, rootCause))
}
private fun IrConstructor.exploreAnnotationConstructor(visitedSymbols: MutableSet<IrClassifierSymbol>): Unusable? {
return valueParameters.firstUnusable { valueParameter ->
valueParameter.type.exploreType(visitedSymbols).asUnusable()
?: valueParameter.exploreAnnotationConstructorParameter(visitedSymbols, annotationClass = parentAsClass)
}
}
/** See also [org.jetbrains.kotlin.resolve.CompileTimeConstantUtils.isAcceptableTypeForAnnotationParameter] */
private fun IrValueParameter.exploreAnnotationConstructorParameter(
visitedSymbols: MutableSet<IrClassifierSymbol>,
annotationClass: IrClass
): Unusable? {
val parameterType = type.asSimpleType() ?: return null
val parameterClassSymbol = parameterType.classifier as IrClassSymbol
val parameterClass = parameterClassSymbol.owner
when {
parameterClass.isAnnotationClass -> {
// Recurse on another annotation.
parameterClassSymbol.exploreSymbol(visitedSymbols).asUnusable()?.let { return it }
}
parameterClass.isEnumClass || parameterClassSymbol in permittedAnnotationParameterSymbols -> return null // Permitted.
parameterClassSymbol == builtIns.arrayClass -> {
// Additional checks for array element type.
for (argument in parameterType.arguments) {
val argumentClassSymbol = (argument.typeOrNull?.asSimpleType() ?: continue).classifier as IrClassSymbol
val argumentClass = argumentClassSymbol.owner
when {
argumentClass.isAnnotationClass -> {
// Recurse on another annotation.
argumentClassSymbol.exploreSymbol(visitedSymbols).asUnusable()?.let { return it }
}
argumentClass.isEnumClass || argumentClassSymbol in permittedAnnotationArrayParameterSymbols -> continue // Permitted.
else -> return AnnotationWithUnacceptableParameter(annotationClass.symbol, argumentClassSymbol)
}
}
}
else -> return AnnotationWithUnacceptableParameter(annotationClass.symbol, parameterClassSymbol)
}
return null
}
private fun IrClass.exploreSuperClasses(superTypeSymbols: Set<IrClassSymbol>): Unusable? {
if (isInterface) {
// Can inherit only from other interfaces.
val realSuperClassSymbols = superTypeSymbols.filter { it != builtIns.anyClass && !it.owner.isInterface }
if (realSuperClassSymbols.isNotEmpty())
return InvalidInheritance(symbol, realSuperClassSymbols) // Interface inherits from a real class(es).
} else {
// Check the number of non-interface supertypes.
val superClassSymbols = superTypeSymbols.filter { !it.owner.isInterface }
val superClassSymbol = when (superClassSymbols.size) {
0 -> return null // It can be only Any.
1 -> superClassSymbols.first()
else -> return InvalidInheritance(symbol, superClassSymbols) // Class inherits from multiple classes.
}
// Super class can not be final or of illegal kind.
if (superClassSymbol != builtIns.anyClass
&& superClassSymbol != builtIns.enumClass // Enum class can't be explicitly inherited, only valid enum class can inherit it.
) {
val superClass = superClassSymbol.owner
// Note: Super-interfaces are already filtered out above.
val isInvalidInheritance = when (kind) {
ClassKind.INTERFACE,
ClassKind.ENUM_CLASS,
ClassKind.ANNOTATION_CLASS -> true
else -> isValue || when (superClass.kind) {
ClassKind.ENUM_CLASS -> kind != ClassKind.ENUM_ENTRY
ClassKind.CLASS -> superClass.modality == Modality.FINAL
else -> true
}
}
if (isInvalidInheritance)
return InvalidInheritance(symbol, superClassSymbols) // Invalid inheritance.
}
}
return null
}
companion object {
private val IrClass.annotationConstructorsIfApplicable: Sequence<IrConstructor>?
get() = if (isAnnotationClass) constructors else null
// Note: Don't consider any nested class automatically as unlinked if enclosing class is unlinked.
private val IrClass.outerClassSymbolIfApplicable: IrClassSymbol?
get() = if (isInner || isEnumEntry) (parent as? IrClass)?.symbol else null
private val IrTypeArgument.typeOrNull: IrType?
get() = (this as? IrTypeProjection)?.type
private fun IrType.asSimpleType() = this as? IrSimpleType
private fun ExploredClassifier?.asUnusable() = this as? Unusable
/** Iterate the collection and find the first unusable classifier. */
private inline fun <T> Iterable<T>.firstUnusable(transform: (T) -> ExploredClassifier?): Unusable? =
firstNotNullOfOrNull { transform(it).asUnusable() }
private inline fun <T> Sequence<T>.firstUnusable(transform: (T) -> ExploredClassifier?): Unusable? =
firstNotNullOfOrNull { transform(it).asUnusable() }
}
}
private class IrElementExplorer(private val visitType: (IrType) -> Unit) : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitValueParameter(declaration: IrValueParameter) {
visitType(declaration.type)
super.visitValueParameter(declaration)
}
override fun visitTypeParameter(declaration: IrTypeParameter) {
declaration.superTypes.forEach(visitType)
super.visitTypeParameter(declaration)
}
override fun visitFunction(declaration: IrFunction) {
visitType(declaration.returnType)
super.visitFunction(declaration)
}
override fun visitField(declaration: IrField) {
visitType(declaration.type)
super.visitField(declaration)
}
override fun visitVariable(declaration: IrVariable) {
visitType(declaration.type)
super.visitVariable(declaration)
}
override fun visitExpression(expression: IrExpression) {
visitType(expression.type)
super.visitExpression(expression)
}
override fun visitClassReference(expression: IrClassReference) {
visitType(expression.classType)
super.visitClassReference(expression)
}
override fun visitConstantObject(expression: IrConstantObject) {
expression.typeArguments.forEach(visitType)
super.visitConstantObject(expression)
}
override fun visitTypeOperator(expression: IrTypeOperatorCall) {
visitType(expression.typeOperand)
super.visitTypeOperator(expression)
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.linkage.partial
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.linkage.partial.ExploredClassifier
internal class ExploredClassifiers {
private val usableSymbols = HashSet<IrClassifierSymbol>()
private val unusableSymbols = HashMap<IrClassifierSymbol, ExploredClassifier.Unusable>()
operator fun get(symbol: IrClassifierSymbol): ExploredClassifier? =
if (symbol in usableSymbols) ExploredClassifier.Usable else unusableSymbols[symbol]
fun registerUnusable(symbol: IrClassifierSymbol, exploredClassifier: ExploredClassifier.Unusable): ExploredClassifier.Unusable {
unusableSymbols[symbol] = exploredClassifier
return exploredClassifier
}
fun registerUsable(symbol: IrClassifierSymbol): ExploredClassifier.Usable {
usableSymbols += symbol
return ExploredClassifier.Usable
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.linkage.partial
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrOverridableMember
import org.jetbrains.kotlin.ir.linkage.partial.IrUnimplementedOverridesStrategy
import org.jetbrains.kotlin.ir.linkage.partial.PartiallyLinkedDeclarationOrigin
internal object ImplementAsErrorThrowingStubs : IrUnimplementedOverridesStrategy {
override fun <T : IrOverridableMember> computeCustomization(overridableMember: T, parent: IrClass) =
if (overridableMember.modality == Modality.ABSTRACT
&& parent.modality != Modality.ABSTRACT
&& parent.modality != Modality.SEALED
) {
IrUnimplementedOverridesStrategy.Customization(
origin = PartiallyLinkedDeclarationOrigin.UNIMPLEMENTED_ABSTRACT_CALLABLE_MEMBER,
modality = parent.modality // Use modality of class for implemented callable member.
)
} else
IrUnimplementedOverridesStrategy.Customization.NO
}
@@ -0,0 +1,162 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.linkage.partial
import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageUtils.guessName
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.linkage.partial.PartiallyLinkedDeclarationOrigin
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.util.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.types.error.ErrorUtils
/**
* Generates the simplest possible stubs for missing declarations.
*
* Note: This is a special type of [IrProvider]. It should not be used in row with other IR providers, because it may bring to
* undesired situation when stubs for unbound fake override symbols are generated even before the corresponding call of
* [FakeOverrideBuilder.provideFakeOverrides] is made leaving no chance for proper linkage of fake overrides. This IR provider
* should be applied only after the fake overrides generation.
*/
internal class MissingDeclarationStubGenerator(private val builtIns: IrBuiltIns) : IrProvider {
private val commonParent by lazy {
IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(ErrorUtils.errorModule, FqName.ROOT)
}
private var declarationsToPatch = arrayListOf<IrDeclaration>()
fun grabDeclarationsToPatch(): Collection<IrDeclaration> {
val result = declarationsToPatch
declarationsToPatch = arrayListOf()
return result
}
override fun getDeclaration(symbol: IrSymbol): IrDeclaration {
require(!symbol.isBound)
return when (symbol) {
is IrClassSymbol -> generateClass(symbol)
is IrSimpleFunctionSymbol -> generateSimpleFunction(symbol)
is IrConstructorSymbol -> generateConstructor(symbol)
is IrPropertySymbol -> generateProperty(symbol)
is IrEnumEntrySymbol -> generateEnumEntry(symbol)
is IrTypeAliasSymbol -> generateTypeAlias(symbol)
else -> throw NotImplementedError("Generation of stubs for ${symbol::class.java} is not supported yet")
}
}
private fun generateClass(symbol: IrClassSymbol): IrClass {
return builtIns.irFactory.createClass(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION,
symbol = symbol,
name = symbol.guessName(),
kind = ClassKind.CLASS,
visibility = DescriptorVisibilities.DEFAULT_VISIBILITY,
modality = Modality.OPEN
).apply {
setCommonParent()
createImplicitParameterDeclarationWithWrappedDescriptor()
}
}
private fun generateSimpleFunction(symbol: IrSimpleFunctionSymbol): IrSimpleFunction {
return builtIns.irFactory.createFunction(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION,
symbol = symbol,
name = symbol.guessName(),
visibility = DescriptorVisibilities.DEFAULT_VISIBILITY,
modality = Modality.FINAL,
returnType = builtIns.nothingType,
isInline = false,
isExternal = false,
isTailrec = false,
isSuspend = false,
isOperator = false,
isInfix = false,
isExpect = false
).setCommonParent()
}
private fun generateConstructor(symbol: IrConstructorSymbol): IrConstructor {
return builtIns.irFactory.createConstructor(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION,
symbol = symbol,
name = SpecialNames.INIT,
visibility = DescriptorVisibilities.DEFAULT_VISIBILITY,
returnType = builtIns.nothingType,
isInline = false,
isExternal = false,
isPrimary = false,
isExpect = false,
).setCommonParent()
}
private fun generateProperty(symbol: IrPropertySymbol): IrProperty {
return builtIns.irFactory.createProperty(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION,
symbol = symbol,
name = symbol.guessName(),
visibility = DescriptorVisibilities.DEFAULT_VISIBILITY,
modality = Modality.FINAL,
isVar = false,
isConst = false,
isLateinit = false,
isDelegated = false,
isExternal = false,
isExpect = false
).setCommonParent()
}
private fun generateEnumEntry(symbol: IrEnumEntrySymbol): IrEnumEntry {
return builtIns.irFactory.createEnumEntry(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION,
symbol = symbol,
name = symbol.guessName()
).setCommonParent()
}
private fun generateTypeAlias(symbol: IrTypeAliasSymbol): IrTypeAlias {
return builtIns.irFactory.createTypeAlias(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
symbol = symbol,
name = symbol.guessName(),
visibility = DescriptorVisibilities.DEFAULT_VISIBILITY,
expandedType = builtIns.nothingType,
isActual = true,
origin = PartiallyLinkedDeclarationOrigin.MISSING_DECLARATION,
).setCommonParent()
}
private fun <T : IrDeclaration> T.setCommonParent(): T {
parent = commonParent
declarationsToPatch += this
return this
}
private fun IrSymbol.guessName(): Name =
signature?.guessName(nameSegmentsToPickUp = 1)?.let(Name::guessByFirstCharacter) ?: PartialLinkageUtils.UNKNOWN_NAME
}
@@ -0,0 +1,563 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.linkage.partial
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.backend.common.linkage.partial.DeclarationKind.*
import org.jetbrains.kotlin.backend.common.linkage.partial.ExpressionKind.*
import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageUtils.DeclarationId.Companion.declarationId
import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageUtils.UNKNOWN_NAME
import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageUtils.guessName
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.linkage.partial.ExploredClassifier.Unusable
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.util.IdSignature.*
import org.jetbrains.kotlin.ir.util.isAnonymousObject
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.parentClassOrNull
import org.jetbrains.kotlin.name.SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.Module as PLModule
internal fun PartialLinkageCase.renderLinkageError(): String = buildString {
when (this@renderLinkageError) {
is UnusableClassifier -> unusableClassifier(cause, CauseRendering.Standalone, printIntermediateCause = false)
is MissingDeclaration -> noDeclarationForSymbol(missingDeclarationSymbol)
is DeclarationWithUnusableClassifier -> declarationWithUnusableClassifier(declarationSymbol, cause, forExpression = false)
is ExpressionWithUnusableClassifier -> expressionWithUnusableClassifier(expression, cause)
is ExpressionWithMissingDeclaration -> expression(expression) { noDeclarationForSymbol(missingDeclarationSymbol) }
is ExpressionHasDeclarationWithUnusableClassifier -> expression(expression) {
declarationWithUnusableClassifier(referencedDeclarationSymbol, cause, forExpression = true)
}
is ExpressionHasWrongTypeOfDeclaration -> expression(expression) {
wrongTypeOfDeclaration(actualDeclarationSymbol, expectedDeclarationDescription)
}
is MemberAccessExpressionArgumentsMismatch -> expression(expression) {
memberAccessExpressionArgumentsMismatch(
expression.symbol,
expressionHasDispatchReceiver,
functionHasDispatchReceiver,
expressionValueArgumentCount,
functionValueParameterCount
)
}
is SuspendableFunctionCallWithoutCoroutineContext -> expression(expression) {
suspendableCallWithoutCoroutine()
}
is IllegalNonLocalReturn -> illegalNonLocalReturn(expression, validReturnTargets)
is ExpressionHasInaccessibleDeclaration -> expression(expression) {
inaccessibleDeclaration(referencedDeclarationSymbol, declaringModule, useSiteModule)
}
is InvalidConstructorDelegation -> invalidConstructorDelegation(
constructorSymbol,
superClassSymbol,
unexpectedSuperClassConstructorSymbol
)
is AbstractClassInstantiation -> expression(constructorCall) { cantInstantiateAbstractClass(classSymbol) }
is UnimplementedAbstractCallable -> unimplementedAbstractCallable(callable)
}
}
private enum class DeclarationKind(val displayName: String) {
CLASS("class"),
INNER_CLASS("inner class"),
DATA_CLASS("data class"),
VALUE_CLASS("value class"),
INTERFACE("interface"),
FUN_INTERFACE("fun interface"),
ENUM_CLASS("enum class"),
ENUM_ENTRY("enum entry"),
ENUM_ENTRY_CLASS("enum entry class"),
ANNOTATION_CLASS("annotation class"),
OBJECT("object"),
ANONYMOUS_OBJECT("anonymous object"),
COMPANION_OBJECT("companion object"),
VARIABLE("variable"),
VALUE_PARAMETER("value parameter"),
FIELD("field"),
FIELD_OF_PROPERTY("backing field of property"),
PROPERTY("property"),
PROPERTY_ACCESSOR("property accessor"),
LAMBDA("lambda"),
FUNCTION("function"),
CONSTRUCTOR("constructor"),
TYPE_PARAMETER("type parameter"),
OTHER_DECLARATION("declaration");
}
private val IrSymbol.declarationKind: DeclarationKind
get() = when (this) {
is IrClassSymbol -> when (owner.kind) {
ClassKind.CLASS -> when {
owner.isData -> DATA_CLASS
owner.isAnonymousObject -> ANONYMOUS_OBJECT
owner.isInner -> INNER_CLASS
owner.isValue -> VALUE_CLASS
else -> CLASS
}
ClassKind.INTERFACE -> if (owner.isFun) FUN_INTERFACE else INTERFACE
ClassKind.ENUM_CLASS -> ENUM_CLASS
ClassKind.ENUM_ENTRY -> ENUM_ENTRY_CLASS
ClassKind.ANNOTATION_CLASS -> ANNOTATION_CLASS
ClassKind.OBJECT -> if (owner.isCompanion) COMPANION_OBJECT else OBJECT
}
is IrEnumEntrySymbol -> ENUM_ENTRY
is IrVariableSymbol -> VARIABLE
is IrValueParameterSymbol -> VALUE_PARAMETER
is IrFieldSymbol -> if (owner.correspondingPropertySymbol != null) FIELD_OF_PROPERTY else FIELD
is IrPropertySymbol -> PROPERTY
is IrSimpleFunctionSymbol -> when {
owner.correspondingPropertySymbol != null || signature is AccessorSignature -> PROPERTY_ACCESSOR
owner.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA -> LAMBDA
else -> FUNCTION
}
is IrConstructorSymbol -> CONSTRUCTOR
is IrTypeParameterSymbol -> TYPE_PARAMETER
else -> OTHER_DECLARATION
}
private enum class ExpressionKind(val prefix: String?, val postfix: String?) {
REFERENCE("Reference to", "can not be evaluated"),
CALLING(null, "can not be called"),
CALLING_INSTANCE_INITIALIZER("Instance initializer of", "can not be called"),
READING("Can not read value from", null),
WRITING("Can not write value to", null),
GETTING_INSTANCE("Can not get instance of", null),
TYPE_OPERATOR("Type operator expression", "can not be evaluated"),
ANONYMOUS_OBJECT_LITERAL("Anonymous object literal", "can not be evaluated"),
OTHER_EXPRESSION("Expression", "can not be evaluated")
}
private data class Expression(val kind: ExpressionKind, val referencedDeclarationKind: DeclarationKind?)
// More can be added for verbosity in the future.
private val IrExpression.expression: Expression
get() = when (this) {
is IrDeclarationReference -> when (this) {
is IrFunctionReference -> Expression(REFERENCE, symbol.declarationKind)
is IrPropertyReference,
is IrLocalDelegatedPropertyReference -> Expression(REFERENCE, PROPERTY)
is IrCall -> Expression(CALLING, symbol.declarationKind)
is IrConstructorCall,
is IrEnumConstructorCall,
is IrDelegatingConstructorCall -> Expression(CALLING, CONSTRUCTOR)
is IrClassReference -> Expression(REFERENCE, symbol.declarationKind)
is IrGetField -> Expression(READING, symbol.declarationKind)
is IrSetField -> Expression(WRITING, symbol.declarationKind)
is IrGetValue -> Expression(READING, symbol.declarationKind)
is IrSetValue -> Expression(WRITING, symbol.declarationKind)
is IrGetSingletonValue -> Expression(GETTING_INSTANCE, symbol.declarationKind)
else -> Expression(REFERENCE, OTHER_DECLARATION)
}
is IrInstanceInitializerCall -> Expression(CALLING_INSTANCE_INITIALIZER, classSymbol.declarationKind)
is IrTypeOperatorCall -> Expression(TYPE_OPERATOR, null)
else -> {
if (this is IrBlock && origin == IrStatementOrigin.OBJECT_LITERAL)
Expression(ANONYMOUS_OBJECT_LITERAL, null)
else
Expression(OTHER_EXPRESSION, null)
}
}
private fun IrSymbol.guessName(): String? {
fun IrElement.isCompanionWithDefaultName() = this is IrClass && isCompanion && name == DEFAULT_NAME_FOR_COMPANION_OBJECT
return signature
// First, try to guess name by the signature. This is the most reliable way, especially when the declaration itself is missing
// and the symbol owner is just an IR stub, which is always a direct child of the auxiliary package fragment.
?.let { signature ->
val nameSegmentsToPickUp = when {
signature is AccessorSignature -> 2 // property_name.accessor_name
this is IrConstructorSymbol -> if (owner.parent.isCompanionWithDefaultName()) 3 else 2 // class_name.<init> or class_name.Companion.<init>
this is IrEnumEntrySymbol -> 2 // enum_class_name.entry_name
owner.isCompanionWithDefaultName() -> 2 // class_name.Companion
else -> 1
}
signature.guessName(nameSegmentsToPickUp)
}
?: (owner as? IrDeclarationWithName)
// Lazy IR may not have signatures. Let's try to extract name from the declaration itself.
?.let { owner ->
when (owner) {
is IrSimpleFunction -> listOfNotNull(owner.correspondingPropertySymbol?.owner?.name, owner.name)
is IrConstructor -> {
val parent = owner.parentClassOrNull
when {
parent == null || parent.isAnonymousObject -> listOf(owner.name)
parent.isCompanionWithDefaultName() -> listOfNotNull(parent.parentClassOrNull?.name, parent.name, owner.name)
else -> listOf(parent.name, owner.name)
}
}
is IrEnumEntry -> listOfNotNull(owner.parentClassOrNull?.name, owner.name)
else -> listOf(owner.name)
}.joinToString(".")
}
}
private fun Appendable.signature(symbol: IrSymbol): Appendable {
var file: String? = null
val symbolRepresentation = symbol.signature?.render()
?: symbol.privateSignature?.let {
// Try to extract symbol name from private signature if no public signature is available.
// This could happen during visiting local IR entities declared inside function body.
when (it) {
is FileSignature -> null // Avoid printing FileSignature.
is CompositeSignature -> {
// Avoid printing full paths from FileSignature.
val container = it.container
if (container is FileSignature) {
file = PathUtil.getFileName(container.fileName).takeIf(String::isNotEmpty) ?: UNKNOWN_FILE
it.inner.render()
} else it.render()
}
else -> it.render()
}
}
?: (symbol.owner as? IrDeclarationWithName)?.let { lazyIrDeclaration ->
// Lazy IR declaration might not have any signature at all. So let's print anything helpful at least.
lazyIrDeclaration.declarationId?.let { "$it|?" /* We don't know the exact hash and mask to print them here. */ }
}
?: UNKNOWN_SYMBOL
append('\'').append(symbolRepresentation).append('\'')
if (file != null) append(" declared in file ").append(file)
return this
}
private const val UNKNOWN_SYMBOL = "<unknown symbol>"
private const val UNKNOWN_FILE = "<unknown file>"
private fun Appendable.declarationName(symbol: IrSymbol): Appendable =
append('\'').append(symbol.guessName() ?: UNKNOWN_NAME.asString()).append('\'')
private fun Appendable.declarationKind(symbol: IrSymbol, capitalized: Boolean): Appendable =
appendCapitalized(symbol.declarationKind.displayName, capitalized)
private fun Appendable.declarationKindName(symbol: IrSymbol, capitalized: Boolean): Appendable {
val declarationKind = symbol.declarationKind
appendCapitalized(declarationKind.displayName, capitalized)
when (declarationKind) {
ANONYMOUS_OBJECT -> return this
LAMBDA -> ((symbol.owner as? IrDeclaration)?.parent as? IrDeclaration)?.symbol?.let { parentDeclarationSymbol ->
return append(" in ").declarationKindName(parentDeclarationSymbol, capitalized = false)
}
else -> Unit
}
return append(" ").declarationName(symbol)
}
private fun Appendable.declarationNameIsKind(symbol: IrSymbol): Appendable =
declarationName(symbol).append(" is ").declarationKind(symbol, capitalized = false)
private fun Appendable.module(module: PLModule): Appendable =
append("module ").append(module.name)
private sealed interface CauseRendering {
// <subject> <reason>
object Standalone : CauseRendering
// <object> uses <subject>. <subject> <reason>
// or
// <object> uses <root subject> via <direct subject>. <root subject> <reason>
sealed interface UsedFromSomewhere : CauseRendering {
val objectText: String
val objectSymbol: IrSymbol?
}
// <object> := <declaration>
class UsedFromDeclaration(override val objectText: String, override val objectSymbol: IrSymbol) : UsedFromSomewhere
// <object> := <expression>
object UsedFromExpression : UsedFromSomewhere {
override val objectText = "Expression"
override val objectSymbol = null
}
}
private fun Appendable.unusableClassifier(
cause: Unusable,
rendering: CauseRendering,
printIntermediateCause: Boolean
): Appendable {
val (rootCause: Unusable.CanBeRootCause, intermediateCause: Unusable.DueToOtherClassifier?) = when (cause) {
is Unusable.CanBeRootCause -> cause to null
is Unusable.DueToOtherClassifier -> cause.rootCause to cause
}
// some shortcuts:
fun CauseRendering.UsedFromSomewhere.`object`() = append(objectText)
fun CauseRendering.UsedFromSomewhere.objectUses() = `object`().append(" uses ")
fun Appendable.subject(capitalized: Boolean) = declarationKindName(rootCause.symbol, capitalized)
fun Appendable.subjectKind(capitalized: Boolean) = declarationKind(rootCause.symbol, capitalized)
fun Appendable.via() = if (printIntermediateCause && intermediateCause != null)
append(" (via ").declarationKindName(intermediateCause.symbol, capitalized = false).append(")") else this
when (rootCause) {
// Case: Missing class.
is Unusable.MissingClassifier -> when (rendering) {
CauseRendering.Standalone -> noDeclarationForSymbol(rootCause.symbol)
is CauseRendering.UsedFromSomewhere -> with(rendering) {
objectUses().append("unlinked ").subjectKind(capitalized = false).append(" symbol ").signature(rootCause.symbol).via()
}
}
// Case: Invalid inheritance.
is Unusable.InvalidInheritance -> {
when (rootCause.superClassSymbols.size) {
// Subcase: Invalid super class.
1 -> {
fun Appendable.inheritsFromSuperClass(): Appendable {
val superClassSymbol = rootCause.superClassSymbols.single()
if (superClassSymbol.owner.modality == Modality.FINAL)
append(" inherits from final ")
else
append(" has illegal inheritance from ")
return declarationKindName(superClassSymbol, capitalized = false)
}
when (rendering) {
CauseRendering.Standalone -> subject(capitalized = true).inheritsFromSuperClass()
is CauseRendering.UsedFromSomewhere -> with(rendering) {
if (objectSymbol == rootCause.symbol) {
// 'rootCause.symbol' is already mentioned in `rendering.objectText`, so use shorter message
`object`().inheritsFromSuperClass()
} else {
objectUses().subject(capitalized = false).via().append(" that").inheritsFromSuperClass()
}
}
}
}
// Subcase: Inheritance from multiple classes (i.e. non-interfaces).
else -> {
fun Appendable.inheritsFromMultipleClasses(): Appendable {
val renderedSuperClasses = ArrayList<String>(rootCause.superClassSymbols.size)
rootCause.superClassSymbols.mapTo(renderedSuperClasses) { buildString { declarationName(it) } }
renderedSuperClasses.sort()
append(" simultaneously inherits from ").append(renderedSuperClasses.size.toString()).append(" classes: ")
renderedSuperClasses.joinTo(this)
return this
}
when (rendering) {
CauseRendering.Standalone -> subject(capitalized = true).inheritsFromMultipleClasses()
is CauseRendering.UsedFromSomewhere -> with(rendering) {
if (objectSymbol == rootCause.symbol) {
// 'rootCause.symbol' is already mentioned in `rendering.objectText`, so use shorter message
`object`().inheritsFromMultipleClasses()
} else {
objectUses().subject(capitalized = false).via().append(" that").inheritsFromMultipleClasses()
}
}
}
}
}
}
// Case: Annotation class that has unacceptable type in a parameter.
is Unusable.AnnotationWithUnacceptableParameter -> {
fun Appendable.unacceptableClassifier(): Appendable = append("non-annotation ")
.declarationKindName(rootCause.unacceptableClassifierSymbol, capitalized = false).append(" as a parameter")
when (rendering) {
CauseRendering.Standalone -> subject(capitalized = true).append(" has ").unacceptableClassifier()
is CauseRendering.UsedFromSomewhere -> with(rendering) {
if (objectSymbol == rootCause.symbol) {
// 'rootCause.symbol' is already mentioned in `rendering.objectText`, so use shorter message
objectUses().unacceptableClassifier().via()
} else
objectUses().subject(capitalized = false).via().append(" that has ").unacceptableClassifier()
}
}
}
}
return this
}
private fun Appendable.noDeclarationForSymbol(symbol: IrSymbol): Appendable =
append("No ").declarationKind(symbol, capitalized = false).append(" found for symbol ").signature(symbol)
private fun Appendable.declarationWithUnusableClassifier(
declarationSymbol: IrSymbol,
cause: Unusable,
forExpression: Boolean
): Appendable {
val functionDeclaration = declarationSymbol.owner as? IrFunction
val functionIsUnusableDueToContainingClass = (functionDeclaration?.parent as? IrClass)?.symbol == cause.symbol
// The user (object) of the unusable classifier. In case the current declaration is a function with its own parent class being
// the dispatch receiver, the class is the "object".
val objectSymbol = if (functionIsUnusableDueToContainingClass) cause.symbol else declarationSymbol
val objectDescription = buildString {
if (functionIsUnusableDueToContainingClass) {
// Callable member is unusable due to unusable dispatch receiver.
val functionIsConstructor = functionDeclaration is IrConstructor
if (forExpression) {
if (functionIsConstructor)
declarationKindName(objectSymbol, capitalized = true) // "Class 'Foo'"
else
append("Dispatch receiver ").declarationKindName(objectSymbol, capitalized = false) // "Dispatch receiver class 'Foo'"
} else {
declarationKindName(objectSymbol, capitalized = true) // "Class 'Foo'"
.append(if (functionIsConstructor) " created by " else ", the dispatch receiver of ") // ", the dispatch receiver of"
.declarationKindName(declarationSymbol, capitalized = false) // "function 'foo'"
}
} else {
if (forExpression)
declarationKind(objectSymbol, capitalized = true) // "Function"
else
declarationKindName(objectSymbol, capitalized = true) // "Function 'foo'"
}
}
return unusableClassifier(
cause,
CauseRendering.UsedFromDeclaration(objectDescription, objectSymbol),
printIntermediateCause = !functionIsUnusableDueToContainingClass
)
}
private fun StringBuilder.expressionWithUnusableClassifier(
expression: IrExpression,
cause: Unusable
): Appendable = expression(expression) { expressionKind ->
// Printing the intermediate cause may pollute certain types of error messages. Need to avoid it when possible.
val printIntermediateCause = when {
expression is IrGetSingletonValue -> when (val expressionSymbol = expression.symbol) {
is IrEnumEntrySymbol -> (expressionSymbol.owner.parent as? IrClass)?.symbol != cause.symbol
else -> expressionSymbol != cause.symbol
}
expressionKind == ANONYMOUS_OBJECT_LITERAL -> cause.symbol.declarationKind != ANONYMOUS_OBJECT
else -> true
}
unusableClassifier(cause, CauseRendering.UsedFromExpression, printIntermediateCause)
}
private fun StringBuilder.expression(expression: IrExpression, continuation: (ExpressionKind) -> Appendable): Appendable {
val (expressionKind, referencedDeclarationKind) = expression.expression
// Prefix may be null. But when it's not null, it is always capitalized.
val hasPrefix = expressionKind.prefix != null
if (hasPrefix) append(expressionKind.prefix)
if (referencedDeclarationKind != null) {
if (hasPrefix) append(" ")
when (expression) {
is IrGetSingletonValue -> appendCapitalized("singleton", capitalized = !hasPrefix)
.append(" ").declarationName(expression.symbol)
is IrDeclarationReference -> declarationKindName(expression.symbol, capitalized = !hasPrefix)
is IrInstanceInitializerCall -> declarationKindName(expression.classSymbol, capitalized = !hasPrefix)
else -> appendCapitalized(referencedDeclarationKind.displayName, capitalized = !hasPrefix)
}
}
expressionKind.postfix?.let { postfix -> append(" ").append(postfix) }
append(": ")
return continuation(expressionKind)
}
private fun Appendable.wrongTypeOfDeclaration(actualDeclarationSymbol: IrSymbol, expectedDeclarationDescription: String): Appendable =
declarationNameIsKind(actualDeclarationSymbol).append(" while ").append(expectedDeclarationDescription).append(" is expected")
private fun Appendable.memberAccessExpressionArgumentsMismatch(
functionSymbol: IrFunctionSymbol,
expressionHasDispatchReceiver: Boolean,
functionHasDispatchReceiver: Boolean,
expressionValueArgumentCount: Int,
functionValueParameterCount: Int
): Appendable = when {
expressionHasDispatchReceiver && !functionHasDispatchReceiver ->
append("The call site provides excessive dispatch receiver parameter 'this' that is not needed for the ")
.declarationKind(functionSymbol, capitalized = false)
!expressionHasDispatchReceiver && functionHasDispatchReceiver ->
append("The call site does not provide a dispatch receiver parameter 'this' that the ")
.declarationKind(functionSymbol, capitalized = false).append(" requires")
else ->
append("The call site provides ").append(if (expressionValueArgumentCount > functionValueParameterCount) "more" else "less")
.append(" value arguments (").append(expressionValueArgumentCount.toString()).append(") than the ")
.declarationKind(functionSymbol, capitalized = false).append(" requires (")
.append(functionValueParameterCount.toString()).append(")")
}
private fun Appendable.suspendableCallWithoutCoroutine(): Appendable =
append("Suspend function can be called only from a coroutine or another suspend function")
private fun Appendable.illegalNonLocalReturn(expression: IrReturn, validReturnTargets: Set<IrReturnTargetSymbol>): Appendable {
append("Illegal non-local return: The return target is ").declarationKindName(expression.returnTargetSymbol, capitalized = false)
append(" while only the following return targets are allowed: ")
validReturnTargets.forEachIndexed { index, returnTarget ->
if (index > 0) append(", ")
declarationKindName(returnTarget, capitalized = false)
}
return this
}
private fun Appendable.inaccessibleDeclaration(
referencedDeclarationSymbol: IrSymbol,
declaringModule: PLModule,
useSiteModule: PLModule
): Appendable = append("Private ").declarationKind(referencedDeclarationSymbol, capitalized = false)
.append(" declared in ").module(declaringModule).append(" can not be accessed in ").module(useSiteModule)
private fun Appendable.invalidConstructorDelegation(
constructorSymbol: IrConstructorSymbol,
superClassSymbol: IrClassSymbol,
unexpectedSuperClassConstructorSymbol: IrConstructorSymbol
): Appendable = declarationKind(constructorSymbol.owner.parentAsClass.symbol, capitalized = true).append(" initialization error: ")
.declarationKindName(constructorSymbol, capitalized = true)
.append(" should call a constructor of direct super class ")
.declarationName(superClassSymbol).append(" but calls ")
.declarationName(unexpectedSuperClassConstructorSymbol).append(" instead")
private fun Appendable.cantInstantiateAbstractClass(classSymbol: IrClassSymbol): Appendable =
append("Can not instantiate ").append(classSymbol.owner.modality.name.lowercase()).append(" ")
.declarationKindName(classSymbol, capitalized = false)
private fun Appendable.unimplementedAbstractCallable(callable: IrOverridableDeclaration<*>): Appendable =
append("Abstract ").declarationKindName(callable.symbol, capitalized = false)
.append(" is not implemented in non-abstract ").declarationKindName(callable.parentAsClass.symbol, capitalized = false)
private fun Appendable.appendCapitalized(text: String, capitalized: Boolean): Appendable {
if (capitalized && text.isNotEmpty()) {
val firstChar = text[0]
if (firstChar.isLowerCase())
return append(firstChar.uppercaseChar()).append(text.substring(1))
}
return append(text)
}
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.linkage.partial
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.SymbolTable
interface PartialLinkageSupportForLinker {
val isEnabled: Boolean
/**
* For general use in IR linker.
*
* Note: Those classifiers that were detected as partially linked are excluded from the fake overrides generation
* to avoid failing with `Symbol for <signature> is unbound` error or generating fake overrides with incorrect signatures.
*/
fun exploreClassifiers(fakeOverrideBuilder: FakeOverrideBuilder)
/**
* For local use only in inline lazy-IR functions.
*
* Such functions are fully deserialized when e.g. a cache is generated for a Kotlin/Native library given that the function itself
* is from another library. The rest of IR from another library remains lazy meantime.
*/
fun exploreClassifiersInInlineLazyIrFunction(function: IrFunction)
/**
* Generate stubs for the remaining unbound symbols. Traverse the IR tree and patch every usage of any unbound symbol
* to throw an appropriate IrLinkageError on access.
*/
fun generateStubsAndPatchUsages(symbolTable: SymbolTable, roots: () -> Sequence<IrModuleFragment>)
fun generateStubsAndPatchUsages(symbolTable: SymbolTable, root: IrDeclaration)
companion object {
val DISABLED = object : PartialLinkageSupportForLinker {
override val isEnabled get() = false
override fun exploreClassifiers(fakeOverrideBuilder: FakeOverrideBuilder) = Unit
override fun exploreClassifiersInInlineLazyIrFunction(function: IrFunction) = Unit
override fun generateStubsAndPatchUsages(symbolTable: SymbolTable, roots: () -> Sequence<IrModuleFragment>) = Unit
override fun generateStubsAndPatchUsages(symbolTable: SymbolTable, root: IrDeclaration) = Unit
}
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.linkage.partial
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.allUnbound
fun createPartialLinkageSupportForLinker(isEnabled: Boolean, builtIns: IrBuiltIns, messageLogger: IrMessageLogger) =
if (isEnabled) PartialLinkageSupportForLinkerImpl(builtIns, messageLogger) else PartialLinkageSupportForLinker.DISABLED
internal class PartialLinkageSupportForLinkerImpl(builtIns: IrBuiltIns, messageLogger: IrMessageLogger) : PartialLinkageSupportForLinker {
private val stubGenerator = MissingDeclarationStubGenerator(builtIns)
private val classifierExplorer = ClassifierExplorer(builtIns, stubGenerator)
private val patcher = PartiallyLinkedIrTreePatcher(builtIns, classifierExplorer, stubGenerator, messageLogger)
override val isEnabled get() = true
override fun exploreClassifiers(fakeOverrideBuilder: FakeOverrideBuilder) {
val entries = fakeOverrideBuilder.fakeOverrideCandidates
if (entries.isEmpty()) return
val toExclude = buildSet {
for (clazz in entries.keys) {
if (classifierExplorer.exploreSymbol(clazz.symbol) != null) {
this += clazz
}
}
}
entries -= toExclude
}
override fun exploreClassifiersInInlineLazyIrFunction(function: IrFunction) {
classifierExplorer.exploreIrElement(function)
}
override fun generateStubsAndPatchUsages(symbolTable: SymbolTable, roots: () -> Sequence<IrModuleFragment>) {
generateStubsAndPatchUsagesInternal(symbolTable) { patcher.patchModuleFragments(roots()) }
}
override fun generateStubsAndPatchUsages(symbolTable: SymbolTable, root: IrDeclaration) {
generateStubsAndPatchUsagesInternal(symbolTable) { patcher.patchDeclarations(listOf(root)) }
}
private fun generateStubsAndPatchUsagesInternal(symbolTable: SymbolTable, patchIrTree: () -> Unit) {
// Generate stubs.
for (symbol in symbolTable.allUnbound) {
stubGenerator.getDeclaration(symbol)
}
// Patch the IR tree.
patchIrTree()
// Patch the stubs which were not patched yet.
patcher.patchDeclarations(stubGenerator.grabDeclarationsToPatch())
}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.linkage.partial
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageSupportForLowerings
import org.jetbrains.kotlin.ir.linkage.partial.PartiallyLinkedDeclarationOrigin
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.File as PLFile
fun createPartialLinkageSupportForLowerings(isEnabled: Boolean, builtIns: IrBuiltIns, messageLogger: IrMessageLogger) =
if (isEnabled) PartialLinkageSupportForLoweringsImpl(builtIns, messageLogger) else PartialLinkageSupportForLowerings.DISABLED
internal class PartialLinkageSupportForLoweringsImpl(
private val builtIns: IrBuiltIns,
private val messageLogger: IrMessageLogger
) : PartialLinkageSupportForLowerings {
override val isEnabled get() = true
override fun throwLinkageError(
partialLinkageCase: PartialLinkageCase,
element: IrElement,
file: PLFile,
suppressWarningInCompilerOutput: Boolean
): IrCall {
val errorMessage = if (suppressWarningInCompilerOutput)
partialLinkageCase.renderLinkageError()
else
renderAndLogLinkageError(partialLinkageCase, element, file)
return IrCallImpl(
startOffset = element.startOffset,
endOffset = element.endOffset,
type = builtIns.nothingType,
symbol = builtIns.linkageErrorSymbol,
typeArgumentsCount = 0,
valueArgumentsCount = 1,
origin = IrStatementOrigin.PARTIAL_LINKAGE_RUNTIME_ERROR
).apply {
putValueArgument(0, IrConstImpl.string(startOffset, endOffset, builtIns.stringType, errorMessage))
}
}
fun renderAndLogLinkageError(partialLinkageCase: PartialLinkageCase, element: IrElement, file: PLFile): String {
val errorMessage = partialLinkageCase.renderLinkageError()
val locationInSourceCode = file.computeLocationForOffset(element.startOffsetOfFirstDenotableIrElement())
messageLogger.report(IrMessageLogger.Severity.WARNING, errorMessage, locationInSourceCode) // It's OK. We log it as a warning.
return errorMessage
}
companion object {
private tailrec fun IrElement.startOffsetOfFirstDenotableIrElement(): Int = when (this) {
is IrPackageFragment -> UNDEFINED_OFFSET
!is IrDeclaration -> {
// We don't generate non-denotable IR expressions in the course of partial linkage.
startOffset
}
else -> if (origin is PartiallyLinkedDeclarationOrigin) {
// There is no sense to take coordinates from the declaration that does not exist in the code.
// Let's take the coordinates of the parent.
parent.startOffsetOfFirstDenotableIrElement()
} else {
startOffset
}
}
}
}
@@ -0,0 +1,94 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.linkage.partial
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyDeclarationBase
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.File as PLFile
internal object PartialLinkageUtils {
val UNKNOWN_NAME = Name.identifier("<unknown name>")
fun IdSignature.guessName(nameSegmentsToPickUp: Int): String? = when (this) {
is IdSignature.CommonSignature -> if (nameSegmentsToPickUp == 1)
shortName
else
nameSegments.takeLast(nameSegmentsToPickUp).joinToString(".")
is IdSignature.CompositeSignature -> inner.guessName(nameSegmentsToPickUp)
is IdSignature.AccessorSignature -> accessorSignature.guessName(nameSegmentsToPickUp)
else -> null
}
/** Like [ClassId], but can be used for any declaration. */
data class DeclarationId(val packageFqName: String, val declarationRelativeFqName: String) {
private fun createNested(name: String) =
DeclarationId(packageFqName, if (declarationRelativeFqName.isNotEmpty()) "$declarationRelativeFqName.$name" else name)
override fun toString() = "$packageFqName/$declarationRelativeFqName"
companion object {
val IrDeclarationWithName.declarationId: DeclarationId?
get() {
return when (val parent = parent) {
is IrPackageFragment -> DeclarationId(parent.fqName.asString(), name.asString())
is IrDeclarationWithName -> parent.declarationId?.createNested(name.asString())
else -> null
}
}
}
}
/**
* Check if the outermost lazy IR class containing [this] declaration is private. If it is, which normally should not happen
* because the lazy IR declaration referenced from non-lazy IR is always expected to be exported from its module and thus
* to be non-private, then consider [this] as the effectively missing declaration.
*
* Such behaviour conforms with behavior that can be observed with no lazy IR usage. Consider the case:
* 1. Module A.v1:
* public class A {
* fun foo(): String // ID signature: "/A.foo|123"
* }
* 2. Module A.v2:
* private class A {
* fun foo(): String // private ID signature (as the containing class is private)
* }
* 3. Module B -> A.v1:
* fun bar() {
* val a = A()
* a.foo() // IR call references a function symbol with ID signature: "/A.foo|123"
* }
* 4. Module App -> A.v2, B
* // Invocation of `A.foo()` inside `fun bar()` is replaced by the IR linkage error. That's
* // because no declaration found for symbol "/A.foo|123" during the IR linkage phase.
*/
fun IrLazyDeclarationBase.isEffectivelyMissingLazyIrDeclaration(): Boolean {
val nearestClass = this as? IrClass ?: parentClassOrNull ?: return false
val outermostClass = generateSequence(nearestClass) { it.parentClassOrNull }.last()
return outermostClass.visibility == DescriptorVisibilities.PRIVATE
}
}
/** An optimization to avoid re-computing file for every visited declaration */
internal abstract class FileAwareIrElementTransformerVoid(startingFile: PLFile?) : IrElementTransformerVoid() {
private var _currentFile: PLFile? = startingFile
val currentFile: PLFile get() = _currentFile ?: error("No information about current file")
final override fun visitFile(declaration: IrFile): IrFile {
_currentFile = PLFile.IrBased(declaration)
return try {
super.visitFile(declaration)
} finally {
_currentFile = null
}
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.linkage.partial
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.linkage.partial.ExploredClassifier
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.types.Variance
/**
* Replacement for IR types that reference unusable classifier symbols.
* Behaves like [kotlin.Any]?. Preserves [ExploredClassifier.Unusable].
*/
internal class PartiallyLinkedMarkerType(
builtIns: IrBuiltIns,
val unusableClassifier: ExploredClassifier.Unusable
) : IrSimpleType(null) {
override val annotations get() = emptyList<IrConstructorCall>()
override val classifier = builtIns.anyClass
override val nullability get() = SimpleTypeNullability.MARKED_NULLABLE
override val arguments get() = emptyList<IrTypeArgument>()
override val abbreviation: IrTypeAbbreviation? get() = null
override val variance get() = Variance.INVARIANT
override fun equals(other: Any?) = (other as? PartiallyLinkedMarkerType)?.unusableClassifier == unusableClassifier
override fun hashCode() = unusableClassifier.hashCode()
}
@@ -16,25 +16,26 @@
package org.jetbrains.kotlin.backend.common.overrides
import org.jetbrains.kotlin.backend.common.linkage.partial.ImplementAsErrorThrowingStubs
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable
import org.jetbrains.kotlin.backend.common.serialization.GlobalDeclarationTable
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
import org.jetbrains.kotlin.backend.common.serialization.signature.PublicIdSignatureComputer
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedDeclarationsProcessor.Companion.MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
import org.jetbrains.kotlin.ir.builders.declarations.buildTypeParameter
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.linkage.partial.IrUnimplementedOverridesStrategy.ProcessAsFakeOverrides
import org.jetbrains.kotlin.ir.overrides.FakeOverrideBuilderStrategy
import org.jetbrains.kotlin.ir.overrides.IrOverridingUtil
import org.jetbrains.kotlin.ir.overrides.IrUnimplementedOverridesStrategy
import org.jetbrains.kotlin.ir.overrides.IrUnimplementedOverridesStrategy.Customization
import org.jetbrains.kotlin.ir.overrides.IrUnimplementedOverridesStrategy.ProcessAsFakeOverrides
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrPropertySymbolImpl
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
class FakeOverrideGlobalDeclarationTable(
mangler: KotlinMangler.IrMangler
@@ -49,6 +50,7 @@ open class FakeOverrideDeclarationTable(
) : DeclarationTable(globalTable) {
override val globalDeclarationTable: FakeOverrideGlobalDeclarationTable = globalTable
override val signaturer: IdSignatureSerializer = signatureSerializerFactory(globalTable.publicIdSignatureComputer, this)
fun clear() {
this.table.clear()
globalDeclarationTable.clear()
@@ -68,28 +70,13 @@ object DefaultFakeOverrideClassFilter : FakeOverrideClassFilter {
override fun needToConstructFakeOverrides(clazz: IrClass): Boolean = true
}
private object ImplementAsErrorThrowingStubs : IrUnimplementedOverridesStrategy {
override fun <T : IrOverridableMember> computeCustomization(overridableMember: T, parent: IrClass) =
if (overridableMember.modality == Modality.ABSTRACT
&& parent.modality != Modality.ABSTRACT
&& parent.modality != Modality.SEALED
) {
Customization(
origin = MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION,
modality = parent.modality, // Use modality of class for implemented callable member.
needToCreateBody = true // At least it should have empty body. Later, the body will be patched in UnlinkedDeclarationsProcessor.
)
} else
Customization.NO
}
class FakeOverrideBuilder(
val linker: FileLocalAwareLinker,
val symbolTable: SymbolTable,
mangler: KotlinMangler.IrMangler,
typeSystem: IrTypeSystemContext,
friendModules: Map<String, Collection<String>>,
partialLinkageEnabled: Boolean,
private val partialLinkageEnabled: Boolean,
val platformSpecificClassFilter: FakeOverrideClassFilter = DefaultFakeOverrideClassFilter,
private val fakeOverrideDeclarationTable: DeclarationTable = FakeOverrideDeclarationTable(mangler) { builder, table ->
IdSignatureSerializer(builder, table)
@@ -101,6 +88,7 @@ class FakeOverrideBuilder(
private val haveFakeOverrides = mutableSetOf<IrClass>()
private val irOverridingUtil = IrOverridingUtil(typeSystem, this)
private val irBuiltIns = typeSystem.irBuiltIns
// TODO: The declaration table is needed for the signaturer.
// private val fakeOverrideDeclarationTable = FakeOverrideDeclarationTable(mangler, signatureSerializerFactory)
@@ -136,12 +124,16 @@ class FakeOverrideBuilder(
return true
}
override fun linkFunctionFakeOverride(declaration: IrFunctionWithLateBinding, compatibilityMode: Boolean) {
val signature = composeSignature(declaration, compatibilityMode)
declareFunctionFakeOverride(declaration, signature)
override fun linkFunctionFakeOverride(function: IrFunctionWithLateBinding, manglerCompatibleMode: Boolean) {
val (signature, symbol) = computeFunctionFakeOverrideSymbol(function, manglerCompatibleMode)
symbolTable.declareSimpleFunction(signature, { symbol }) {
assert(it === symbol)
function.acquireSymbol(it)
}
}
override fun linkPropertyFakeOverride(declaration: IrPropertyWithLateBinding, compatibilityMode: Boolean) {
override fun linkPropertyFakeOverride(property: IrPropertyWithLateBinding, manglerCompatibleMode: Boolean) {
// To compute a signature for a property with type parameters,
// we must have its accessor's correspondingProperty pointing to the property's symbol.
// See IrMangleComputer.mangleTypeParameterReference() for details.
@@ -149,53 +141,142 @@ class FakeOverrideBuilder(
// To break this loop we use temp symbol in correspondingProperty.
val tempSymbol = IrPropertySymbolImpl().also {
it.bind(declaration as IrProperty)
it.bind(property as IrProperty)
}
declaration.getter?.let {
it.correspondingPropertySymbol = tempSymbol
property.getter?.let { getter ->
getter.correspondingPropertySymbol = tempSymbol
}
declaration.setter?.let {
it.correspondingPropertySymbol = tempSymbol
property.setter?.let { setter ->
setter.correspondingPropertySymbol = tempSymbol
}
val signature = composeSignature(declaration, compatibilityMode)
declarePropertyFakeOverride(declaration, signature)
declaration.getter?.let {
it.correspondingPropertySymbol = declaration.symbol
linkFunctionFakeOverride(it as? IrFunctionWithLateBinding ?: error("Unexpected fake override getter: $it"), compatibilityMode)
}
declaration.setter?.let {
it.correspondingPropertySymbol = declaration.symbol
linkFunctionFakeOverride(it as? IrFunctionWithLateBinding ?: error("Unexpected fake override setter: $it"), compatibilityMode)
}
}
private fun composeSignature(declaration: IrDeclaration, compatibleMode: Boolean) =
fakeOverrideDeclarationTable.signaturer.composeSignatureForDeclaration(declaration, compatibleMode)
private fun declareFunctionFakeOverride(declaration: IrFunctionWithLateBinding, signature: IdSignature) {
val parent = declaration.parentAsClass
val symbol = linker.tryReferencingSimpleFunctionByLocalSignature(parent, signature)
?: symbolTable.referenceSimpleFunction(signature, false)
symbolTable.declareSimpleFunction(signature, { symbol }) {
assert(it === symbol)
declaration.acquireSymbol(it)
}
}
private fun declarePropertyFakeOverride(declaration: IrPropertyWithLateBinding, signature: IdSignature) {
val parent = declaration.parentAsClass
val symbol = linker.tryReferencingPropertyByLocalSignature(parent, signature)
?: symbolTable.referenceProperty(signature, false)
val (signature, symbol) = computePropertyFakeOverrideSymbol(property, manglerCompatibleMode)
symbolTable.declareProperty(signature, { symbol }) {
assert(it === symbol)
declaration.acquireSymbol(it)
property.acquireSymbol(it)
}
property.getter?.let { getter ->
getter.correspondingPropertySymbol = property.symbol
linkFunctionFakeOverride(
getter as? IrFunctionWithLateBinding ?: error("Unexpected fake override getter: $getter"),
manglerCompatibleMode
)
}
property.setter?.let { setter ->
setter.correspondingPropertySymbol = property.symbol
linkFunctionFakeOverride(
setter as? IrFunctionWithLateBinding ?: error("Unexpected fake override setter: $setter"),
manglerCompatibleMode
)
}
}
fun provideFakeOverrides(klass: IrClass, compatibleMode: CompatibilityMode) {
buildFakeOverrideChainsForClass(klass, compatibleMode)
private fun composeSignature(declaration: IrDeclaration, manglerCompatibleMode: Boolean) =
fakeOverrideDeclarationTable.signaturer.composeSignatureForDeclaration(declaration, manglerCompatibleMode)
private fun computeFunctionFakeOverrideSymbol(
function: IrFunctionWithLateBinding,
manglerCompatibleMode: Boolean
): Pair<IdSignature, IrSimpleFunctionSymbol> {
require(function is IrSimpleFunction) { "Unexpected fake override function: $function" }
val parent = function.parentAsClass
val signature = composeSignature(function, manglerCompatibleMode)
val symbol = linker.tryReferencingSimpleFunctionByLocalSignature(parent, signature)
?: symbolTable.referenceSimpleFunction(signature)
if (!partialLinkageEnabled
|| !symbol.isBound
|| symbol.owner.let { boundFunction ->
boundFunction.isSuspend == function.isSuspend && !boundFunction.isInline && !function.isInline
}
) {
return signature to symbol
}
// In old KLIB signatures we don't distinguish between suspend and non-suspend, inline and non-inline functions. So we need to
// manually patch the signature of the fake override to avoid clash with the existing function with the different `isSuspend` flag
// state or the existing function with `isInline=true`.
// This signature is not supposed to be ever serialized (as fake overrides are not serialized in KLIBs).
// In new KLIB signatures `isSuspend` and `isInline` flags will be taken into account as a part of signature.
val functionWithDisambiguatedSignature = buildFunctionWithDisambiguatedSignature(function)
val disambiguatedSignature = composeSignature(functionWithDisambiguatedSignature, manglerCompatibleMode)
assert(disambiguatedSignature != signature) { "Failed to compute disambiguated signature for fake override $function" }
val symbolWithDisambiguatedSignature = linker.tryReferencingSimpleFunctionByLocalSignature(parent, disambiguatedSignature)
?: symbolTable.referenceSimpleFunction(disambiguatedSignature)
return disambiguatedSignature to symbolWithDisambiguatedSignature
}
private fun computePropertyFakeOverrideSymbol(
property: IrPropertyWithLateBinding,
manglerCompatibleMode: Boolean
): Pair<IdSignature, IrPropertySymbol> {
require(property is IrProperty) { "Unexpected fake override property: $property" }
val parent = property.parentAsClass
val signature = composeSignature(property, manglerCompatibleMode)
val symbol = linker.tryReferencingPropertyByLocalSignature(parent, signature)
?: symbolTable.referenceProperty(signature)
if (!partialLinkageEnabled
|| !symbol.isBound
|| symbol.owner.let { boundProperty ->
boundProperty.getter?.isInline != true && boundProperty.setter?.isInline != true
&& property.getter?.isInline != true && property.setter?.isInline != true
}
) {
return signature to symbol
}
// In old KLIB signatures we don't distinguish between inline and non-inline property accessors. So we need to
// manually patch the signature of the fake override to avoid clash with the existing property with `inline` accessors.
// This signature is not supposed to be ever serialized (as fake overrides are not serialized in KLIBs).
// In new KLIB signatures `isInline` flag will be taken into account as a part of signature.
val propertyWithDisambiguatedSignature = buildPropertyWithDisambiguatedSignature(property)
val disambiguatedSignature = composeSignature(propertyWithDisambiguatedSignature, manglerCompatibleMode)
assert(disambiguatedSignature != signature) { "Failed to compute disambiguated signature for fake override $property" }
val symbolWithDisambiguatedSignature = linker.tryReferencingPropertyByLocalSignature(parent, disambiguatedSignature)
?: symbolTable.referenceProperty(disambiguatedSignature)
return disambiguatedSignature to symbolWithDisambiguatedSignature
}
private fun buildFunctionWithDisambiguatedSignature(function: IrSimpleFunction): IrSimpleFunction =
function.factory.buildFun {
updateFrom(function)
name = function.name
returnType = irBuiltIns.unitType // Does not matter.
}.apply {
parent = function.parent
copyAnnotationsFrom(function)
copyParameterDeclarationsFrom(function)
typeParameters = typeParameters + buildTypeParameter(this) {
name = Name.identifier("disambiguation type parameter")
index = typeParameters.size
superTypes += irBuiltIns.nothingType // This is something that can't be expressed in the source code.
}
}
private fun buildPropertyWithDisambiguatedSignature(property: IrProperty): IrProperty =
property.factory.buildProperty {
updateFrom(property)
name = property.name
}.apply {
parent = property.parent
copyAnnotationsFrom(property)
getter = property.getter?.let { buildFunctionWithDisambiguatedSignature(it) }
setter = property.setter?.let { buildFunctionWithDisambiguatedSignature(it) }
}
fun provideFakeOverrides(klass: IrClass, compatibilityMode: CompatibilityMode) {
buildFakeOverrideChainsForClass(klass, compatibilityMode)
irOverridingUtil.clear()
haveFakeOverrides.add(klass)
}
@@ -77,12 +77,12 @@ abstract class BasicIrModuleDeserializer(
val expect = deserializeIdSignature(expectSymbol.signatureId)
val actual = deserializeIdSignature(actualSymbol.signatureId)
assert(linker.expectUniqIdToActualUniqId[expect] == null) {
"Expect signature $expect is already actualized by ${linker.expectUniqIdToActualUniqId[expect]}, while we try to record $actual"
assert(linker.expectIdSignatureToActualIdSignature[expect] == null) {
"Expect signature $expect is already actualized by ${linker.expectIdSignatureToActualIdSignature[expect]}, while we try to record $actual"
}
linker.expectUniqIdToActualUniqId[expect] = actual
linker.expectIdSignatureToActualIdSignature[expect] = actual
// Non-null only for topLevel declarations.
findModuleDeserializerForTopLevelId(actual)?.let { md -> linker.topLevelActualUniqItToDeserializer[actual] = md }
findModuleDeserializerForTopLevelId(actual)?.let { md -> linker.topLevelActualIdSignatureToModuleDeserializer[actual] = md }
}
}
@@ -5,7 +5,10 @@
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.backend.common.linkage.issues.IrSymbolTypeMismatchException
import org.jetbrains.kotlin.backend.common.serialization.encodings.*
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData.SymbolKind
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData.SymbolKind.*
import org.jetbrains.kotlin.backend.common.serialization.proto.IrConst.ValueCase.*
import org.jetbrains.kotlin.backend.common.serialization.proto.IrOperation.OperationCase.*
import org.jetbrains.kotlin.backend.common.serialization.proto.IrStatement.StatementCase
@@ -75,7 +78,7 @@ class IrBodyDeserializer(
private val allowErrorNodes: Boolean,
private val irFactory: IrFactory,
private val libraryFile: IrLibraryFile,
private val declarationDeserializer: IrDeclarationDeserializer,
private val declarationDeserializer: IrDeclarationDeserializer
) {
private val fileLoops = mutableMapOf<Int, IrLoop>()
@@ -149,7 +152,7 @@ class IrBodyDeserializer(
private fun deserializeBlock(proto: ProtoBlock, start: Int, end: Int, type: IrType): IrBlock {
val statements = mutableListOf<IrStatement>()
val statementProtos = proto.statementList
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
statementProtos.forEach {
statements.add(deserializeStatement(it) as IrStatement)
@@ -185,7 +188,10 @@ class IrBodyDeserializer(
end: Int,
type: IrType
): IrClassReference {
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.classSymbol) as IrClassifierSymbol
val symbol = deserializeTypedSymbol<IrClassifierSymbol>(
proto.classSymbol,
fallbackSymbolKind = /* just the first possible option */ CLASS_SYMBOL
)
val classType = declarationDeserializer.deserializeIrType(proto.classType)
/** TODO: [createClassifierSymbolForClassReference] is internal function */
return IrClassReferenceImpl(start, end, type, symbol, classType)
@@ -240,26 +246,22 @@ class IrBodyDeserializer(
}
private fun deserializeConstructorCall(proto: ProtoConstructorCall, start: Int, end: Int, type: IrType): IrConstructorCall {
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.symbol) as IrConstructorSymbol
val symbol = deserializeTypedSymbol<IrConstructorSymbol>(proto.symbol, CONSTRUCTOR_SYMBOL)
return IrConstructorCallImpl(
start, end, type,
symbol, typeArgumentsCount = proto.memberAccess.typeArgumentCount,
constructorTypeArgumentsCount = proto.constructorTypeArgumentsCount,
valueArgumentsCount = proto.memberAccess.valueArgumentCount,
origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
).also {
deserializeMemberAccessCommon(it, proto.memberAccess)
}
}
private fun deserializeCall(proto: ProtoCall, start: Int, end: Int, type: IrType): IrCall {
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.symbol) as IrSimpleFunctionSymbol
val superSymbol = if (proto.hasSuper()) {
declarationDeserializer.deserializeIrSymbolAndRemap(proto.`super`) as IrClassSymbol
} else null
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
val symbol = deserializeTypedSymbol<IrSimpleFunctionSymbol>(proto.symbol, FUNCTION_SYMBOL)
val superSymbol = deserializeTypedSymbolWhen<IrClassSymbol>(proto.hasSuper(), CLASS_SYMBOL) { proto.`super` }
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
val call: IrCall =
// TODO: implement the last three args here.
@@ -277,7 +279,7 @@ class IrBodyDeserializer(
private fun deserializeComposite(proto: ProtoComposite, start: Int, end: Int, type: IrType): IrComposite {
val statements = mutableListOf<IrStatement>()
val statementProtos = proto.statementList
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
statementProtos.forEach {
statements.add(deserializeStatement(it) as IrStatement)
@@ -290,7 +292,7 @@ class IrBodyDeserializer(
start: Int,
end: Int
): IrDelegatingConstructorCall {
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.symbol) as IrConstructorSymbol
val symbol = deserializeTypedSymbol<IrConstructorSymbol>(proto.symbol, CONSTRUCTOR_SYMBOL)
val call = IrDelegatingConstructorCallImpl(
start,
end,
@@ -310,7 +312,7 @@ class IrBodyDeserializer(
start: Int,
end: Int,
): IrEnumConstructorCall {
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.symbol) as IrConstructorSymbol
val symbol = deserializeTypedSymbol<IrConstructorSymbol>(proto.symbol, CONSTRUCTOR_SYMBOL)
val call = IrEnumConstructorCallImpl(
start,
end,
@@ -368,10 +370,15 @@ class IrBodyDeserializer(
start: Int, end: Int, type: IrType
): IrFunctionReference {
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.symbol) as IrFunctionSymbol
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
val reflectionTarget =
if (proto.hasReflectionTargetSymbol()) declarationDeserializer.deserializeIrSymbolAndRemap(proto.reflectionTargetSymbol) as IrFunctionSymbol else null
val symbol = deserializeTypedSymbol<IrFunctionSymbol>(
proto.symbol,
fallbackSymbolKind = /* just the first possible option */ FUNCTION_SYMBOL
)
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
val reflectionTarget = deserializeTypedSymbolWhen<IrFunctionSymbol>(
proto.hasReflectionTargetSymbol(),
fallbackSymbolKind = /* just the first possible option */ FUNCTION_SYMBOL
) { proto.reflectionTargetSymbol }
val callable = IrFunctionReferenceImpl(
start,
end,
@@ -394,12 +401,9 @@ class IrBodyDeserializer(
private fun deserializeGetField(proto: ProtoGetField, start: Int, end: Int, type: IrType): IrGetField {
val access = proto.fieldAccess
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(access.symbol) as IrFieldSymbol
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
val superQualifier = if (access.hasSuper()) {
declarationDeserializer.deserializeIrSymbolAndRemap(access.`super`) as IrClassSymbol
} else null
val symbol = deserializeTypedSymbol<IrFieldSymbol>(access.symbol, FIELD_SYMBOL)
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
val superQualifier = deserializeTypedSymbolWhen<IrClassSymbol>(access.hasSuper(), CLASS_SYMBOL) { access.`super` }
val receiver = if (access.hasReceiver()) {
deserializeExpression(access.receiver)
} else null
@@ -408,8 +412,8 @@ class IrBodyDeserializer(
}
private fun deserializeGetValue(proto: ProtoGetValue, start: Int, end: Int, type: IrType): IrGetValue {
val symbol = declarationDeserializer.deserializeIrSymbol(proto.symbol) as IrValueSymbol
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
val symbol = deserializeTypedSymbol<IrValueSymbol>(proto.symbol, fallbackSymbolKind = null, remap = false)
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
// TODO: origin!
return IrGetValueImpl(start, end, type, symbol, origin)
}
@@ -420,7 +424,7 @@ class IrBodyDeserializer(
end: Int,
type: IrType
): IrGetEnumValue {
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.symbol) as IrEnumEntrySymbol
val symbol = deserializeTypedSymbol<IrEnumEntrySymbol>(proto.symbol, ENUM_ENTRY_SYMBOL)
return IrGetEnumValueImpl(start, end, type, symbol)
}
@@ -430,7 +434,7 @@ class IrBodyDeserializer(
end: Int,
type: IrType
): IrGetObjectValue {
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.symbol) as IrClassSymbol
val symbol = deserializeTypedSymbol<IrClassSymbol>(proto.symbol, CLASS_SYMBOL)
return IrGetObjectValueImpl(start, end, type, symbol)
}
@@ -439,7 +443,7 @@ class IrBodyDeserializer(
start: Int,
end: Int
): IrInstanceInitializerCall {
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.symbol) as IrClassSymbol
val symbol = deserializeTypedSymbol<IrClassSymbol>(proto.symbol, CLASS_SYMBOL)
return IrInstanceInitializerCallImpl(start, end, symbol, builtIns.unitType)
}
@@ -450,12 +454,12 @@ class IrBodyDeserializer(
type: IrType
): IrLocalDelegatedPropertyReference {
val delegate = declarationDeserializer.deserializeIrSymbolAndRemap(proto.delegate) as IrVariableSymbol
val getter = declarationDeserializer.deserializeIrSymbolAndRemap(proto.getter) as IrSimpleFunctionSymbol
val delegate = deserializeTypedSymbol<IrVariableSymbol>(proto.delegate, fallbackSymbolKind = null)
val getter = deserializeTypedSymbol<IrSimpleFunctionSymbol>(proto.getter, FUNCTION_SYMBOL)
val setter =
if (proto.hasSetter()) declarationDeserializer.deserializeIrSymbolAndRemap(proto.setter) as IrSimpleFunctionSymbol else null
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.symbol) as IrLocalDelegatedPropertySymbol
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
deserializeTypedSymbolWhen<IrSimpleFunctionSymbol>(proto.hasSetter(), FUNCTION_SYMBOL) { proto.setter }
val symbol = deserializeTypedSymbol<IrLocalDelegatedPropertySymbol>(proto.symbol, fallbackSymbolKind = null)
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
return IrLocalDelegatedPropertyReferenceImpl(
start, end, type,
@@ -468,15 +472,12 @@ class IrBodyDeserializer(
}
private fun deserializePropertyReference(proto: ProtoPropertyReference, start: Int, end: Int, type: IrType): IrPropertyReference {
val symbol = deserializeTypedSymbol<IrPropertySymbol>(proto.symbol, PROPERTY_SYMBOL)
val field = deserializeTypedSymbolWhen<IrFieldSymbol>(proto.hasField(), FIELD_SYMBOL) { proto.field }
val getter = deserializeTypedSymbolWhen<IrSimpleFunctionSymbol>(proto.hasGetter(), FUNCTION_SYMBOL) { proto.getter }
val setter = deserializeTypedSymbolWhen<IrSimpleFunctionSymbol>(proto.hasSetter(), FUNCTION_SYMBOL) { proto.setter }
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.symbol) as IrPropertySymbol
val field = if (proto.hasField()) declarationDeserializer.deserializeIrSymbolAndRemap(proto.field) as IrFieldSymbol else null
val getter =
if (proto.hasGetter()) declarationDeserializer.deserializeIrSymbolAndRemap(proto.getter) as IrSimpleFunctionSymbol else null
val setter =
if (proto.hasSetter()) declarationDeserializer.deserializeIrSymbolAndRemap(proto.setter) as IrSimpleFunctionSymbol else null
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
val callable = IrPropertyReferenceImpl(
start, end, type,
@@ -492,30 +493,31 @@ class IrBodyDeserializer(
}
private fun deserializeReturn(proto: ProtoReturn, start: Int, end: Int): IrReturn {
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(proto.returnTarget) as IrReturnTargetSymbol
val symbol = deserializeTypedSymbol<IrReturnTargetSymbol>(
proto.returnTarget,
fallbackSymbolKind = /* just the first possible option */ FUNCTION_SYMBOL
)
val value = deserializeExpression(proto.value)
return IrReturnImpl(start, end, builtIns.nothingType, symbol, value)
}
private fun deserializeSetField(proto: ProtoSetField, start: Int, end: Int): IrSetField {
val access = proto.fieldAccess
val symbol = declarationDeserializer.deserializeIrSymbolAndRemap(access.symbol) as IrFieldSymbol
val superQualifier = if (access.hasSuper()) {
declarationDeserializer.deserializeIrSymbolAndRemap(access.`super`) as IrClassSymbol
} else null
val symbol = deserializeTypedSymbol<IrFieldSymbol>(access.symbol, FIELD_SYMBOL)
val superQualifier = deserializeTypedSymbolWhen<IrClassSymbol>(access.hasSuper(), CLASS_SYMBOL) { access.`super` }
val receiver = if (access.hasReceiver()) {
deserializeExpression(access.receiver)
} else null
val value = deserializeExpression(proto.value)
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
return IrSetFieldImpl(start, end, symbol, receiver, value, builtIns.unitType, origin, superQualifier)
}
private fun deserializeSetValue(proto: ProtoSetValue, start: Int, end: Int): IrSetValue {
val symbol = declarationDeserializer.deserializeIrSymbol(proto.symbol) as IrValueSymbol
val symbol = deserializeTypedSymbol<IrValueSymbol>(proto.symbol, fallbackSymbolKind = null, remap = false)
val value = deserializeExpression(proto.value)
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
return IrSetValueImpl(start, end, builtIns.unitType, symbol, value, origin)
}
@@ -545,8 +547,7 @@ class IrBodyDeserializer(
proto.catchList.forEach {
catches.add(deserializeStatement(it) as IrCatch)
}
val finallyExpression =
if (proto.hasFinally()) deserializeExpression(proto.getFinally()) else null
val finallyExpression = if (proto.hasFinally()) deserializeExpression(proto.finally) else null
return IrTryImpl(start, end, type, result, catches, finallyExpression)
}
@@ -605,7 +606,7 @@ class IrBodyDeserializer(
private fun deserializeWhen(proto: ProtoWhen, start: Int, end: Int, type: IrType): IrWhen {
val branches = mutableListOf<IrBranch>()
val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
proto.branchList.forEach {
branches.add(deserializeStatement(it) as IrBranch)
@@ -633,7 +634,7 @@ class IrBodyDeserializer(
deserializeLoop(
proto.loop,
deserializeLoopHeader(proto.loop.loopId) {
val origin = if (proto.loop.hasOriginName()) deserializeIrStatementOrigin(proto.loop.originName) else null
val origin = deserializeIrStatementOrigin(proto.loop.hasOriginName()) { proto.loop.originName }
IrDoWhileLoopImpl(start, end, type, origin)
}
)
@@ -642,7 +643,7 @@ class IrBodyDeserializer(
deserializeLoop(
proto.loop,
deserializeLoopHeader(proto.loop.loopId) {
val origin = if (proto.loop.hasOriginName()) deserializeIrStatementOrigin(proto.loop.originName) else null
val origin = deserializeIrStatementOrigin(proto.loop.hasOriginName()) { proto.loop.originName }
IrWhileLoopImpl(start, end, type, origin)
}
)
@@ -820,17 +821,51 @@ class IrBodyDeserializer(
}
private fun deserializeIrStatementOrigin(protoName: Int): IrStatementOrigin {
return libraryFile.string(protoName).let {
val componentPrefix = "COMPONENT_"
when {
it.startsWith(componentPrefix) -> {
IrStatementOrigin.COMPONENT_N.withIndex(it.removePrefix(componentPrefix).toInt())
}
else -> statementOriginIndex[it] ?: error("Unexpected statement origin: $it")
}
}
val originName = libraryFile.string(protoName)
val componentPrefix = "COMPONENT_"
return if (originName.startsWith(componentPrefix))
IrStatementOrigin.COMPONENT_N.withIndex(originName.removePrefix(componentPrefix).toInt())
else
statementOriginIndex[originName] ?: error("Unexpected statement origin: $originName")
}
/**
* This is more compact form of deserializeIrStatementOrigin() that allows writing
* val origin = deserializeIrStatementOrigin(proto.hasOriginName()) { proto.originName }
* instead of (as it was before)
* val origin = if (proto.hasOriginName()) deserializeIrStatementOrigin(proto.originName) else null
*/
private inline fun deserializeIrStatementOrigin(hasOriginName: Boolean, protoName: () -> Int): IrStatementOrigin? =
if (hasOriginName) deserializeIrStatementOrigin(protoName()) else null
/**
* This function allows to check deserialized symbols. If the deserialized symbol mismatches the symbol kind
* at the call site in the deserializer then generate and reference another symbol with
* the same signature. In case PL is off, just throw [IrSymbolTypeMismatchException].
*
* Note: [fallbackSymbolKind] must not completely match [S], but it should represent a subclass of [S].
*
* Example: [S] is [IrClassifierSymbol] and [fallbackSymbolKind] is [CLASS_SYMBOL],
* which is only one possible option along with [TYPE_PARAMETER_SYMBOL].
*
* Note, that for local IR declarations such as [IrValueDeclaration] [fallbackSymbolKind] can be left null.
*/
private inline fun <reified S : IrSymbol> deserializeTypedSymbol(
code: Long,
fallbackSymbolKind: SymbolKind?,
remap: Boolean = true
): S = with(declarationDeserializer) {
val symbol = if (remap) deserializeIrSymbolAndRemap(code) else deserializeIrSymbol(code)
symbol.checkSymbolType(fallbackSymbolKind)
}
private inline fun <reified S : IrSymbol> deserializeTypedSymbolWhen(
condition: Boolean,
fallbackSymbolKind: SymbolKind?,
code: () -> Long
): S? = if (condition) deserializeTypedSymbol(code(), fallbackSymbolKind, remap = true) else null
companion object {
private val allKnownStatementOrigins = IrStatementOrigin::class.nestedClasses.toList()
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.backend.common.linkage.issues.IrDisallowedErrorNode
import org.jetbrains.kotlin.backend.common.linkage.issues.IrSymbolTypeMismatchException
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideClassFilter
import org.jetbrains.kotlin.backend.common.serialization.encodings.*
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkErrorNodesAllowed
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkSymbolType
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData.SymbolKind
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData.SymbolKind.*
import org.jetbrains.kotlin.backend.common.serialization.proto.IrDeclaration.DeclaratorCase.*
import org.jetbrains.kotlin.backend.common.serialization.proto.IrType.KindCase.*
import org.jetbrains.kotlin.descriptors.ClassKind
@@ -74,6 +76,7 @@ class IrDeclarationDeserializer(
private val platformFakeOverrideClassFilter: FakeOverrideClassFilter,
private val fakeOverrideBuilder: FakeOverrideBuilder,
private val compatibilityMode: CompatibilityMode,
private val partialLinkageEnabled: Boolean
) {
private val bodyDeserializer = IrBodyDeserializer(builtIns, allowErrorNodes, irFactory, libraryFile, this)
@@ -119,7 +122,8 @@ class IrDeclarationDeserializer(
}
private fun deserializeSimpleType(proto: ProtoSimpleType): IrSimpleType {
val symbol = checkSymbolType<IrClassifierSymbol>(deserializeIrSymbolAndRemap(proto.classifier))
val symbol = deserializeIrSymbolAndRemap(proto.classifier)
.checkSymbolType<IrClassifierSymbol>(fallbackSymbolKind = /* just the first possible option */ CLASS_SYMBOL)
val arguments = proto.argumentList.map { deserializeIrTypeArgument(it) }
val annotations = deserializeAnnotations(proto.annotationList)
@@ -135,7 +139,8 @@ class IrDeclarationDeserializer(
}
private fun deserializeLegacySimpleType(proto: ProtoSimpleTypeLegacy): IrSimpleType {
val symbol = checkSymbolType<IrClassifierSymbol>(deserializeIrSymbolAndRemap(proto.classifier))
val symbol = deserializeIrSymbolAndRemap(proto.classifier)
.checkSymbolType<IrClassifierSymbol>(fallbackSymbolKind = /* just the first possible option */ CLASS_SYMBOL)
val arguments = proto.argumentList.map { deserializeIrTypeArgument(it) }
val annotations = deserializeAnnotations(proto.annotationList)
@@ -152,7 +157,7 @@ class IrDeclarationDeserializer(
private fun deserializeTypeAbbreviation(proto: ProtoTypeAbbreviation): IrTypeAbbreviation =
IrTypeAbbreviationImpl(
checkSymbolType(deserializeIrSymbolAndRemap(proto.typeAlias)),
deserializeIrSymbolAndRemap(proto.typeAlias).checkSymbolType(TYPEALIAS_SYMBOL),
proto.hasQuestionMark,
proto.argumentList.map { deserializeIrTypeArgument(it) },
deserializeAnnotations(proto.annotationList)
@@ -164,7 +169,7 @@ class IrDeclarationDeserializer(
}
private fun deserializeErrorType(proto: ProtoErrorType): IrErrorType {
checkErrorNodesAllowed<IrErrorType>(allowErrorNodes)
if (!allowErrorNodes) throw IrDisallowedErrorNode(IrErrorType::class.java)
val annotations = deserializeAnnotations(proto.annotationList)
return IrErrorTypeImpl(null, annotations, Variance.INVARIANT)
}
@@ -286,7 +291,7 @@ class IrDeclarationDeserializer(
val result = symbolTable.run {
if (isGlobal) {
val p = symbolDeserializer.deserializeIrSymbolToDeclare(proto.base.symbol)
val symbol = checkSymbolType<IrTypeParameterSymbol>(p.first)
val symbol: IrTypeParameterSymbol = p.first.checkSymbolType(TYPE_PARAMETER_SYMBOL)
sig = p.second
declareGlobalTypeParameter(sig, { symbol }, factory)
} else {
@@ -296,10 +301,9 @@ class IrDeclarationDeserializer(
sig,
{
if (it.isPubliclyVisible)
symbolDeserializer.deserializeIrSymbol(
sig, BinarySymbolData.SymbolKind.TYPE_PARAMETER_SYMBOL
) as IrTypeParameterSymbol
else IrTypeParameterSymbolImpl()
symbolDeserializer.deserializeIrSymbol(sig, TYPE_PARAMETER_SYMBOL).checkSymbolType(TYPE_PARAMETER_SYMBOL)
else
IrTypeParameterSymbolImpl()
},
factory
)
@@ -319,7 +323,7 @@ class IrDeclarationDeserializer(
val nameAndType = BinaryNameAndType.decode(proto.nameType)
irFactory.createValueParameter(
startOffset, endOffset, origin,
checkSymbolType(symbol),
symbol.checkSymbolType(fallbackSymbolKind = null),
deserializeName(nameAndType.nameIndex),
index,
deserializeIrType(nameAndType.typeIndex),
@@ -337,8 +341,6 @@ class IrDeclarationDeserializer(
private fun deserializeIrClass(proto: ProtoClass, setParent: Boolean = true): IrClass =
withDeserializedIrDeclarationBase(proto.base, setParent) { symbol, signature, startOffset, endOffset, origin, fcode ->
checkSymbolType<IrClassSymbol>(symbol)
val flags = ClassFlags.decode(fcode)
// Similar to 948dc4f3, compatibility hack for libs that were generated before 1.6.20.
val effectiveModality = if (flags.kind == ClassKind.ANNOTATION_CLASS) {
@@ -346,7 +348,7 @@ class IrDeclarationDeserializer(
} else {
flags.modality
}
symbolTable.declareClass(signature, { symbol }) {
symbolTable.declareClass(signature, { symbol.checkSymbolType(CLASS_SYMBOL) }) {
irFactory.createClass(
startOffset, endOffset, origin,
it,
@@ -387,7 +389,7 @@ class IrDeclarationDeserializer(
else -> computeMissingInlineClassRepresentationForCompatibility(this)
}
sealedSubclasses = proto.sealedSubclassList.map { deserializeIrSymbol(it) as IrClassSymbol }
sealedSubclasses = proto.sealedSubclassList.map { deserializeIrSymbol(it).checkSymbolType(CLASS_SYMBOL) }
fakeOverrideBuilder.enqueueClass(this, signature, compatibilityMode)
}
@@ -417,8 +419,7 @@ class IrDeclarationDeserializer(
private fun deserializeIrTypeAlias(proto: ProtoTypeAlias, setParent: Boolean = true): IrTypeAlias =
withDeserializedIrDeclarationBase(proto.base, setParent) { symbol, uniqId, startOffset, endOffset, origin, fcode ->
checkSymbolType<IrTypeAliasSymbol>(symbol)
symbolTable.declareTypeAlias(uniqId, { symbol }) {
symbolTable.declareTypeAlias(uniqId, { symbol.checkSymbolType(TYPEALIAS_SYMBOL) }) {
val flags = TypeAliasFlags.decode(fcode)
val nameType = BinaryNameAndType.decode(proto.nameType)
irFactory.createTypeAlias(
@@ -436,7 +437,7 @@ class IrDeclarationDeserializer(
}
private fun deserializeErrorDeclaration(proto: ProtoErrorDeclaration, setParent: Boolean = true): IrErrorDeclaration {
checkErrorNodesAllowed<IrErrorDeclaration>(allowErrorNodes)
if (!allowErrorNodes) throw IrDisallowedErrorNode(IrErrorDeclaration::class.java)
val coordinates = BinaryCoordinates.decode(proto.coordinates)
return irFactory.createErrorDeclaration(coordinates.startOffset, coordinates.endOffset).also {
if (setParent) it.parent = currentParent
@@ -550,13 +551,15 @@ class IrDeclarationDeserializer(
}
}
private inline fun <T : IrFunction> withDeserializedIrFunctionBase(
private inline fun <reified S : IrFunctionSymbol, T : IrFunction> withDeserializedIrFunctionBase(
proto: ProtoFunctionBase,
setParent: Boolean = true,
block: (IrFunctionSymbol, IdSignature, Int, Int, IrDeclarationOrigin, Long) -> T
fallbackSymbolKind: SymbolKind,
block: (S, IdSignature, Int, Int, IrDeclarationOrigin, Long) -> T
): T = withDeserializedIrDeclarationBase(proto.base, setParent) { symbol, idSig, startOffset, endOffset, origin, fcode ->
symbolTable.withScope(symbol) {
block(checkSymbolType(symbol), idSig, startOffset, endOffset, origin, fcode).usingParent {
val functionSymbol: S = symbol.checkSymbolType(fallbackSymbolKind)
symbolTable.withScope(functionSymbol) {
block(functionSymbol, idSig, startOffset, endOffset, origin, fcode).usingParent {
typeParameters = deserializeTypeParameters(proto.typeParameterList, false)
val nameType = BinaryNameAndType.decode(proto.nameType)
returnType = deserializeIrType(nameType.typeIndex)
@@ -579,10 +582,12 @@ class IrDeclarationDeserializer(
}
}
internal fun deserializeIrFunction(proto: ProtoFunction, setParent: Boolean = true): IrSimpleFunction {
return withDeserializedIrFunctionBase(proto.base, setParent) { symbol, idSig, startOffset, endOffset, origin, fcode ->
checkSymbolType<IrSimpleFunctionSymbol>(symbol)
internal fun deserializeIrFunction(proto: ProtoFunction, setParent: Boolean = true): IrSimpleFunction =
withDeserializedIrFunctionBase<IrSimpleFunctionSymbol, IrSimpleFunction>(
proto.base,
setParent,
FUNCTION_SYMBOL
) { symbol, idSig, startOffset, endOffset, origin, fcode ->
val flags = FunctionFlags.decode(fcode)
symbolTable.declareSimpleFunction(idSig, { symbol }) {
val nameType = BinaryNameAndType.decode(proto.base.nameType)
@@ -603,21 +608,18 @@ class IrDeclarationDeserializer(
flags.isFakeOverride
)
}.apply {
overriddenSymbols = proto.overriddenList.map { checkSymbolType(deserializeIrSymbolAndRemap(it)) }
overriddenSymbols = proto.overriddenList.map { deserializeIrSymbolAndRemap(it).checkSymbolType(FUNCTION_SYMBOL) }
}
}
}
fun deserializeIrVariable(proto: ProtoVariable, setParent: Boolean = true): IrVariable =
withDeserializedIrDeclarationBase(proto.base, setParent) { symbol, _, startOffset, endOffset, origin, fcode ->
checkSymbolType<IrVariableSymbol>(symbol)
val flags = LocalVariableFlags.decode(fcode)
val nameType = BinaryNameAndType.decode(proto.nameType)
IrVariableImpl(
startOffset, endOffset, origin,
symbol,
symbol.checkSymbolType(fallbackSymbolKind = null),
deserializeName(nameType.nameIndex),
deserializeIrType(nameType.typeIndex),
flags.isVar,
@@ -631,7 +633,7 @@ class IrDeclarationDeserializer(
private fun deserializeIrEnumEntry(proto: ProtoEnumEntry, setParent: Boolean = true): IrEnumEntry =
withDeserializedIrDeclarationBase(proto.base, setParent) { symbol, uniqId, startOffset, endOffset, origin, _ ->
symbolTable.declareEnumEntry(uniqId, { checkSymbolType(symbol) }) {
symbolTable.declareEnumEntry(uniqId, { symbol.checkSymbolType(ENUM_ENTRY_SYMBOL) }) {
irFactory.createEnumEntry(startOffset, endOffset, origin, it, deserializeName(proto.name))
}.apply {
if (proto.hasCorrespondingClass())
@@ -642,15 +644,21 @@ class IrDeclarationDeserializer(
}
private fun deserializeIrAnonymousInit(proto: ProtoAnonymousInit, setParent: Boolean = true): IrAnonymousInitializer =
withDeserializedIrDeclarationBase(proto.base, setParent) { symbol, _, startOffset, endOffset, origin, _ ->
irFactory.createAnonymousInitializer(startOffset, endOffset, origin, checkSymbolType(symbol)).apply {
withDeserializedIrDeclarationBase(
proto.base,
setParent
) { symbol, _, startOffset, endOffset, origin, _ ->
irFactory.createAnonymousInitializer(startOffset, endOffset, origin, symbol.checkSymbolType(fallbackSymbolKind = null)).apply {
body = deserializeStatementBody(proto.body) as IrBlockBody? ?: irFactory.createBlockBody(startOffset, endOffset)
}
}
private fun deserializeIrConstructor(proto: ProtoConstructor, setParent: Boolean = true): IrConstructor =
withDeserializedIrFunctionBase(proto.base, setParent) { symbol, idSig, startOffset, endOffset, origin, fcode ->
checkSymbolType<IrConstructorSymbol>(symbol)
withDeserializedIrFunctionBase<IrConstructorSymbol, IrConstructor>(
proto.base,
setParent,
CONSTRUCTOR_SYMBOL
) { symbol, idSig, startOffset, endOffset, origin, fcode ->
val flags = FunctionFlags.decode(fcode)
val nameType = BinaryNameAndType.decode(proto.base.nameType)
symbolTable.declareConstructor(idSig, { symbol }) {
@@ -671,12 +679,11 @@ class IrDeclarationDeserializer(
private fun deserializeIrField(proto: ProtoField, isConst: Boolean, setParent: Boolean = true): IrField =
withDeserializedIrDeclarationBase(proto.base, setParent) { symbol, uniqId, startOffset, endOffset, origin, fcode ->
checkSymbolType<IrFieldSymbol>(symbol)
val nameType = BinaryNameAndType.decode(proto.nameType)
val type = deserializeIrType(nameType.typeIndex)
val flags = FieldFlags.decode(fcode)
val field = symbolTable.declareField(uniqId, { symbol }) {
val field = symbolTable.declareField(uniqId, { symbol.checkSymbolType(FIELD_SYMBOL) }) {
irFactory.createField(
startOffset, endOffset, origin,
it,
@@ -705,14 +712,12 @@ class IrDeclarationDeserializer(
setParent: Boolean = true
): IrLocalDelegatedProperty =
withDeserializedIrDeclarationBase(proto.base, setParent) { symbol, _, startOffset, endOffset, origin, fcode ->
checkSymbolType<IrLocalDelegatedPropertySymbol>(symbol)
val flags = LocalVariableFlags.decode(fcode)
val nameAndType = BinaryNameAndType.decode(proto.nameType)
val prop = irFactory.createLocalDelegatedProperty(
startOffset, endOffset, origin,
symbol,
symbol.checkSymbolType(fallbackSymbolKind = null),
deserializeName(nameAndType.nameIndex),
deserializeIrType(nameAndType.typeIndex),
flags.isVar
@@ -728,9 +733,9 @@ class IrDeclarationDeserializer(
private fun deserializeIrProperty(proto: ProtoProperty, setParent: Boolean = true): IrProperty =
withDeserializedIrDeclarationBase(proto.base, setParent) { symbol, uniqId, startOffset, endOffset, origin, fcode ->
checkSymbolType<IrPropertySymbol>(symbol)
val flags = PropertyFlags.decode(fcode)
val prop = symbolTable.declareProperty(uniqId, { symbol }) {
val propertySymbol: IrPropertySymbol = symbol.checkSymbolType(PROPERTY_SYMBOL)
val prop = symbolTable.declareProperty(uniqId, { propertySymbol }) {
irFactory.createProperty(
startOffset, endOffset, origin,
it,
@@ -751,17 +756,17 @@ class IrDeclarationDeserializer(
withExternalValue(isExternal) {
if (proto.hasGetter()) {
getter = deserializeIrFunction(proto.getter).also {
it.correspondingPropertySymbol = symbol
it.correspondingPropertySymbol = propertySymbol
}
}
if (proto.hasSetter()) {
setter = deserializeIrFunction(proto.setter).also {
it.correspondingPropertySymbol = symbol
it.correspondingPropertySymbol = propertySymbol
}
}
if (proto.hasBackingField()) {
backingField = deserializeIrField(proto.backingField, prop.isConst).also {
it.correspondingPropertySymbol = symbol
it.correspondingPropertySymbol = propertySymbol
}
}
}
@@ -821,4 +826,30 @@ class IrDeclarationDeserializer(
else -> false
}
}
/**
* This function allows to check deserialized symbols. If the deserialized symbol mismatches the symbol kind
* at the call site in the deserializer then generate and reference another symbol with
* the same signature. In case PL is off, just throw [IrSymbolTypeMismatchException].
*
* Note: [fallbackSymbolKind] must not completely match [S], but it should represent a subclass of [S].
*
* Example: [S] is [IrClassifierSymbol] and [fallbackSymbolKind] is [CLASS_SYMBOL],
* which is only one possible option along with [TYPE_PARAMETER_SYMBOL].
*
* Note, that for local IR declarations such as [IrValueDeclaration] [fallbackSymbolKind] can be left null.
*/
internal inline fun <reified S : IrSymbol> IrSymbol.checkSymbolType(fallbackSymbolKind: SymbolKind?): S {
if (this is S) return this // Fast pass.
if (!partialLinkageEnabled)
throw IrSymbolTypeMismatchException(S::class.java, this)
return referenceDeserializedSymbol(
symbolTable = symbolDeserializer.symbolTable,
fileSymbol = null,
symbolKind = fallbackSymbolKind ?: error("No fallback symbol kind specified for symbol $this"),
idSig = signature?.takeIf { it.isPubliclyVisible } ?: error("No public signature for symbol $this")
) as S
}
}
@@ -90,7 +90,8 @@ class FileDeserializationState(
symbolDeserializer,
linker.fakeOverrideBuilder.platformSpecificClassFilter,
linker.fakeOverrideBuilder,
compatibilityMode = moduleDeserializer.compatibilityMode
compatibilityMode = moduleDeserializer.compatibilityMode,
linker.partialLinkageSupport.isEnabled
)
val fileDeserializer = IrFileDeserializer(file, fileReader, fileProto, symbolDeserializer, declarationDeserializer)
@@ -118,10 +118,10 @@ class IrModuleDeserializerWithBuiltIns(
// assert(builtIns.builtIns.builtInsModule === delegate.moduleDescriptor)
}
private val irBuiltInsMap = builtIns.knownBuiltins.map {
private val irBuiltInsMap = builtIns.knownBuiltins.associate {
val symbol = (it as IrSymbolOwner).symbol
symbol.signature to symbol
}.toMap()
}
override operator fun contains(idSig: IdSignature): Boolean {
val topLevel = idSig.topLevelSignature()
@@ -26,10 +26,9 @@ class IrSymbolDeserializer(
val enqueueLocalTopLevelDeclaration: (IdSignature) -> Unit,
val handleExpectActualMapping: (IdSignature, IrSymbol) -> IrSymbol,
val symbolProcessor: IrSymbolDeserializer.(IrSymbol, IdSignature) -> IrSymbol = { s, _ -> s },
private val fileSignature: IdSignature.FileSignature = IdSignature.FileSignature(fileSymbol),
fileSignature: IdSignature.FileSignature = IdSignature.FileSignature(fileSymbol),
val deserializePublicSymbol: (IdSignature, BinarySymbolData.SymbolKind) -> IrSymbol
) {
val deserializedSymbols: MutableMap<IdSignature, IrSymbol> = mutableMapOf()
fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
@@ -112,8 +111,7 @@ internal fun referenceDeserializedSymbol(
BinarySymbolData.SymbolKind.VARIABLE_SYMBOL -> IrVariableSymbolImpl()
BinarySymbolData.SymbolKind.VALUE_PARAMETER_SYMBOL -> IrValueParameterSymbolImpl()
BinarySymbolData.SymbolKind.RECEIVER_PARAMETER_SYMBOL -> IrValueParameterSymbolImpl()
BinarySymbolData.SymbolKind.LOCAL_DELEGATED_PROPERTY_SYMBOL ->
IrLocalDelegatedPropertySymbolImpl()
BinarySymbolData.SymbolKind.LOCAL_DELEGATED_PROPERTY_SYMBOL -> IrLocalDelegatedPropertySymbolImpl()
BinarySymbolData.SymbolKind.FILE_SYMBOL -> fileSymbol ?: error("File symbol is not provided")
else -> error("Unexpected classifier symbol kind: $symbolKind for signature $idSig")
}
@@ -5,12 +5,12 @@
package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.backend.common.linkage.issues.*
import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageSupportForLinker
import org.jetbrains.kotlin.backend.common.linkage.partial.createPartialLinkageSupportForLinker
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.backend.common.overrides.FileLocalAwareLinker
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.*
import org.jetbrains.kotlin.backend.common.serialization.unlinked.PartialLinkageSupport
import org.jetbrains.kotlin.backend.common.serialization.unlinked.PartialLinkageSupportImpl
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -36,8 +36,8 @@ abstract class KotlinIrLinker(
) : IrDeserializer, FileLocalAwareLinker {
// Kotlin-MPP related data. Consider some refactoring
val expectUniqIdToActualUniqId = mutableMapOf<IdSignature, IdSignature>()
val topLevelActualUniqItToDeserializer = mutableMapOf<IdSignature, IrModuleDeserializer>()
val expectIdSignatureToActualIdSignature = mutableMapOf<IdSignature, IdSignature>()
val topLevelActualIdSignatureToModuleDeserializer = mutableMapOf<IdSignature, IrModuleDeserializer>()
internal val expectSymbols = mutableMapOf<IdSignature, IrSymbol>()
internal val actualSymbols = mutableMapOf<IdSignature, IrSymbol>()
@@ -53,10 +53,8 @@ abstract class KotlinIrLinker(
private lateinit var linkerExtensions: Collection<IrDeserializer.IrLinkerExtension>
val partialLinkageSupport: PartialLinkageSupport = if (partialLinkageEnabled)
PartialLinkageSupportImpl(builtIns)
else
PartialLinkageSupport.DISABLED
val partialLinkageSupport: PartialLinkageSupportForLinker =
createPartialLinkageSupportForLinker(partialLinkageEnabled, builtIns, messageLogger)
protected open val userVisibleIrModulesSupport: UserVisibleIrModulesSupport get() = UserVisibleIrModulesSupport.DEFAULT
@@ -74,11 +72,11 @@ abstract class KotlinIrLinker(
// Note: It might happen that the top-level symbol still exists in KLIB, but nested symbol has been removed.
// Then the `actualModuleDeserializer` will be non-null, but `actualModuleDeserializer.tryDeserializeIrSymbol()` call
// will return null.
// might return null (like KonanInteropModuleDeserializer does) or non-null unbound symbol (like JsModuleDeserializer does).
val symbol: IrSymbol? = actualModuleDeserializer?.tryDeserializeIrSymbol(idSignature, symbolKind)
return symbol ?: run {
if (partialLinkageSupport.partialLinkageEnabled)
if (partialLinkageSupport.isEnabled)
referenceDeserializedSymbol(symbolTable, null, symbolKind, idSignature)
else
SignatureIdNotFoundInModuleWithDependencies(
@@ -160,9 +158,7 @@ abstract class KotlinIrLinker(
?: tryResolveCustomDeclaration(symbol)
?: return null
} catch (e: IrSymbolTypeMismatchException) {
if (!partialLinkageSupport.partialLinkageEnabled) {
SymbolTypeMismatch(e, deserializersForModules.values, userVisibleIrModulesSupport).raiseIssue(messageLogger)
}
SymbolTypeMismatch(e, deserializersForModules.values, userVisibleIrModulesSupport).raiseIssue(messageLogger)
}
}
@@ -212,15 +208,20 @@ abstract class KotlinIrLinker(
}
override fun postProcess() {
// TODO: Expect/actual actualization should be fixed to cope with the situation when either expect or actual symbol is unbound.
finalizeExpectActualLinker()
partialLinkageSupport.markUsedClassifiersExcludingUnlinkedFromFakeOverrideBuilding(fakeOverrideBuilder)
// We have to exclude classifiers with unbound symbols in supertypes and in type parameter upper bounds from F.O. generation
// to avoid failing with `Symbol for <signature> is unbound` error or generating fake overrides with incorrect signatures.
partialLinkageSupport.exploreClassifiers(fakeOverrideBuilder)
// Fake override generator creates new IR declarations. This may have effect of binding for certain symbols.
fakeOverrideBuilder.provideFakeOverrides()
triedToDeserializeDeclarationForSymbol.clear()
partialLinkageSupport.processUnlinkedDeclarations(messageLogger) {
deserializersForModules.values.map { it.moduleFragment }
// Finally, generate stubs for the remaining unbound symbols and patch every usage of any unbound symbol inside the IR tree.
partialLinkageSupport.generateStubsAndPatchUsages(symbolTable) {
deserializersForModules.values.asSequence().map { it.moduleFragment }
}
// TODO: fix IrPluginContext to make it not produce additional external reference
@@ -230,12 +231,12 @@ abstract class KotlinIrLinker(
fun handleExpectActualMapping(idSig: IdSignature, rawSymbol: IrSymbol): IrSymbol {
// Actual signature
if (idSig in expectUniqIdToActualUniqId.values) {
if (idSig in expectIdSignatureToActualIdSignature.values) {
actualSymbols[idSig] = rawSymbol
}
// Expect signature
expectUniqIdToActualUniqId[idSig]?.let { actualSig ->
expectIdSignatureToActualIdSignature[idSig]?.let { actualSig ->
assert(idSig.run { IdSignature.Flags.IS_EXPECT.test() })
val referencingSymbol = wrapInDelegatedSymbol(rawSymbol)
@@ -243,7 +244,7 @@ abstract class KotlinIrLinker(
expectSymbols[idSig] = referencingSymbol
// Trigger actual symbol deserialization
topLevelActualUniqItToDeserializer[actualSig]?.let { moduleDeserializer -> // Not null if top-level
topLevelActualIdSignatureToModuleDeserializer[actualSig]?.let { moduleDeserializer -> // Not null if top-level
val actualSymbol = actualSymbols[actualSig]
// Check if
if (actualSymbol == null || !actualSymbol.isBound) {
@@ -306,7 +307,7 @@ abstract class KotlinIrLinker(
// So we force deserialization of actuals for all deserialized expect symbols here.
private fun finalizeExpectActualLinker() {
// All actuals have been deserialized, retarget delegating symbols from expects to actuals.
expectUniqIdToActualUniqId.forEach {
expectIdSignatureToActualIdSignature.forEach {
val expectSymbol = expectSymbols[it.key]
val actualSymbol = actualSymbols[it.value]
if (expectSymbol != null && actualSymbol != null) {
@@ -1,38 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization.unlinked
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.IrMessageLogger
interface PartialLinkageSupport {
val partialLinkageEnabled: Boolean
/** For general use in IR linker. */
fun markUsedClassifiersExcludingUnlinkedFromFakeOverrideBuilding(fakeOverrideBuilder: FakeOverrideBuilder)
/** For local use only in inline lazy-IR functions. */
fun markUsedClassifiersInInlineLazyIrFunction(function: IrFunction)
fun processUnlinkedDeclarations(messageLogger: IrMessageLogger, lazyRoots: () -> List<IrElement>)
interface UnlinkedMarkerTypeHandler {
val unlinkedMarkerType: IrType
fun IrType.isUnlinkedMarkerType(): Boolean
}
companion object {
val DISABLED = object : PartialLinkageSupport {
override val partialLinkageEnabled get() = false
override fun markUsedClassifiersExcludingUnlinkedFromFakeOverrideBuilding(fakeOverrideBuilder: FakeOverrideBuilder) = Unit
override fun markUsedClassifiersInInlineLazyIrFunction(function: IrFunction) = Unit
override fun processUnlinkedDeclarations(messageLogger: IrMessageLogger, lazyRoots: () -> List<IrElement>) = Unit
}
}
}
@@ -1,182 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization.unlinked
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.backend.common.serialization.unlinked.PartialLinkageSupport.UnlinkedMarkerTypeHandler
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UsedClassifierSymbolStatus.*
import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrConstantObject
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.types.IrErrorType
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.util.parentClassOrNull
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class PartialLinkageSupportImpl(private val builtIns: IrBuiltIns) : PartialLinkageSupport {
private val handler = object : UnlinkedMarkerTypeHandler {
override val unlinkedMarkerType = IrSimpleTypeImpl(
classifier = builtIns.anyClass,
hasQuestionMark = true,
arguments = emptyList(),
annotations = emptyList()
)
override fun IrType.isUnlinkedMarkerType(): Boolean = this === unlinkedMarkerType
}
private val usedClassifierSymbols = UsedClassifierSymbols()
override val partialLinkageEnabled get() = true
override fun markUsedClassifiersExcludingUnlinkedFromFakeOverrideBuilding(fakeOverrideBuilder: FakeOverrideBuilder) {
val entries = fakeOverrideBuilder.fakeOverrideCandidates
if (entries.isEmpty()) return
val toRemove = hashSetOf<IrClass>()
for (clazz in entries.keys) {
if (clazz.symbol.isUnlinkedClassifier(visited = hashSetOf())) {
toRemove += clazz
}
}
entries -= toRemove
}
private fun IrType.isUnlinkedType(visited: MutableSet<IrClassifierSymbol>): Boolean {
val simpleType = this as? IrSimpleType ?: return this !is IrErrorType
if (simpleType.classifier.isUnlinkedClassifier(visited))
return true
for (argument in simpleType.arguments) {
if (argument is IrTypeProjection) {
if (argument.type.isUnlinkedType(visited))
return true
}
}
return false
}
private fun IrClassifierSymbol.isUnlinkedClassifier(visited: MutableSet<IrClassifierSymbol>): Boolean {
when (val status = usedClassifierSymbols[this]) {
UNLINKED, LINKED -> return status.isUnlinked
null -> {
// Unknown classifier. Continue.
}
}
if (!isBound || (hasDescriptor && descriptor is NotFoundClasses.MockClassDescriptor))
return usedClassifierSymbols.register(this, UNLINKED)
if (!visited.add(this))
return false // Recursion avoidance.
when (val classifier = owner) {
is IrClass -> {
if (classifier.parentClassOrNull?.symbol?.isUnlinkedClassifier(visited) == true)
return usedClassifierSymbols.register(this, UNLINKED)
for (typeParameter in classifier.typeParameters) {
if (typeParameter.superTypes.any { it.isUnlinkedType(visited) })
return usedClassifierSymbols.register(this, UNLINKED)
}
if (classifier.superTypes.any { it.isUnlinkedType(visited) })
return usedClassifierSymbols.register(this, UNLINKED)
}
is IrTypeParameter -> {
if (classifier.superTypes.any { it.isUnlinkedType(visited) })
return usedClassifierSymbols.register(this, UNLINKED)
}
}
return usedClassifierSymbols.register(this, LINKED)
}
override fun markUsedClassifiersInInlineLazyIrFunction(function: IrFunction) {
function.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
fun visitType(type: IrType?) {
type?.isUnlinkedType(visited = hashSetOf())
}
override fun visitValueParameter(declaration: IrValueParameter) {
visitType(declaration.type)
super.visitValueParameter(declaration)
}
override fun visitTypeParameter(declaration: IrTypeParameter) {
declaration.superTypes.forEach(::visitType)
super.visitTypeParameter(declaration)
}
override fun visitFunction(declaration: IrFunction) {
visitType(declaration.returnType)
super.visitFunction(declaration)
}
override fun visitField(declaration: IrField) {
visitType(declaration.type)
super.visitField(declaration)
}
override fun visitVariable(declaration: IrVariable) {
visitType(declaration.type)
super.visitVariable(declaration)
}
override fun visitExpression(expression: IrExpression) {
visitType(expression.type)
super.visitExpression(expression)
}
override fun visitClassReference(expression: IrClassReference) {
visitType(expression.classType)
super.visitClassReference(expression)
}
override fun visitConstantObject(expression: IrConstantObject) {
expression.typeArguments.forEach(::visitType)
super.visitConstantObject(expression)
}
override fun visitTypeOperator(expression: IrTypeOperatorCall) {
visitType(expression.typeOperand)
super.visitTypeOperator(expression)
}
})
}
override fun processUnlinkedDeclarations(messageLogger: IrMessageLogger, lazyRoots: () -> List<IrElement>) {
val processor = UnlinkedDeclarationsProcessor(builtIns, usedClassifierSymbols, handler, messageLogger)
processor.addLinkageErrorIntoUnlinkedClasses()
val roots = lazyRoots()
val signatureTransformer = processor.signatureTransformer()
roots.forEach { it.transformChildrenVoid(signatureTransformer) }
val usageTransformer = processor.usageTransformer()
roots.forEach { it.transformChildrenVoid(usageTransformer) }
}
}
@@ -1,385 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization.unlinked
import org.jetbrains.kotlin.backend.common.serialization.unlinked.PartialLinkageSupport.UnlinkedMarkerTypeHandler
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedDeclarationsProcessor.Companion.MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedIrElementRenderer.appendDeclaration
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UnlinkedIrElementRenderer.renderError
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UsedClassifierSymbolStatus.Companion.isUnlinked
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.util.IrMessageLogger.Location
import org.jetbrains.kotlin.ir.util.IrMessageLogger.Severity
import org.jetbrains.kotlin.ir.util.fileOrNull
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.utils.addIfNotNull
internal class UnlinkedDeclarationsProcessor(
private val builtIns: IrBuiltIns,
private val usedClassifierSymbols: UsedClassifierSymbols,
private val unlinkedMarkerTypeHandler: UnlinkedMarkerTypeHandler,
private val messageLogger: IrMessageLogger
) {
fun addLinkageErrorIntoUnlinkedClasses() {
usedClassifierSymbols.forEachClassSymbolToPatch { unlinkedSymbol ->
val clazz = unlinkedSymbol.owner
val anonInitializer = clazz.declarations.firstNotNullOfOrNull { it as? IrAnonymousInitializer }
?: builtIns.irFactory.createAnonymousInitializer(
clazz.startOffset,
clazz.endOffset,
IrDeclarationOrigin.DEFINED,
IrAnonymousInitializerSymbolImpl()
).also {
it.body = builtIns.irFactory.createBlockBody(clazz.startOffset, clazz.endOffset)
it.parent = clazz
clazz.declarations.add(it)
}
anonInitializer.body.statements.clear()
anonInitializer.body.statements += clazz.throwLinkageError() // TODO: which exactly classifiers are unlinked?
clazz.superTypes = clazz.superTypes.filter { !it.isUnlinked() }
}
}
fun signatureTransformer(): IrElementTransformerVoid = SignatureTransformer()
private inner class SignatureTransformer : IrElementTransformerVoid() {
override fun visitFunction(declaration: IrFunction): IrStatement {
val removedUnlinkedTypes = declaration.fixUnlinkedTypes()
val isImplementedFakeOverride = declaration.origin == MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION
return declaration.transformBodyIfNecessary(isImplementedFakeOverride, removedUnlinkedTypes)
}
override fun visitField(declaration: IrField): IrStatement {
if (declaration.type.isUnlinked()) {
// TODO: it would be more precise to use the set of unlinked symbols than a collection of unlinked types here.
declaration.logLinkageError(listOfNotNull((declaration.type as? IrSimpleType)?.classifier))
declaration.type = unlinkedMarkerTypeHandler.unlinkedMarkerType
declaration.initializer = null
} else {
declaration.transformChildrenVoid()
}
return declaration
}
override fun visitVariable(declaration: IrVariable): IrStatement {
if (declaration.type.isUnlinked()) {
// TODO: it would be more precise to use the set of unlinked symbols than a collection of unlinked types here.
declaration.logLinkageError(listOfNotNull((declaration.type as? IrSimpleType)?.classifier))
declaration.type = unlinkedMarkerTypeHandler.unlinkedMarkerType
declaration.initializer = null
} else {
declaration.transformChildrenVoid()
}
return declaration
}
/**
* Returns the set of all unlinked types encountered during transformation of the given [IrFunction].
* Or empty set if there were no unlinked types.
*/
private fun IrFunction.fixUnlinkedTypes(): Set<IrType> = buildSet {
fun IrValueParameter.fixType() {
if (type.isUnlinked()) {
this@buildSet += type
type = unlinkedMarkerTypeHandler.unlinkedMarkerType
defaultValue = null
}
varargElementType?.let {
if (it.isUnlinked()) {
this@buildSet += it
varargElementType = unlinkedMarkerTypeHandler.unlinkedMarkerType
}
}
}
dispatchReceiverParameter?.fixType()
extensionReceiverParameter?.fixType()
valueParameters.forEach { it.fixType() }
if (returnType.isUnlinked()) {
this += returnType
returnType = unlinkedMarkerTypeHandler.unlinkedMarkerType
}
typeParameters.forEach {
val unlinkedSuperType = it.superTypes.firstOrNull { s -> s.isUnlinked() }
if (unlinkedSuperType != null) {
this += unlinkedSuperType
it.superTypes = listOf(unlinkedMarkerTypeHandler.unlinkedMarkerType)
}
}
}
private fun IrFunction.transformBodyIfNecessary(
isImplementedFakeOverride: Boolean,
removedUnlinkedTypes: Set<IrType>
): IrFunction {
if (!isImplementedFakeOverride && removedUnlinkedTypes.isEmpty()) {
transformChildrenVoid()
} else {
val errorMessages = listOfNotNull(
if (isImplementedFakeOverride)
buildString {
append("Abstract ").appendDeclaration(this@transformBodyIfNecessary)
append(" is not implemented in non-abstract ").appendDeclaration(parentAsClass)
}
else null,
if (removedUnlinkedTypes.isNotEmpty()) {
// TODO: it would be more precise to use the set of unlinked symbols than a collection of unlinked types here.
composeUnlinkedSymbolsErrorMessage(removedUnlinkedTypes.mapNotNull { (it as? IrSimpleType)?.classifier })
} else null
)
body?.let { body ->
val bb = body as IrBlockBody
bb.statements.clear()
bb.statements += throwLinkageError(errorMessages, location())
}
}
return this
}
}
private fun IrSymbol.isUnlinked(): Boolean {
if (!isBound) return true
when (this) {
is IrClassifierSymbol -> isUnlinked()
is IrPropertySymbol -> {
owner.getter?.let { if (it.symbol.isUnlinked()) return true }
owner.setter?.let { if (it.symbol.isUnlinked()) return true }
owner.backingField?.let { return it.symbol.isUnlinked() }
}
is IrFunctionSymbol -> return isUnlinked()
}
return false
}
private fun IrClassifierSymbol.isUnlinked(): Boolean = !isBound || usedClassifierSymbols[this].isUnlinked
private fun IrType.isUnlinked(): Boolean {
val simpleType = this as? IrSimpleType ?: return false
if (simpleType.classifier.isUnlinked()) return true
return simpleType.arguments.any { it is IrTypeProjection && it.type.isUnlinked() }
}
private fun IrFieldSymbol.isUnlinked(): Boolean {
return owner.type.isUnlinkedMarkerType()
}
private fun IrFunctionSymbol.isUnlinked(): Boolean {
val function = owner
if (function.returnType.isUnlinkedMarkerType()) return true
if (function.dispatchReceiverParameter?.type?.isUnlinkedMarkerType() == true) return true
if (function.extensionReceiverParameter?.type?.isUnlinkedMarkerType() == true) return true
if (function.valueParameters.any { it.type.isUnlinkedMarkerType() }) return true
if (function.typeParameters.any { tp -> tp.superTypes.any { st -> st.isUnlinkedMarkerType() } }) return true
return false
}
// That's not the same as IrType.isUnlinked()!
private fun IrType.isUnlinkedMarkerType(): Boolean {
return with(unlinkedMarkerTypeHandler) { isUnlinkedMarkerType() }
}
private fun IrElement.composeUnlinkedSymbolsErrorMessage(unlinkedSymbols: Collection<IrSymbol>) =
renderError(this@composeUnlinkedSymbolsErrorMessage, unlinkedSymbols)
private fun IrDeclaration.throwLinkageError(unlinkedSymbols: Collection<IrSymbol> = emptyList()): IrCall =
throwLinkageError(
messages = listOf(composeUnlinkedSymbolsErrorMessage(unlinkedSymbols)),
location = location()
)
private fun IrElement.throwLinkageError(messages: List<String>, location: Location?): IrCall {
check(messages.isNotEmpty())
messages.forEach { logLinkageError(it, location) }
val irCall = IrCallImpl(startOffset, endOffset, builtIns.nothingType, builtIns.linkageErrorSymbol, 0, 1, ERROR_ORIGIN)
irCall.putValueArgument(0, IrConstImpl.string(startOffset, endOffset, builtIns.stringType, messages.joinToString("\n")))
return irCall
}
private fun IrDeclaration.logLinkageError(unlinkedSymbols: Collection<IrSymbol>) {
logLinkageError(
composeUnlinkedSymbolsErrorMessage(unlinkedSymbols),
location()
)
}
private fun logLinkageError(message: String, location: Location?) {
messageLogger.report(Severity.WARNING, message, location) // It's OK. We log it as a warning.
}
fun usageTransformer(): IrElementTransformerVoid = UsageTransformer()
private inner class UsageTransformer : IrElementTransformerVoid() {
private var currentFile: IrFile? = null
override fun visitFile(declaration: IrFile): IrFile {
currentFile = declaration
return super.visitFile(declaration).also { currentFile = null }
}
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
expression.transformChildrenVoid()
val classifierSymbol = expression.typeOperand.classifierOrFail
return if (classifierSymbol.isUnlinked())
IrCompositeImpl(expression.startOffset, expression.endOffset, builtIns.nothingType).apply {
statements += expression.argument
statements += expression.throwLinkageError(classifierSymbol)
}
else
expression
}
override fun visitExpression(expression: IrExpression): IrExpression {
return if (expression.type.isUnlinked() || expression.type.isUnlinkedMarkerType())
expression.throwLinkageError() // TODO: which exactly classifiers are unlinked?
else
super.visitExpression(expression)
}
override fun visitMemberAccess(expression: IrMemberAccessExpression<*>): IrExpression {
expression.transformChildrenVoid()
return if (!expression.symbol.isUnlinked() && !expression.type.isUnlinked())
expression
else
IrCompositeImpl(expression.startOffset, expression.endOffset, builtIns.nothingType, expression.origin).apply {
statements.addIfNotNull(expression.dispatchReceiver)
statements.addIfNotNull(expression.extensionReceiver)
for (i in 0 until expression.valueArgumentsCount) {
statements.addIfNotNull(expression.getValueArgument(i))
}
statements += expression.throwLinkageError() // TODO: which exactly classifiers are unlinked?
}
}
override fun visitFieldAccess(expression: IrFieldAccessExpression): IrExpression {
expression.transformChildrenVoid()
return if (!expression.symbol.isUnlinked())
expression
else
IrCompositeImpl(expression.startOffset, expression.endOffset, builtIns.nothingType, expression.origin).apply {
statements.addIfNotNull(expression.receiver)
if (expression is IrSetField)
statements += expression.value
statements += expression.throwLinkageError(expression.symbol)
}
}
override fun visitClassReference(expression: IrClassReference): IrExpression {
return if (expression.symbol.isUnlinked())
expression.throwLinkageError(expression.symbol)
else
expression
}
override fun visitClass(declaration: IrClass): IrStatement {
declaration.transformChildrenVoid()
fun <S : IrSymbol> IrOverridableDeclaration<S>.filterOverriddenSymbols() {
overriddenSymbols = overriddenSymbols.filter { symbol ->
symbol.isBound
// Handle the case when the overridden declaration became private.
&& (symbol.owner as? IrDeclarationWithVisibility)?.visibility != DescriptorVisibilities.PRIVATE
}
}
for (member in declaration.declarations) {
when (member) {
is IrSimpleFunction -> member.filterOverriddenSymbols()
is IrProperty -> {
member.filterOverriddenSymbols()
member.getter?.filterOverriddenSymbols()
member.setter?.filterOverriddenSymbols()
}
}
}
return declaration
}
private fun IrExpression.throwLinkageError(unlinkedSymbol: IrSymbol? = null): IrCall =
throwLinkageError(
messages = listOf(composeUnlinkedSymbolsErrorMessage(listOfNotNull(unlinkedSymbol))),
location = locationIn(currentFile)
)
}
companion object {
private val ERROR_ORIGIN = object : IrStatementOriginImpl("LINKAGE ERROR") {}
val MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION =
object : IrDeclarationOriginImpl("MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION", isSynthetic = true) {}
}
}
private fun IrDeclaration.location(): Location? = locationIn(fileOrNull)
private fun IrElement.locationIn(currentFile: IrFile?): Location? {
if (currentFile == null) return null
val moduleName: String = currentFile.module.name.asString()
val filePath: String = currentFile.fileEntry.name
val lineNumber: Int
val columnNumber: Int
when (val effectiveStartOffset = startOffsetOfFirstNonSyntheticIrElement()) {
UNDEFINED_OFFSET -> {
lineNumber = UNDEFINED_LINE_NUMBER
columnNumber = UNDEFINED_COLUMN_NUMBER
}
else -> {
lineNumber = currentFile.fileEntry.getLineNumber(effectiveStartOffset) + 1 // since humans count from 1, not 0
columnNumber = currentFile.fileEntry.getColumnNumber(effectiveStartOffset) + 1
}
}
// TODO: should module name still be added here?
return Location("$moduleName @ $filePath", lineNumber, columnNumber)
}
private tailrec fun IrElement.startOffsetOfFirstNonSyntheticIrElement(): Int = when (this) {
is IrPackageFragment -> UNDEFINED_OFFSET
!is IrDeclaration -> {
// We don't generate synthetic IR expressions in the course of partial linkage.
startOffset
}
else -> when (origin) {
MISSING_ABSTRACT_CALLABLE_MEMBER_IMPLEMENTATION -> {
// There is no sense to take coordinates from the declaration that does not exist in the code.
// Let's take the coordinates of the parent.
parent.startOffsetOfFirstNonSyntheticIrElement()
}
else -> startOffset
}
}
@@ -1,221 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization.unlinked
import org.jetbrains.kotlin.backend.common.serialization.unlinked.DeclarationKind.*
import org.jetbrains.kotlin.backend.common.serialization.unlinked.ExpressionKind.*
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.IdSignature.*
import org.jetbrains.kotlin.ir.util.getNameWithAssert
import org.jetbrains.kotlin.ir.util.isAnonymousObject
// TODO: Consider getting rid of this class when new self-descriptive signatures are implemented.
internal object UnlinkedIrElementRenderer {
fun StringBuilder.appendDeclaration(declaration: IrDeclaration): StringBuilder = appendDeclaration(
declarationKind = declaration.declarationKind,
declarationName = declaration.symbol.guessName()
)
private fun StringBuilder.appendDeclaration(declarationKind: DeclarationKind, declarationName: String?): StringBuilder {
append(declarationKind)
if (declarationKind != ANONYMOUS_OBJECT) {
// This is a declaration NOT under a property.
appendWithWhitespaceBefore(declarationName ?: UNKNOWN_NAME)
}
return this
}
fun renderError(element: IrElement, unlinkedSymbols: Collection<IrSymbol>): String = buildString {
when (element) {
is IrDeclaration -> appendDeclaration(element)
is IrExpression -> {
val (expressionKind, referencedDeclarationKind) = element.expression
appendWithWhitespaceAfter(expressionKind.displayName)
if (referencedDeclarationKind != null) {
val referencedDeclarationSymbol = when (element) {
is IrDeclarationReference -> element.symbol
is IrInstanceInitializerCall -> element.classSymbol
else -> null
}
val referencedDeclaration = referencedDeclarationSymbol?.boundOwnerDeclarationOrNull
if (referencedDeclaration == null) {
// If it's impossible to obtain referenced declaration because the symbol of this declaration is unbound,
// but the declaration itself is supposed to be, then do best effort and try to output the short name of
// the declaration at least.
appendDeclaration(referencedDeclarationKind, referencedDeclarationSymbol?.guessName())
} else {
appendDeclaration(referencedDeclaration)
}
}
append(" can not be ").append(expressionKind.verb3rdForm).append(" because it")
}
else -> error("Unexpected type of IR element: ${this::class.java}, $this")
}
append(" uses unlinked symbols")
if (unlinkedSymbols.isNotEmpty()) {
unlinkedSymbols.joinTo(this, prefix = ": ") { it.anySignature?.render() ?: UNKNOWN_SYMBOL }
}
}
}
private enum class DeclarationKind(private val displayName: String) {
CLASS("class"),
INTERFACE("interface"),
ENUM_CLASS("enum class"),
ENUM_ENTRY("enum entry"),
ANNOTATION_CLASS("annotation class"),
OBJECT("object"),
ANONYMOUS_OBJECT("anonymous object"),
COMPANION_OBJECT("companion object"),
MUTABLE_VARIABLE("var"),
IMMUTABLE_VARIABLE("val"),
VALUE_PARAMETER("value parameter"),
FIELD("field"),
FIELD_OF_PROPERTY("backing field of property"),
PROPERTY("property"),
PROPERTY_ACCESSOR("property accessor"),
FUNCTION("function"),
CONSTRUCTOR("constructor"),
OTHER_DECLARATION("declaration");
override fun toString() = displayName
}
private val IrDeclaration.declarationKind: DeclarationKind
get() = when (this) {
is IrClass -> when (kind) {
ClassKind.CLASS -> if (isAnonymousObject) ANONYMOUS_OBJECT else CLASS
ClassKind.INTERFACE -> INTERFACE
ClassKind.ENUM_CLASS -> ENUM_CLASS
ClassKind.ENUM_ENTRY -> ENUM_ENTRY
ClassKind.ANNOTATION_CLASS -> ANNOTATION_CLASS
ClassKind.OBJECT -> if (isCompanion) COMPANION_OBJECT else OBJECT
}
is IrVariable -> if (isVar) MUTABLE_VARIABLE else IMMUTABLE_VARIABLE
is IrValueParameter -> VALUE_PARAMETER
is IrField -> if (correspondingPropertySymbol != null) FIELD_OF_PROPERTY else FIELD
is IrProperty -> PROPERTY
is IrSimpleFunction -> if (correspondingPropertySymbol != null) PROPERTY_ACCESSOR else FUNCTION
is IrConstructor -> CONSTRUCTOR
else -> OTHER_DECLARATION
}
private val IrFunctionSymbol.functionDeclarationKind: DeclarationKind
get() = when (this) {
is IrConstructorSymbol -> CONSTRUCTOR
is IrSimpleFunctionSymbol -> boundOwnerDeclarationOrNull?.declarationKind
?: if (anySignature is AccessorSignature) PROPERTY_ACCESSOR else FUNCTION
else -> OTHER_DECLARATION // Something unexpected.
}
private val IrFieldSymbol.fieldDeclarationKind: DeclarationKind
get() = boundOwnerDeclarationOrNull?.declarationKind
?: FIELD // Fallback to simple field as it's impossible to make a guess by signature.
private val IrValueSymbol.valueDeclarationKind: DeclarationKind
get() = when (this) {
is IrValueParameterSymbol -> VALUE_PARAMETER
is IrVariableSymbol -> boundOwnerDeclarationOrNull?.declarationKind
?: IMMUTABLE_VARIABLE // Fallback to immutable variable as it's impossible to make a guess by signature.
else -> OTHER_DECLARATION // Something unexpected.
}
private val IrSymbol.classDeclarationKind: DeclarationKind
get() = when (this) {
is IrClassSymbol -> boundOwnerDeclarationOrNull?.declarationKind
?: CLASS // Fallback to class as it's impossible to make a guess by signature.
is IrEnumEntrySymbol -> ENUM_ENTRY
else -> OTHER_DECLARATION // Something unexpected.
}
private data class Expression(val kind: ExpressionKind, val referencedDeclarationKind: DeclarationKind?)
private enum class ExpressionKind(val displayName: String?, val verb3rdForm: String) {
REFERENCE("reference to", "evaluated"),
CALLING(null, "called"),
CALLING_INSTANCE_INITIALIZER("instance initializer of", "called"),
READING(null, "read"),
WRITING(null, "written"),
GETTING_INSTANCE(null, "gotten"),
OTHER_EXPRESSION("expression", "evaluated")
}
// More can be added for verbosity in the future.
private val IrExpression.expression: Expression
get() = when (this) {
is IrDeclarationReference -> when (this) {
is IrFunctionReference -> Expression(REFERENCE, symbol.functionDeclarationKind)
is IrPropertyReference,
is IrLocalDelegatedPropertyReference -> Expression(REFERENCE, PROPERTY)
is IrCall -> Expression(CALLING, symbol.functionDeclarationKind)
is IrConstructorCall,
is IrEnumConstructorCall,
is IrDelegatingConstructorCall -> Expression(CALLING, CONSTRUCTOR)
is IrClassReference -> Expression(REFERENCE, symbol.classDeclarationKind)
is IrGetField -> Expression(READING, symbol.fieldDeclarationKind)
is IrSetField -> Expression(WRITING, symbol.fieldDeclarationKind)
is IrGetValue -> Expression(READING, symbol.valueDeclarationKind)
is IrSetValue -> Expression(WRITING, symbol.valueDeclarationKind)
is IrGetSingletonValue -> Expression(GETTING_INSTANCE, symbol.classDeclarationKind)
else -> Expression(REFERENCE, OTHER_DECLARATION)
}
is IrInstanceInitializerCall -> Expression(CALLING_INSTANCE_INITIALIZER, classSymbol.classDeclarationKind)
else -> Expression(OTHER_EXPRESSION, null)
}
private val IrSymbol.boundOwnerDeclarationOrNull: IrDeclaration?
get() = if (isBound) owner as? IrDeclaration else null
private fun IrSymbol.guessName(): String? {
fun IdSignature.guessNameBySignature(nameSegmentsToPickUp: Int): String? = when (this) {
is CommonSignature -> nameSegments.takeLast(nameSegmentsToPickUp).joinToString(".")
is CompositeSignature -> inner.guessNameBySignature(nameSegmentsToPickUp)
is AccessorSignature -> accessorSignature.guessNameBySignature(nameSegmentsToPickUp)
else -> null
}
return anySignature
?.let { effectiveSignature ->
val nameSegmentsToPickUp = when {
effectiveSignature is AccessorSignature -> 2 // property_name.accessor_name
this is IrConstructorSymbol -> 2 // class_name.<init>
else -> 1
}
effectiveSignature.guessNameBySignature(nameSegmentsToPickUp)
}
?: boundOwnerDeclarationOrNull?.getNameWithAssert()?.asString()
}
private val IrSymbol.anySignature: IdSignature?
get() = signature ?: privateSignature
private const val UNKNOWN_SYMBOL = "<unknown symbol>"
private const val UNKNOWN_NAME = "<unknown name>"
private fun StringBuilder.appendWithWhitespaceBefore(text: String): StringBuilder {
if (text.isNotEmpty()) appendWhitespaceIfNotEmpty().append(text)
return this
}
private fun StringBuilder.appendWithWhitespaceAfter(text: String?): StringBuilder {
if (!text.isNullOrEmpty()) append(text).append(" ")
return this
}
private fun StringBuilder.appendWhitespaceIfNotEmpty(): StringBuilder {
if (isNotEmpty()) append(" ")
return this
}
@@ -1,47 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization.unlinked
import org.jetbrains.kotlin.backend.common.serialization.unlinked.UsedClassifierSymbolStatus.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
internal enum class UsedClassifierSymbolStatus(val isUnlinked: Boolean) {
/** IR symbol of unlinked classifier. */
UNLINKED(true),
/** IR symbol of linked classifier. */
LINKED(false);
companion object {
val UsedClassifierSymbolStatus?.isUnlinked: Boolean get() = this?.isUnlinked == true
}
}
internal class UsedClassifierSymbols {
private val symbols = HashMap<IrClassifierSymbol, Boolean>()
private val patchedSymbols = HashSet<IrClassSymbol>() // To avoid re-patching what already has been patched.
fun forEachClassSymbolToPatch(patchAction: (IrClassSymbol) -> Unit) {
symbols.forEach { (symbol, isUnlinked) ->
if (isUnlinked && symbol.isBound && symbol is IrClassSymbol && patchedSymbols.add(symbol)) {
patchAction(symbol)
}
}
}
operator fun get(symbol: IrClassifierSymbol): UsedClassifierSymbolStatus? =
when (symbols[symbol]) {
true -> UNLINKED
false -> LINKED
null -> null
}
fun register(symbol: IrClassifierSymbol, status: UsedClassifierSymbolStatus): Boolean {
symbols[symbol] = status.isUnlinked
return status.isUnlinked
}
}